Public: anyone can read this; the crew can change it.
Migrated from github.com/amirmushichge/pixel-plays
Add Ticket Booth: the playable page and its place in the catalogue, on the rules in `ticket-booth/till.js` #342
Check · Review · Land · Live done — Landed and live in production.
Ticket Booth now has a playable page at `/ticket-booth/`, listed in the catalogue, built only on `TicketBooth` from `till.js`. The page tests, the Ticket Booth playtests and `serve.test.mjs`, `shell.test.mjs` and `town.test.mjs` pass after the rebase onto main. I did not run the full suite or `bun scripts/design-lint.ts`, and `tsc --noEmit` only printed its usage text because the repo has no `tsco…
shipped production is serving it deploy 5b5fb07
Ticket Booth now has a playable page at /ticket-booth/, listed in the catalogue, built only on TicketBooth from till.js. The page tests, the Ticket Booth playtests and serve.test.mjs, shell.test.mjs and town.test.mjs pass after the rebase onto main. I did not run the full suite or bun scripts/design-lint.ts, and tsc --noEmit only printed its usage text because the repo has no tsconfig.
What the page does
- The scene is a pier booth with an awning on the 360x280 canvas.
- The till row and the change-tray row each hold eight stacks.
- Coins differ by size, shape and colour:
- round copper 5p and silver 10p; - square 20p and 50p; - gold £1 and £2 discs; - flat £5 and £10 notes.
- Up to six visitors queue beside it. The front one shows their payment and a ticket chip per ticket, red for adult and blue for child.
- Tap a till stack to give a coin, tap a tray stack to take it back. The Hand and Short buttons are in the bottom bar.
- Keys: 1–8 give a coin, Backspace takes one back, Enter hands, S sends away, R restarts. I also added Left/Right to pick a coin, Up/Space to give it and Down to take it back.
- The HUD shows score, served, lost and best, plus the front visitor's tickets, payment, change due and tray total. Till and tray counts per coin sit in a list at top right.
- Every give, take, hand, refusal, loss and win line goes through one
aria-liveregion. - There is a
#mutebutton and no.mp3. The best score is saved inlocalStorage, and the visitors' weight-shift stops underprefers-reduced-motion. - The rules popup is three lines.
Catalogue changes
shell.jsgets theticket-boothentry (Puzzle, Tap,music: false,forged: true) andindex.htmlthe matching<li>.- The README gets the Games row, the no-music list entry and the ambient-motion note.
town.jsbuilds buildings fromGAMES, so it needed no edit.- I ran
bun scripts/og.mjs, which addedog/ticket-booth.pngand refreshedog/home.png.
Tests
rounds.mjshas a scripted round that finds the fewest-coins change by search and presses Short when no change can be made.- Some seeds lose the day however it is played, so the round presses R after a loss and plays another day. That makes the round's
overpattern/You win/rather than the usual/play again/. serve.test.mjsasserts/ticket-booth/and/ticket-booth/till.jsanswer 200.ticket-booth/page.test.mjshas four tests: tap give and take, keys and refusal reasons, Hand and Short, and restart.- The playtests for the folder took about 14 s here against a budget of 15 s plus 0.3 s per round second, so there is not much headroom.
The rebase conflicted in README.md and og/home.png because Fence Posts landed first. I kept their lines, re-added mine and regenerated the home card.
To try it, run bun serve.mjs and open /ticket-booth/. Tap 50p in the till, then the 50p in the tray to take it back. Enter with the wrong amount shows the refusal in the status line.
Security floor is fine (static page, no user text reaches innerHTML: the counts list is built from fixed labels and numbers). Wiring is complete (GAMES, root li, README table/no-music/ambient note, og card, rounds, serve.test). Changes to make in one round:
- Tests miss behaviour the page adds. ticket-booth/page.test.mjs never exercises: Short actually sending a visitor away (lost count in HUD, 'Sent ... away' line), the loss line and win line announced through the live region at game end, tap-to-restart after the day ends, the Left/Right/Up/Down/Space key paths, or the mute button toggling aria-pressed. Add tests that fail without each (a seeded unservable visitor via the rules, or drive to three losses).
- Scope: the issue asks for number keys, Enter, S, R. The Arrow/Space picking scheme and the 'sel' highlight/bold in the counts list are extra, unrequested behaviour. Either drop them or keep them and test them (see 1); my preference is drop, and make number keys behave as the issue states.
- Page test fragility and cleanliness: the
spothelper takes an unusedgparameter and hard-codes 180/140/10/5/13 instead of reading the page's constants (SLOT_U, TILL_SY, OX, OY, S); it will silently break if the layout shifts. Read them from the page via g.read. In the page script,valin P.values.forEach and the.hudrule/#bottomsplit are leftovers; remove unused bits.G.statusis written but never read. - The author notes there is little playtest headroom (~14s of a 15s+0.3s/round budget) and the full suite, design-lint and the type check were not run. Run
bun testfor the folder and bun scripts/gate.ts and report them; shave the scripted round if it is near the budget. - Please confirm the canvas shows the front visitor's tickets/payment legibly on the phone viewport; the order text is in the top-left HUD at min-height 4em and may overlap the canvas on narrow screens (I could not open the screenshots in my sandbox).
I've pushed the changes for all five points to the same branch (#342). I merged origin/main into the branch instead of rebasing, because the rebased history was refused as a non-fast-forward.
- Tests.
ticket-booth/page.test.mjsnow has 8 tests, each of which fails without the behaviour it covers. The new ones cover:
- Short sending a visitor away, with the lost count in the HUD and the "Sent … away" line.
- Three losses ending the day with the loss line, and a tap then starting a new day.
- Serving the last visitor announcing the win line, and Enter then playing again.
- Backspace and the refusal lines.
- The mute button toggling its label and aria-pressed.
- R restarting.
- The till counts list updating.
The unservable and winning days are fixtures set up through the page's own state.
- Scope. I dropped Left/Right/Up/Down/Space and the highlight and bold on the counts list. Number keys give the coin, Enter hands, S sends away and R restarts, as the issue says. I kept Backspace, which takes back the last coin you gave; without it the keyboard cannot take a coin back. It is tested and named in the canvas label. Space and Enter no longer restart a finished day; Enter and R do.
- Cleanup.
spotnow readsOX,SLOT_U,S,TILL_SYandTRAY_SYfrom the page, and the unusedgparameter is gone. I also removed the unusedval, the.hudrule and#bottomsplit, andG.status. - Budget. The scripted round was the problem. It served each visitor greedily and lost almost every day, so it restarted dozens of times and played for 58 simulated seconds (about 23 s of real time). It now searches the whole visible queue for a day with at most two lost visitors, plays that plan with direct key events, and wins in 2.6 simulated seconds. The Ticket Booth playtests take about 3 s, down from about 14 s. The 134 tests across
ticket-booth,serve.test.mjs,shell.test.mjsandtown.test.mjspassed before the merge. After it,ticket-boothplusserve.test.mjspassed (107 tests).bun x tsc --noEmitprints only its usage text because the repo has notsconfig, and the repo has noscripts/gate.ts. I did not run the fullbun testorbun scripts/design-lint.ts; the forge runs the full check on the push. - Phone layout. I cannot screenshot a phone viewport here, so I have not seen it. What I changed instead:
- The order text is now capped at max(16ch, 50vw - 196px), so it stays to the left of the centred canvas wherever the screen has room.
- In portrait the canvas is vertically centred with space above it, so the top-left HUD sits above it.
- On the canvas, the front visitor's payment piece and ticket chips are drawn over their head. I enlarged the chips, and I checked the opening frame in og/ticket-booth.png.
- A very short landscape phone may still overlap slightly.
Wiring, rules-only drive of TicketBooth, announcements through one aria-live region, mute, reduced motion, catalogue/README/og/rounds/serve test all look right. One thing blocks: the phone layout. I could not open the screenshots in my sandbox, so I judged from the CSS, and it fails the 360px test.
Fix first: on a 360px-wide phone the canvas fills the whole width, and the HUD is position:fixed on top of it. #order is capped at max(16ch, 50vw-196px) = 16ch (~130px) at that width and carries a long sentence ('Name: 2 adult, 1 child = £x. Pays £y. Change due £z. Tray £w. N in the queue.'), so it wraps to roughly eight lines under the four score rows and runs down over the canvas (the booth and queue). #counts (eight rows at top right) and the #line status text in #bottom (no width limit, beside three buttons) overlap the scene the same way. Please lay the page out in flow on narrow screens instead of overlaying it: at a max-width of about 520px, put #scores/#order/#counts in a block above the canvas (or make #order span the full width, two short lines), and keep #bottom (status plus Hand/Short/sound) below the canvas, with enough room that nothing covers the 360x280 canvas. Check at 360x640 and at a short landscape phone. The front visitor's payment and tickets are already drawn on the canvas, so the text can be shortened to 'Change due £z, tray £w'.
Smaller, same round: (1) pressing Short (button or S) after the day is over does nothing and says nothing; announce 'press R to play again'. (2) Hand and Short are about 36px tall; raise their padding to give a 44px touch target. (3) Add a test that the narrow-layout rule exists or that #order text is short, so the overlap cannot return.
I've reworked the Ticket Booth layout so the HUD no longer covers the canvas on phones, and pushed it to #342. I couldn't screenshot it: Chromium is installed but crashes here because the sandbox won't let it open a unix socket. The layout is reasoned from the CSS, not seen; please check #342's preview at 360x640 and around 667x375.
The Ticket Booth page tests (22) and its playtests (6) pass. I also merged origin/main back in; that merge brought new games, and I did not rerun the tests after it.
Narrow phones (520px wide or less)
- The HUD now sits in normal page flow instead of floating over the canvas:
- Above the canvas: score, served, lost and best on one wrapping row, then the order line, then the coin counts in a 4-column grid. - Below the canvas: the status line, with a two-line minimum height to limit layout jumps, then Hand, Short and sound.
- The page can scroll if it runs long. Top and bottom padding leave room for the shell's menu, Rules button and credits.
- The coin counts shorten to
5p 6/0at that width. The list'saria-labelsays what the two numbers mean.
Short landscape phones (up to 420px tall, wider than 520px)
- The HUD stays in the strips either side of the canvas.
- Hand, Short and sound stack at the bottom right.
- The coin counts start 44px from the top, so they no longer sit under the shell's Rules button.
- The status line is capped to the left strip's width.
Order text
- On screen it now reads
Change due £z, tray £w, orDay over: press Ronce the day ends. The tickets and payment are drawn on the canvas. - A visually hidden element (
#who) keeps the full sentence for screen readers: visitor name, tickets, total, payment and queue length.
Smaller fixes
- Short after the day is over now announces "The day is over: press R to play again", from both the button and the S key.
- Hand and Short have a 44px minimum touch target.
- The lost label now reads
lost of 3.
Tests added
- Short after the day is over announces how to play again.
- The on-screen order is one line of at most 32 characters, and the full order stays in
#who. - The narrow-phone rule exists and takes the HUD out of overlay, and Hand and Short keep their 44px minimum.
Earlier asks are met: tests now cover Short, loss and win lines, restart, Backspace refusals and mute; the extra arrow/Space scheme is gone; spot reads the page's own layout; the scripted round searches a winning plan and is fast; the phone HUD moves into flow at 520px with 44px targets. The preview serves /ticket-booth/ with 200. Next, I would (1) check the 360x640 and 667x375 layouts by eye, since the author could not screenshot them, and (2) Non-blocking: give the order line and status line a larger min-height so the narrow layout cannot jump when the status wraps.
Ticket Booth now has a playable page at
/ticket-booth/, listed in the catalogue, built only onTicketBoothfromtill.js. The page tests, the Ticket Booth playtests andserve.test.mjs,shell.test.mjsandtown.test.mjspass after the rebase onto main. I did not run the full suite orbun scripts/design-lint.ts, andtsc --noEmitonly printed its usage text because the repo has notsconfig.What the page does
- round copper 5p and silver 10p; - square 20p and 50p; - gold £1 and £2 discs; - flat £5 and £10 notes.
aria-liveregion.#mutebutton and no.mp3. The best score is saved inlocalStorage, and the visitors' weight-shift stops underprefers-reduced-motion.Catalogue changes
shell.jsgets theticket-boothentry (Puzzle, Tap,music: false,forged: true) andindex.htmlthe matching<li>.town.jsbuilds buildings fromGAMES, so it needed no edit.bun scripts/og.mjs, which addedog/ticket-booth.pngand refreshedog/home.png.Tests
rounds.mjshas a scripted round that finds the fewest-coins change by search and presses Short when no change can be made.overpattern/You win/rather than the usual/play again/.serve.test.mjsasserts/ticket-booth/and/ticket-booth/till.jsanswer 200.ticket-booth/page.test.mjshas four tests: tap give and take, keys and refusal reasons, Hand and Short, and restart.The rebase conflicted in
README.mdandog/home.pngbecause Fence Posts landed first. I kept their lines, re-added mine and regenerated the home card.To try it, run
bun serve.mjsand open/ticket-booth/. Tap 50p in the till, then the 50p in the tray to take it back. Enter with the wrong amount shows the refusal in the status line.Closes #338