Public: anyone can read this; the crew can change it.

Migrated from github.com/amirmushichge/pixel-plays

Sack Race rules: a tested `sack-race/course.js` where you hop a sack down a grass lane in time with your own landing, against three seeded rivals, from plain data, in a two-file pull with a test file that runs in under a second #322

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

This adds the Sack Race rules as `sack-race/course.js` with its test file `sack-race/course.test.mjs`, and nothing else. The rules module is plain data in and out, with no DOM, no timers and no `Math.random`.

shipped production is serving it deploy 5b5fb07

opened this

This adds the Sack Race rules as sack-race/course.js with its test file sack-race/course.test.mjs, and nothing else. The rules module is plain data in and out, with no DOM, no timers and no Math.random.

bun test sack-race/course.test.mjs passes: 14 tests, 0 failures, 56 ms for the run. bun x tsc --noEmit printed only its usage banner, so I take it as a no-op, not a type-check result. scripts/gate.ts and scripts/fast.ts don't exist in this clone, so I ran neither, and docs/VISION.md is missing, so I couldn't read it. I didn't run the full suite.

Rules

  • Start: start(seed) gives a 40-unit lane, a timing window of ticks 2–5 after landing, and a 200-tick limit. It also gives three rivals, each with a seeded stride of 3.5–5.5 and a hop every 8–11 ticks. The same seed gives the same rivals and the same race.
  • Hop: a hop stays in the air for 6 ticks, and its distance arrives on landing.

- Stride: a press inside the window is a full stride of 5. - Shuffle: an early press is a shuffle of 2. - Air and toppled: a press in the air or while toppled is refused. - After the end: a press after the race is over is refused with race-over. - Refusals: every refusal returns the same state object and a reason.

  • Wobbles: if the window passes with no press, step counts a wobble and restarts the timing. A full stride clears the run of wobbles, but a shuffle doesn't. Three wobbles in a row topple the player for 8 ticks, then the run resets.
  • End of race: the race ends when all four have crossed or at the tick limit. result holds the player's place out of four, their finishing tick (0 if they never crossed) and the full lane order. Finishers rank by finishing tick, then by lane order with the player as lane 0. Runners who haven't crossed rank behind them, by distance and then lane.

Choices

  • step returns {ok, state, why} like hop, and refuses with race-over once the race has ended.
  • A player who crosses before the rivals waits for them, and their hops are refused with finished.
  • Standing still also topples you, since each missed window counts a wobble.
  • I added two exports beyond the issue: arc(air) for hop height and rank(state).

I did not touch shell.js, index.html, town.js, rounds.mjs, forge.json, scripts/ or the two playtest/serve test files, and I added no page or catalogue entry.

To try it: bun test sack-race/course.test.mjs.

🤖 Generated with Claude Code

Closes #307

commented

This adds the Sack Race rules as sack-race/course.js with its test file sack-race/course.test.mjs, and nothing else. The rules module is plain data in and out, with no DOM, no timers and no Math.random.

bun test sack-race/course.test.mjs passes: 14 tests, 0 failures, 56 ms for the run. bun x tsc --noEmit printed only its usage banner, so I take it as a no-op, not a type-check result. scripts/gate.ts and scripts/fast.ts don't exist in this clone, so I ran neither, and docs/VISION.md is missing, so I couldn't read it. I didn't run the full suite.

Rules

  • Start: start(seed) gives a 40-unit lane, a timing window of ticks 2–5 after landing, and a 200-tick limit. It also gives three rivals, each with a seeded stride of 3.5–5.5 and a hop every 8–11 ticks. The same seed gives the same rivals and the same race.
  • Hop: a hop stays in the air for 6 ticks, and its distance arrives on landing.

- Stride: a press inside the window is a full stride of 5. - Shuffle: an early press is a shuffle of 2. - Air and toppled: a press in the air or while toppled is refused. - After the end: a press after the race is over is refused with race-over. - Refusals: every refusal returns the same state object and a reason.

  • Wobbles: if the window passes with no press, step counts a wobble and restarts the timing. A full stride clears the run of wobbles, but a shuffle doesn't. Three wobbles in a row topple the player for 8 ticks, then the run resets.
  • End of race: the race ends when all four have crossed or at the tick limit. result holds the player's place out of four, their finishing tick (0 if they never crossed) and the full lane order. Finishers rank by finishing tick, then by lane order with the player as lane 0. Runners who haven't crossed rank behind them, by distance and then lane.

Choices

  • step returns {ok, state, why} like hop, and refuses with race-over once the race has ended.
  • A player who crosses before the rivals waits for them, and their hops are refused with finished.
  • Standing still also topples you, since each missed window counts a wobble.
  • I added two exports beyond the issue: arc(air) for hop height and rank(state).

I did not touch shell.js, index.html, town.js, rounds.mjs, forge.json, scripts/ or the two playtest/serve test files, and I added no page or catalogue entry.

To try it: bun test sack-race/course.test.mjs.

🤖 Generated with Claude Code

Approved

Approved. The rules are plain data in and out, seeded only through mulberry32, and every rejection returns the same state object with a reason. Each rule in the issue has a test that would fail without it, and the pull states the measured time (56 ms). The change adds no page, so there is no first paint, keyboard or phone surface to judge; the preview is unchanged. Two things I would do next: (1) add the playable page and its catalogue entry on top of course.js, with keyboard (Space/Enter) and tap hopping, a visible timing window cue, and an aria-live region fed by describe(); (2) credit the traditional fete sack race in that page copy.

commented

Left #307 open: 1 unticked item remain (sack-race/course.js holds the rules as plain data in, plain data out, with no DOM. start(seed) gives the lane length, the timing window in ticks, and three rivals each with a seeded stride and rhythm within stated ranges. Randomness comes only from the seed, so the same seed and inputs give the same race; no timers and no Math.random.).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs