Skip to content

games: draw the ball on half-rows, so it moves like a ball - #376

Merged
ralyodio merged 1 commit into
mainfrom
worktree-games-ball-physics
Aug 12, 2026
Merged

games: draw the ball on half-rows, so it moves like a ball#376
ralyodio merged 1 commit into
mainfrom
worktree-games-ball-physics

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Ball physics still read as wrong after #373. That fix was right about the clock — I re-measured and the column steps are now regular, four ticks each — but it left the grid alone, and the grid was the other half of the problem.

What was wrong

A terminal cell is about twice as tall as it is wide, and the ball was drawn one per cell. So the board it moved on had half the resolution going down that it had going across, and two things fell out of that:

  • A step sideways moved the ball one pixel; a step down moved it two. The same ball appeared to travel twice as fast down the table as across it, and a trajectory the maths had at 45° was drawn at 60°.
  • The two steps kept their own schedules. Crossing into the next column and crossing into the next row almost never land on the same tick, so a ball going diagonally didn't move diagonally — it hopped sideways, then a tick or two later hopped down. Measured on pong: the ball holds a cell for 4 ticks, 4 ticks, then 2 and 2 as a row change splits one of those spans. Three or four times a second. That is what was left of the jiggle.

The fix

and each fill half a cell, which is close to square, so the ball is drawn on a grid with the same pitch both ways. It steps the same distance whichever way it goes, and a row is two steps rather than one.

biggest single jump drawn position held jitter
pong before 2.24 cells
pong after 1.41 cells
breakout before 1–10 ticks ±45ms
breakout after 1–5 ticks ±22ms

Breakout is the steeper game and felt worse; it improves most.

It's a better ball, too — a half block is a square pixel moving on a square grid, where was a round dot snapping between positions two of its own heights apart. In breakout the resting ball now sits flush on the paddle.

Nothing about the simulation changed. No speed, no angle, no bounce, no tuned constant. This is only where the ball is drawn, which is where it was wrong.

Testing

Three new tests in the shared section, and the middle one is a real regression guard — under the rendering that shipped in v0.45.0 it fails with a 2.24-cell jump:

v0.45.0 whole cells      biggest single visible jump: 2.236 -> test FAILS
this branch, half-rows   biggest single visible jump: 1.414 -> test PASSES

Full suite: 1852 tests, 0 failures. Frames verified end-to-end through runGame's incremental repaint — box edges align, no corruption. / are already used by the breakout paddle, so no new width risk.

Not in this PR

🤖 Generated with Claude Code

The clock was fixed last time; the grid was not. A terminal cell is about twice
as tall as it is wide, and the ball was drawn one per cell, so the board it
moved on had half the resolution going down that it had going across. Two
things fall out of that, and both of them read as bad physics rather than as a
coarse grid.

A step sideways moved the ball one pixel and a step down moved it two. The same
ball appeared to travel twice as fast down the table as across it, and a
trajectory the maths had at forty-five degrees was drawn at sixty.

Worse, the two steps kept their own schedules. Crossing into the next column and
crossing into the next row almost never landed on the same tick, so a ball going
diagonally did not move diagonally — it hopped sideways, then a tick or two
later hopped down. On pong the ball held a cell for four ticks, four ticks, then
two and two as a row change split one of those spans, three or four times a
second. That is what was left of the jiggle.

Half blocks fix both. `▀` and `▄` each fill half a cell, which is close to
square, so the ball is drawn on a grid with the same pitch both ways: it steps
the same distance whichever way it goes, and a row is two steps rather than one.
The biggest single jump a rally can make drops from 2.24 cells to 1.41 — one
column and one half-row, never one column and a whole row. Breakout, which is
steeper and felt worse, improves most: the time the ball holds one drawn
position goes from 1–10 ticks to 1–5, and the jitter on it from ±45ms to ±22ms.

It is a better ball, too. A half block is a square pixel moving on a square
grid, where `●` was a round dot snapping between positions two of its own
heights apart. In breakout the resting ball now sits flush on the paddle.

Nothing about the simulation changed — no speed, no angle, no bounce. This is
only where the ball is drawn, which is where it was wrong.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

97 finding(s)

HIGH/CRITICAL: 5 | MEDIUM: 41 | LOW: 51

Severity Rule Location
HIGH js-ssrf-outbound-request apps/pwa/public/sw.js:45
HIGH tls-verification-disabled apps/pwa/src/lib/moshpit-gateway.mjs:299
HIGH sh-remote-script-execution install.sh:79
HIGH sh-remote-script-execution install.sh:83
HIGH tls-verification-disabled src/dns.mjs:741
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:61
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:75
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:101
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:265
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:269
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:314
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:499
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:675
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:677
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:736
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:782
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:852
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:955
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1063
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1199
MEDIUM js-unescaped-html-sink apps/pwa/src/routes/moshpit.mjs:1419
MEDIUM js-timing-unsafe-mac-compare apps/pwa/src/routes/settings-sync.mjs:179
MEDIUM js-dynamic-code-execution apps/pwa/test/apikey-mask.test.mjs:129
MEDIUM sql-template-interpolation apps/pwa/test/credits-webhook-event-match.test.mjs:111
MEDIUM sql-template-interpolation apps/pwa/test/credits-webhook-event-match.test.mjs:131
MEDIUM sql-template-interpolation apps/pwa/test/moshpit-terms.test.mjs:192
MEDIUM sql-string-concatenation src/cli-schema.mjs:128
MEDIUM sql-string-concatenation src/cli-schema.mjs:417
MEDIUM sql-string-concatenation src/cli-schema.mjs:543
MEDIUM js-timing-unsafe-mac-compare src/settings-sync.mjs:309
MEDIUM insecure-temp-file test/dns-disable-restore.test.mjs:93
MEDIUM insecure-temp-file test/dns-disable-restore.test.mjs:310
MEDIUM insecure-temp-file test/news.test.mjs:479
MEDIUM insecure-temp-file test/plugins.test.mjs:152
MEDIUM insecure-temp-file test/pty.test.mjs:28
MEDIUM insecure-temp-file test/pty.test.mjs:31
MEDIUM insecure-temp-file test/pty.test.mjs:40
MEDIUM insecure-temp-file test/pty.test.mjs:42
MEDIUM insecure-temp-file test/pty.test.mjs:47
MEDIUM insecure-temp-file test/pty.test.mjs:48
MEDIUM insecure-temp-file test/pty.test.mjs:49
MEDIUM insecure-temp-file test/tabs.test.mjs:8
MEDIUM insecure-temp-file test/tabs.test.mjs:13
MEDIUM insecure-temp-file test/tabs.test.mjs:14
MEDIUM insecure-temp-file test/tabs.test.mjs:22
MEDIUM insecure-temp-file test/trust.test.mjs:240
LOW secret-generic-credential apps/pwa/test/apikey-bearer-scheme.test.mjs:30
LOW secret-generic-credential apps/pwa/test/apikey-mask.test.mjs:38
LOW secret-generic-credential apps/pwa/test/apikey-reveal.test.mjs:35
LOW secret-generic-credential apps/pwa/test/approvals-context.test.mjs:28

…and 47 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit a3d1c3e into main Aug 12, 2026
4 checks passed
@ralyodio
ralyodio deleted the worktree-games-ball-physics branch August 12, 2026 04:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant