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

Migrated from github.com/amirmushichge/pixel-plays

Cheese Rolling rules: a tested `cheese-rolling/hill.js` where you chase a seeded rolling cheese down a steep hill, dodging tussocks, and catch it before the bottom, in a two-file pull with a test file that runs in under a second #359

Merged opened this

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

Merged

I added `cheese-rolling/hill.js` and `cheese-rolling/hill.test.mjs` and pushed them. The nine tests pass, and `bun test cheese-rolling/hill.test.mjs` ran in 127–130 ms on its own. I did not run the full `bun test`. `bun scripts/gate.ts` doesn't exist in this clone, and `bun x tsc --noEmit` only printed its help text (there is no tsconfig), so neither check ran.

shipped production is serving it deploy 5b5fb07

opened this

I added cheese-rolling/hill.js and cheese-rolling/hill.test.mjs and pushed them. The nine tests pass, and bun test cheese-rolling/hill.test.mjs ran in 127–130 ms on its own. I did not run the full bun test. bun scripts/gate.ts doesn't exist in this clone, and bun x tsc --noEmit only printed its help text (there is no tsconfig), so neither check ran.

What changed

  • hill.js has the same UMD shape as tide-crossing/causeway.js and exports CheeseRolling in the browser. Its only randomness is a seeded mulberry32 generator, so a seed always gives the same hill and cheese path. It exports start, step, solve, describe, ACTIONS and MAX_LEVEL.
  • start(seed, level) gives a hill with at least 5 lanes and 20 rows (more at higher levels, which also add tussocks and longer trips), plus the tussocks, the runner's start, the cheese's start and its path. Tussocks never sit on the cheese path. The runner starts at row 4 and the cheese at row 0, so the cheese rolls down onto the runner and the runner has to be on its square when it arrives. A runner that just moves down alongside the cheese can never catch it, so I chose this over a runner that starts above the cheese. Say if you want a different set-up.
  • Each level is generated, solved by breadth-first search, and regenerated if it can't be won. If 50 tries all fail, start falls back to a hill with no tussocks. That fallback is not proven winnable, but none of the 20 seeds tested at any level reached it.
  • step(state, action) takes left, right, down or stay. It refuses with unknown-action, off-hill or game-over and returns the same state object each time.
  • A tussock trips the runner for tripTicks ticks (2, rising at higher levels). During those ticks the runner stays put and the cheese keeps rolling.
  • Sharing the cheese's square wins, with a line like Caught the cheese on row N at tick T. The cheese passing the bottom loses with Lost: …. State reports tick, trips and rowsLeft for the HUD.

Tests

  • They cover:

- determinism, with Math.random stubbed to throw - hill shape - each refusal and its reason - the trip cost - the win line and the loss line - the HUD fields - all of seeds 1–20 at levels 0–4 winnable, by a search that only calls step

  • They load no page, renderer or server.

Left out No page, catalogue entry or HUD, as the issue asks. Nothing outside cheese-rolling/ was touched.

Closes #350

commented

I added cheese-rolling/hill.js and cheese-rolling/hill.test.mjs and pushed them. The nine tests pass, and bun test cheese-rolling/hill.test.mjs ran in 127–130 ms on its own. I did not run the full bun test. bun scripts/gate.ts doesn't exist in this clone, and bun x tsc --noEmit only printed its help text (there is no tsconfig), so neither check ran.

What changed

  • hill.js has the same UMD shape as tide-crossing/causeway.js and exports CheeseRolling in the browser. Its only randomness is a seeded mulberry32 generator, so a seed always gives the same hill and cheese path. It exports start, step, solve, describe, ACTIONS and MAX_LEVEL.
  • start(seed, level) gives a hill with at least 5 lanes and 20 rows (more at higher levels, which also add tussocks and longer trips), plus the tussocks, the runner's start, the cheese's start and its path. Tussocks never sit on the cheese path. The runner starts at row 4 and the cheese at row 0, so the cheese rolls down onto the runner and the runner has to be on its square when it arrives. A runner that just moves down alongside the cheese can never catch it, so I chose this over a runner that starts above the cheese. Say if you want a different set-up.
  • Each level is generated, solved by breadth-first search, and regenerated if it can't be won. If 50 tries all fail, start falls back to a hill with no tussocks. That fallback is not proven winnable, but none of the 20 seeds tested at any level reached it.
  • step(state, action) takes left, right, down or stay. It refuses with unknown-action, off-hill or game-over and returns the same state object each time.
  • A tussock trips the runner for tripTicks ticks (2, rising at higher levels). During those ticks the runner stays put and the cheese keeps rolling.
  • Sharing the cheese's square wins, with a line like Caught the cheese on row N at tick T. The cheese passing the bottom loses with Lost: …. State reports tick, trips and rowsLeft for the HUD.

Tests

  • They cover:

- determinism, with Math.random stubbed to throw - hill shape - each refusal and its reason - the trip cost - the win line and the loss line - the HUD fields - all of seeds 1–20 at levels 0–4 winnable, by a search that only calls step

  • They load no page, renderer or server.

Left out No page, catalogue entry or HUD, as the issue asks. Nothing outside cheese-rolling/ was touched.

Changes requested — before the latest push

The rules are sound and the tests are good, but one claim in hill.js is false and one direction item is missing. Fix first: the tussock-free fallback in start() is NOT always winnable. The runner moves 1 square per tick and the cheese reaches the runner row (4) at tick 4; a runner that heads down to meet it at row r has only 4 lateral moves in total (r ticks, r-4 of them downs). The runner column is random across 5..9 lanes and path[4] can be up to 4 lanes off centre, so the gap can be 8 and the hill cannot be won. Make the fallback constructive: build the bare hill with the runner placed on path[RUNNER_ROW] (or set the runner column so a catch is guaranteed), correct the comment, and add a test that forces the fallback path (e.g. export a build-with-no-tussocks helper, or test that a bare hill with the runner on the cheese path is solved) so it is covered. Second: the direction says to reference original creators where due. This game is the Cooper's Hill Cheese-Rolling tradition (Gloucestershire); name it in the header comment of hill.js, alongside the existing mulberry32 credit. Everything else checks out: UMD shape, seeded-only randomness, refusals return the same state, trip cost, win and loss lines, HUD fields, 20 seeds x 5 levels searched with step only, tests load no page. No page exists yet by design, so there is no preview to judge; the unfiled state of reachability is the issue's own scoping.

commented

Both review points are fixed on the same branch and pushed. bun test cheese-rolling/hill.test.mjs passes all 10 tests in 128 ms.

  • Fallback: you were right that a bare hill with a random runner column isn't always winnable. The fallback in start() now calls a new exported bareHill(seed, level). It builds a hill with no tussocks and puts the runner on path[4], the cheese's lane at the runner's row. The runner just waits and the cheese arrives on tick 4, so the catch is guaranteed. I rewrote the comment to say so.
  • Test for the fallback: a new test runs bareHill for 50 seeds at all 5 levels. It checks there are no tussocks, that solve finds a catch in 4 ticks, and that four stay steps win with the catch on row 4. The first version expected the solver's answer to be exactly four stay actions, but it found an equally short left/right route, so I assert the length instead.
  • Credit: the header of hill.js now names the Cooper's Hill Cheese-Rolling tradition of Gloucestershire, next to the mulberry32 credit.
Approved

Clean rules module. Randomness is seeded mulberry32 only, with Math.random stubbed to throw in a test. Refusals return the same state object with a reason. The trip cost, the win and loss lines and the HUD fields are each tested. The tussock-free fallback is now constructive (runner placed on path[4]) and has its own test. The Cooper's Hill tradition is credited in the header. There is no page by design, so the preview has nothing to show. Measured time of 128 ms is stated on the pull. No security surface: no I/O, no DOM, no dependencies.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs