Rotate the playfield to portrait like a real pinball machine: - canvas 480x360 landscape -> 360x480 portrait; playfield 308px wide, ~390px tall, with HUD, title/game-over screens and HUD re-laid out for the narrow format (score/best top row, balls/stars/fever below) - flippers reduced to a regular ~quarter-width size (82px) pivoting at the lower corners, tips meeting over a wide centre drain - table elements re-placed for the taller field: 3 star lanes up top, candy bumpers mid-field, slingshots and floor-level outlane guard posts at the bottom; data-checked with the autoplay bot (~350 bumper hits / 30k frames, centre+side drains, no stuck balls) - probes & autoplay policy updated for the portrait geometry; all 13 pixel-probes pass
89 lines
3.5 KiB
Python
89 lines
3.5 KiB
Python
"""Pixel-probe the rendered frames for layout sanity (since art can't be
|
|
viewed directly in this session). Portrait 360x480."""
|
|
|
|
import os
|
|
|
|
os.environ.setdefault("SDL_VIDEODRIVER", "dummy")
|
|
os.environ.setdefault("SDL_AUDIODRIVER", "dummy")
|
|
|
|
import pygame # noqa: E402
|
|
from game import Game, C # noqa: E402
|
|
from physics import CV_W, CV_H # noqa: E402
|
|
|
|
|
|
def count_px(surf, rect, pred):
|
|
n = 0
|
|
for y in range(rect[1], rect[1] + rect[3]):
|
|
for x in range(rect[0], rect[0] + rect[2]):
|
|
px = surf.get_at((x, y))
|
|
if pred(px):
|
|
n += 1
|
|
return n
|
|
|
|
|
|
def near(c, t, tol=60):
|
|
return abs(c[0] - t[0]) <= tol and abs(c[1] - t[1]) <= tol and abs(c[2] - t[2]) <= tol
|
|
|
|
|
|
def main():
|
|
g = Game()
|
|
g.state = "play"
|
|
g.new_game()
|
|
g.score = 123456
|
|
g.lives = 3
|
|
for st in g.table.stars:
|
|
st["lit"] = True
|
|
g.table.ball_live = True
|
|
g.pending = "live"
|
|
g.table.ball.x, g.table.ball.y = 180, 330
|
|
g.table.ball.vx, g.table.ball.vy = 0, 3
|
|
for _ in range(3):
|
|
g.update()
|
|
g.render()
|
|
s = g.canvas
|
|
checks = []
|
|
# HUD white score digits around (10..120, 13..26)
|
|
checks.append(("HUD score digits", count_px(s, (10, 13, 110, 15),
|
|
lambda p: near(p, (255, 255, 255), 40)) > 20))
|
|
# HUD gold BEST digits near right (250..350, 13..26)
|
|
checks.append(("HUD gold best", count_px(s, (230, 13, 120, 15),
|
|
lambda p: near(p, C["gold"], 55)) > 10))
|
|
# hearts red at (8..90, 40..50)
|
|
checks.append(("hearts", count_px(s, (8, 40, 90, 12),
|
|
lambda p: near(p, C["heart"], 50)) > 15))
|
|
# three gold stars in HUD at (118..175, 36..48)
|
|
checks.append(("HUD stars lit", count_px(s, (116, 36, 60, 14),
|
|
lambda p: near(p, C["gold"], 55)) > 10))
|
|
# playfield interior present (not black)
|
|
checks.append(("field fill", near(s.get_at((180, 120)), C["field"], 70)))
|
|
# left rail pink present inside play area
|
|
rail_ok = count_px(s, (23, 100, 8, 20), lambda p: near(p, C["rail"], 60)) > 5
|
|
checks.append(("left rail pink", rail_ok))
|
|
# bumper bodies (sample the rim, away from the face)
|
|
checks.append(("bumper pink", near(s.get_at((82, 205)), C["pink"], 55)))
|
|
checks.append(("bumper mint", count_px(s, (230, 190, 60, 40),
|
|
lambda p: near(p, C["mint"], 55) or
|
|
near(p, C["mint_l"], 55)) > 80))
|
|
checks.append(("bumper lemon", near(s.get_at((160, 300)), C["lemon"], 55)))
|
|
# pit dark center at (180, 455)
|
|
checks.append(("pit center", near(s.get_at((180, 455)), C["pit"], 30)))
|
|
# flippers present on BOTH sides (pivot ~78 and ~282)
|
|
fl = count_px(s, (60, 395, 260, 50), lambda p: near(p, C["flipper"], 55))
|
|
checks.append(("flipper blue both sides", fl > 60))
|
|
# cannon purple body around (300,170)
|
|
checks.append(("cannon", near(s.get_at((300, 170)), (150, 100, 220), 70)))
|
|
# ball near (180,330) is white-ish
|
|
checks.append(("ball", count_px(s, (170, 320, 20, 20),
|
|
lambda p: near(p, (245, 250, 255), 40)) > 5))
|
|
ok = True
|
|
for name, passed in checks:
|
|
print(("%s %s" % ("PASS" if passed else "FAIL", name)))
|
|
ok = ok and passed
|
|
pygame.image.save(s, "probe_play.png")
|
|
pygame.quit()
|
|
return 0 if ok else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|