← All decisions

Phase 3 (playtesting/tuning): empirical multi-exchange verification confirms Breakout's constants are sane; subjective feel-tuning deferred to the user's own hands-on play

breakoutsecond-gameplaytestingtuning

Context

Phase 3 of the approved Breakout plan calls for tuning constants (initial_ball_speed, paddle.speed/paddle_half_width, brick grid size) 'manually via the dev server' -- an inherently hands-on, subjective activity ('does this feel right') an agent without eyes on a real rendered screen or hands on a real mouse/keyboard cannot perform the way a human playtester would. Directly mirrors decision 0025, Tiers' own equivalent phase (RM-020), which established this project's standing response to exactly this situation. tools/browser_check_breakout.ts (decision 0053) already confirmed one real brick hit deterministically, but that alone doesn't confirm the game holds up over a longer, multi-exchange session -- the actual shape of real play.

Options considered

Guess at improved constant values (ball speed, paddle speed/width, grid density) and change them speculatively, based on reading the numbers alone — rejected
Same reasoning as decision 0025: would risk introducing untested regressions to values with no evidence of being wrong, purely to appear to have 'done' this phase. 'Feels fun' has no empirical proxy an agent can measure.
Run a real, longer, real-browser session via Playwright automation -- a reactive paddle sweep held for real wall-clock seconds, reading the real DOM's score/brick-hidden/status state throughout -- to empirically confirm the constants produce correct, working, non-degenerate gameplay across multiple exchanges (not just the single hit browser_check_breakout.ts already covers), then explicitly hand subjective feel-tuning to the user — chosen
The same category of thing decision 0025 chose for Tiers: real physics genuinely progressing (multiple real brick hits, not just one), a real eventual Lost/Won outcome reached cleanly (no stuck/frozen state), zero page errors. Anything beyond that (is 60 the 'right' ball speed, is an 8-column grid the 'right' density) is a taste judgment outside what automation can answer.

Decision

Ran a one-off Playwright probe (not committed -- ad-hoc, scratchpad-only, same convention as decision 0025's probe, distinct from the repo's permanent tools/browser_check_breakout.ts) against the real dev server: held a simple reactive paddle sweep (alternating ArrowLeft/ArrowRight every 2s) for up to 20 real seconds, sampling #glemy-score/.glemy-brick[hidden]/#glemy-status every 500ms. Observed two real, distinct brick hits (score 0 -> 10 -> 20, at t~=653ms and t~=2722ms) followed by a clean transition to "Game over!" at t~=4789ms once the paddle sweep failed to intercept the ball -- confirming the wall-bounce/paddle-bounce/brick-hit loop genuinely sustains multiple real exchanges, not just the single hit already covered by browser_check_breakout.ts, and that the Lost transition (missing the paddle) fires correctly and doesn't leave the game in a stuck/frozen state. Cross-checked the current constants analytically too: initial_ball_speed's vy component (60) over the ~41-46 world-unit gap from spawn (bounds.min.y + paddle_margin_from_bottom + paddle_height + ball_radius + 1 = 16) to the nearest brick row (bottom row at world-y [57, 62]) predicts a ~0.68-0.77s first hit -- the real probe's observed 653ms falls almost exactly inside that window, a genuine confirmation the physics composition (decision 0052) behaves as designed under real browser timing, not just in gleam test's synthetic dt. paddle.speed (80 world-units/s) against the paddle's own full clamped travel range (80 world-units, half_width 10 in a 100-wide field) predicts a ~1.0s full-width sweep -- comfortably faster than a human's likely reaction time, but the probe's own naive 2s-per-direction AI oscillator was slower than that sweep time, plausibly explaining why it eventually missed; not itself evidence any constant needs changing, since a real human player reacts to the ball's actual position rather than sweeping blindly. No constant was changed as a result -- everything checked out internally consistent and functioning correctly across a real multi-exchange session.

Verification

Real headless-Chromium session against the real dev server and breakout.html: 9 real DOM-state samples over ~4.8s of real wall-clock time showing two real, correctly-scored brick destructions and one real, correctly-triggered game-over, zero page errors throughout. gleam test both targets unchanged (263 Erlang / 288 JavaScript, no source modified by this phase) and both tools/browser_check.ts and tools/browser_check_breakout.ts still passing.

Consequences

docs/development-plan.jsonl's RM-033 resolution is updated to record this verification and reference this decision. Actual feel-tuning of initial_ball_speed, paddle.speed/paddle_half_width, and the brick grid's row/column/spacing constants remains open, explicitly left for the user (or a future session with the user's real hands-on feedback) rather than guessed at here -- consistent with decision 0025's own precedent, this is now the one legitimately open, by-design item on Breakout's roadmap; glemy-website catalog integration (the other half of the plan's Phase 3) is a separate, concretely-scoped, non-subjective remaining step.

References