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

Migrated from github.com/amirmushichge/pixel-plays

Add Village Darts: the playable page and its place in the catalogue, on the rules in `village-darts/board.js` #347

Merged opened this

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

Merged

Village Darts is playable at `/village-darts/` and listed in the catalogue, built on `board.js` without touching it. The forge's check has not run yet. Here I ran the folder's page tests, `serve.test.mjs`, `shell.test.mjs`, `town.test.mjs` and the `playtest.test.mjs` cases for `village-darts`; all pass, and `bun x tsc --noEmit` printed only the compiler's help text. I did not run a bare `bun test`…

shipped production is serving it deploy 5b5fb07

opened this

Village Darts is playable at /village-darts/ and listed in the catalogue, built on board.js without touching it. The forge's check has not run yet. Here I ran the folder's page tests, serve.test.mjs, shell.test.mjs, town.test.mjs and the playtest.test.mjs cases for village-darts; all pass, and bun x tsc --noEmit printed only the compiler's help text. I did not run a bare bun test.

What changed

  • Page: village-darts/index.html draws the hall wall and the board, with the segments in the rules' order and the rings at the rules' radii.

- The marker moves one step per tick at 24 ticks a second. Tap, Space or Enter throws, R restarts. - The HUD shows both scores, the darts left and whose visit it is, all read from the rules state. A throw's result reaches the HUD and the aria-live line when the dart lands, not when it leaves your hand. - Each dart is announced with its ring, segment and points. A bust, a refusal reason and the end line are announced too.

  • Rival and darts: The rival's visit plays one dart at a time, with 1.3 s before its first dart and 0.55 s between darts. Landed darts stay on the board until the visit ends, then clear after 1.1 s.

- Your darts are blue, lob in an arc and have triangle flights; the rival's are red, fly flat and have square flights. - Bunting along the top of the wall flutters at 8 fps and stops under prefers-reduced-motion. The marker keeps moving, since it is play. I added a README line saying so.

  • Catalogue: shell.js, the root index.html <li>, the README Games table and the list of games without music have the entry. The town gets its building from the GAMES entry, so town.js is unchanged. bun scripts/og.mjs has been run, which added og/village-darts.png and updated og/home.png.
  • Tests: rounds.mjs has a scripted round that waits for the marker to pass the rival's own stated target for the score, then throws. It reaches the end line in 64 s of play, with a win for you in 23 darts. serve.test.mjs asserts that /village-darts/ and /village-darts/board.js answer 200. village-darts/page.test.mjs is new.
  • Round limit: The round script sets limit: 140, so it is allowed longer than the default 150 s cap would be tight for. It finishes well inside that.

Things to look at

  • board.js gap: rivalDart returns only the new state, not where the dart landed, and a visit-ending or bust dart leaves no record of its position. The page recovers it by probing rivalDart on a copy of the state at 301 (so it cannot bust, and the rival aims at treble 20) and re-basing the landing on the real target. page.test.mjs checks the result against the rules over 399 seeds. If rivalDart returned its dart, the probe could go.
  • Kind: I used kind: 'Sport', not the issue's Sports. serve.test.mjs only accepts the existing Sport, and a second spelling would split the filter.

Try it: node serve.mjs, then open /village-darts/.

🤖 Generated with Claude Code

Closes #343

commented

Village Darts is playable at /village-darts/ and listed in the catalogue, built on board.js without touching it. The forge's check has not run yet. Here I ran the folder's page tests, serve.test.mjs, shell.test.mjs, town.test.mjs and the playtest.test.mjs cases for village-darts; all pass, and bun x tsc --noEmit printed only the compiler's help text. I did not run a bare bun test.

What changed

  • Page: village-darts/index.html draws the hall wall and the board, with the segments in the rules' order and the rings at the rules' radii.

- The marker moves one step per tick at 24 ticks a second. Tap, Space or Enter throws, R restarts. - The HUD shows both scores, the darts left and whose visit it is, all read from the rules state. A throw's result reaches the HUD and the aria-live line when the dart lands, not when it leaves your hand. - Each dart is announced with its ring, segment and points. A bust, a refusal reason and the end line are announced too.

  • Rival and darts: The rival's visit plays one dart at a time, with 1.3 s before its first dart and 0.55 s between darts. Landed darts stay on the board until the visit ends, then clear after 1.1 s.

- Your darts are blue, lob in an arc and have triangle flights; the rival's are red, fly flat and have square flights. - Bunting along the top of the wall flutters at 8 fps and stops under prefers-reduced-motion. The marker keeps moving, since it is play. I added a README line saying so.

  • Catalogue: shell.js, the root index.html <li>, the README Games table and the list of games without music have the entry. The town gets its building from the GAMES entry, so town.js is unchanged. bun scripts/og.mjs has been run, which added og/village-darts.png and updated og/home.png.
  • Tests: rounds.mjs has a scripted round that waits for the marker to pass the rival's own stated target for the score, then throws. It reaches the end line in 64 s of play, with a win for you in 23 darts. serve.test.mjs asserts that /village-darts/ and /village-darts/board.js answer 200. village-darts/page.test.mjs is new.
  • Round limit: The round script sets limit: 140, so it is allowed longer than the default 150 s cap would be tight for. It finishes well inside that.

Things to look at

  • board.js gap: rivalDart returns only the new state, not where the dart landed, and a visit-ending or bust dart leaves no record of its position. The page recovers it by probing rivalDart on a copy of the state at 301 (so it cannot bust, and the rival aims at treble 20) and re-basing the landing on the real target. page.test.mjs checks the result against the rules over 399 seeds. If rivalDart returned its dart, the probe could go.
  • Kind: I used kind: 'Sport', not the issue's Sports. serve.test.mjs only accepts the existing Sport, and a second spelling would split the filter.

Try it: node serve.mjs, then open /village-darts/.

🤖 Generated with Claude Code

Changes requested — before the latest push

Playable and served (200 on the preview), catalogue wiring is complete, and the Sport/Sports choice is reasonable. Changes for one round:

  1. FIRST: the bust test in village-darts/page.test.mjs ('a bust says so…') is vacuous: it asserts 'Bust' only inside if (g.read('G.stuck[0].points') > 0) on a random seed, so it passes whether or not the page announces a bust. Make it deterministic (boot a state whose next dart must bust, e.g. scores.you = 2 with a seeded P, or force the aim to a known scoring point) and assert the status contains 'Bust' and the score returned.
  2. Dead code in village-darts/index.html: G.said is written but never read; boardColor(x, y, r) never uses r; flightAt returns t that drawFlight never uses. Remove them.
  3. The rival trail in drawFlight uses FROM.rival[0] - landed(f.dart)[0] > 0 ? -1 : 1 which is opaque; name the direction in a const.
  4. rivalThrow re-bases a probe to find where the rival dart landed and computes dart.bust in the page, which is a game rule outside board.js. The seed test checks scores but not landing positions. Add an assertion that the probe's (x,y) matches the segment/points the rules state implies, or keep the gap noted in an issue so board.js can return the dart.
  5. The 'page source' test is string-grepping the HTML (e.g. 'STILL ? 0'); prefer a behavioural check that bunting does not change with reduced motion if boot supports it.

I could not view the screenshots in this sandbox, so layout on 360px is unchecked by me.

commented

All five review points are addressed and pushed. The village-darts tests, serve.test.mjs and the village-darts playtest cases pass here. The forge's full check hasn't run.

  1. Bust test: it is now deterministic. It sets the score to 2 and puts the marker on treble 20, where any dart scores. It then checks that the dart is marked as a bust, that the status reads "Bust: your score goes back to 2", that the score is still 2, and that the turn passes to the rival.
  2. Dead code: G.said, the unused r parameter of boardColor and the unused t returned by flightAt are gone.
  3. Rival trail: the opaque ternary is replaced by a named behind direction. In doing so I found the old trail was drawn ahead of the dart rather than behind it, so I fixed that too.
  4. rivalThrow: the bust rule is out of the page. The dart's score and bust flag now come from the rules' own M.land(s, x, y).dart.

- The seed test now calls M.land at the recovered position and checks that it gives the same status, turn, scores and visit as rivalDart, including each dart's ring, segment, points and bust flag. - It does not compare x and y exactly, because the recovered position is rounded twice and can differ by 0.01 mm. - board.js still lacks a way to return the rival's dart, so the probe stays. I haven't opened an issue for that.

  1. Page-source test: the STILL ? 0 and flight-shape string checks are removed. Two behavioural tests replace them.

- A reduced-motion boot gives one unchanging picture, while a normal boot's picture changes as the bunting flutters. - Your dart's flight sits well above the straight line from throw to landing, and the rival's follows it.

I still check the aria-label, the 44 px buttons, the mute button, the live region, the four-line rules popup and the absence of .mp3 by reading the page source. Those are static markup, so I left them as source checks.

I haven't checked the layout at 360 px myself either; the canvas is the engine's fixed 360x280.

the crew's run was interrupted: restart, resumes on the next pass — cancelled
commented

I merged main into the branch and pushed it (78506a0). serve.test.mjs passes after the merge and card regeneration (90 tests). Before the card was regenerated, the run over serve.test.mjs, village-darts and carrot-pull was 132 pass, 1 fail.

  • What the merge brought together: Village Darts is now in the catalogue next to the games that landed on main meanwhile, including Carrot Pull and Ticket Booth.
  • Conflicts: The text conflicts in README.md, index.html, shell.js and serve.test.mjs were already resolved in the working tree, with both sides' entries kept. The conflict that remained was the binary og/home.png.
  • Home card: It no longer matched what the games render, so the "committed card is what the games render" test failed. I regenerated it with bun scripts/og.mjs, and that test passes now.
  • Checks I didn't run: I didn't re-run the Village Darts and Carrot Pull page tests after regenerating the card. bun x tsc --noEmit printed only its usage text (no compiler errors shown), and the repository has no scripts/gate.ts or scripts/fast.ts to run.

The forge's check on this push runs the full suite, including the cell and socket tests that are skipped here.

Approved

Earlier asks (deterministic bust test, dead code removed, named rival trail, bust rule taken out of the page via M.land, behavioural tests in place of source greps) are met in the head. The latest push is only a merge of main and a regenerated og/home.png, and the preview answers 200. Non-blocking, next: 1) add a way for board.js rivalDart to return its dart so the probe in rivalThrow can go (open an issue); 2) check the layout once on a real 360px phone. Kind is Sport rather than the issue Sports, which matches the existing filter and serve.test.

commented

Left #343 open: 3 unticked items remain (Follows the README Visual rules: 360x280 canvas via engine.js, fixed 30-degree camera, hard shadows screen-right, and ambient motion stopped under prefers-reduced-motion. Darts that have landed stay on the board until the visit ends, and yours differ from the rival's by flight shape as well as colour. The canvas aria-label says how to play. The rules popup is at most four lines. There is a #mute button and no .mp3 is committed.; Listed in GAMES in shell.js with id village-darts, title Village Darts, kind Sports, input Tap, music: false and forged: true. It has the matching <li> in the root index.html and a building in the town from town.js like every other game. It appears in the README Games table and the list of games without music. bun scripts/og.mjs has been run.; rounds.mjs has a scripted round for village-darts that reaches the end line. serve.test.mjs asserts that /village-darts/ and /village-darts/board.js answer 200. playtest.test.mjs passes for the folder, and bun test passes. Any new page test stays inside the per-game test budget the suite already holds to.).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs