Portrait 360x480 table with regular-sized flippers

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
This commit is contained in:
georgios
2026-09-09 19:02:11 +02:00
parent c08e22c873
commit cf2c0e4558
18 changed files with 134 additions and 128 deletions
+31 -29
View File
@@ -1,5 +1,5 @@
"""Pixel-probe the rendered frames for layout sanity (since art can't be
viewed directly in this session)."""
viewed directly in this session). Portrait 360x480."""
import os
@@ -35,43 +35,45 @@ def main():
st["lit"] = True
g.table.ball_live = True
g.pending = "live"
g.table.ball.x, g.table.ball.y = 240, 300
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 (14..100, 13..26)
checks.append(("HUD score digits", count_px(s, (14, 13, 110, 15),
lambda p: near(p, (255, 255, 255), 40)) > 25))
# HUD gold BEST digits near (170..300, 13..26)
checks.append(("HUD gold best", count_px(s, (170, 13, 150, 15),
# 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))
# hearts red at (336..390, 14..30)
checks.append(("hearts", count_px(s, (336, 12, 60, 22),
lambda p: near(p, C["heart"], 50)) > 20))
# three gold stars in HUD at (52..112, 30..44)
checks.append(("HUD stars lit", count_px(s, (44, 28, 90, 18),
lambda p: near(p, C["gold"], 55)) > 15))
# playfield interior present (not black)
checks.append(("field fill", near(s.get_at((240, 100)), C["field"], 70)))
# pink rail present on left edge inside play area
rail_ok = count_px(s, (14, 70, 12, 20), lambda p: near(p, C["rail"], 60)) > 10
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 pink at (150,180)
checks.append(("bumper pink", near(s.get_at((150, 178)), C["pink"], 55)))
# bumper lemon at (240,240)
checks.append(("bumper lemon", near(s.get_at((240, 238)), C["lemon"], 55)))
# pit dark center at (240, 352)
checks.append(("pit center", near(s.get_at((240, 353)), C["pit"], 30)))
# flipper blue present around (240, 320)
checks.append(("flipper blue", count_px(s, (100, 300, 280, 45),
lambda p: near(p, C["flipper"], 55)) > 30))
# cannon purple body at (440,152)
checks.append(("cannon", near(s.get_at((440, 152)), (150, 100, 220), 70)))
# ball near (240,300) is white-ish
checks.append(("ball", count_px(s, (230, 290, 20, 20),
# 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: