Dust gets a run sheet, a workbench that can be typed on, and the checkers opponent the disc left out - #388
Merged
Conversation
…kers opponent the disc left out Six pieces of work that turned out to be one: Dust could not be driven from a sheet, and every attempt to drive it found something else that was not there. **The checkers opponent.** `CHECKERS.PRP` is the whole game except the one thing a game needs — it draws the board, validates the drag, plays the taunts, counts the dead and decides who won, then asks `CHECKERS.DLL` what the opponent does. Unimplemented, `pluginfx` answered 0 and `playerturn` went straight back to true: the player moves, nothing happens, and the game looks like it is still waiting for them. Nothing here is guessed — the script is the authority on the 64-cell board, the three-character move, and the difference between a hop CHAIN and ALTERNATIVES with jumps first. Negamax with alpha-beta at the depth the game asks for, ties broken on the session's rng so a seeded run plays the same game twice. One quirk is kept because the game has it visibly: a man that crowns mid-chain keeps jumping, as a king, and may jump backwards. **An idle channel says "none".** `currentvoice ()` and `currentsound ()` answered "" and Dust asks the question the other way round in nine places — `while currentvoice () != "none"` is true for ever. Six of those are in `CHECKERS.PRP` and one is in `win ()` on the path taken when the player LOSES, so a game of checkers worked until Bolivar won it and then hung with his gloat half-said. The other three are the flute room. A rung of the playthrough had monkeypatched the builtin to get past it, which left the suite green on a room the browser hung in; that patch goes. **The editor hears its own keystrokes.** Dust's shortcut handlers listened on `window` without asking whether the focused element owned the key, so every character typed into the sheet went to the game as well. The workbench suites drove this with `page.fill`, which dispatches no key events, so the bug was invisible to the tests written over it. **The sheet grammar learns to say `or`.** Routes have disjunctive stops — every rung that clicks a character stops on "either they answered OR their phase moved on" — and a grammar without `or` has to drop one half and hope. Three conditions the routes ask and the grammar could not: `choosing`, `puppet`, and `star.<name>`. `global.<name>` with no comparison now means "set to something", because "" is a real value in this game. **Two controls the workbench wanted:** a mute that sits outside the game (a multiplier on the sink, not a write the next room change undoes; a mute and not `setSuspended`, so a muted run takes exactly as long as a loud one), and a click on a line number that moves the execution pointer — loud about not having moved the game, and refused outright while a run is in flight. **The run sheet, and the transcriber that must account for itself.** Eight Dust verbs (`goto`, `meet`, `takeInHand`, `give`, `offer`, `talkOut`, `doorAt`, `loadSave`) and a tool that writes the sheet from the golden thread's own rungs. The transcriber used to scan for calls it KNEW and emit nothing for the rest, which cannot tell "the rung does not do that" from "I do not read that" — five separate bugs were that one bug. Inverted, every call is transcribed, named in a table as something that reads or asserts, or marked; the marked count is a measurement that can only fall by transcribing something or declaring it. **And the sheet has now been driven.** A full sweep of all 54 legs (`npm run speedrun:legs -w dust`) finishes 12 and lands the rest on a small number of families rather than 42 separate faults: 12 legs where a door opens and the step through it is refused, 5 rung claims the run does not reach, 4 where `say` fires before the conversation opens, 4 stuck overlays. That sweep found one bug of its own, fixed here: the transcriber negated an already-translated condition by prefixing `!`, so `global.blackout != -1` became `!global.blackout != -1` — the accessor form negated and then compared, which never comes true. Two mine legs waited on it and pressed an arrow key some 3,675 times apiece. `negate()` parenthesises anything that is not a bare atom. The leg sweep's `APP_URL` also defaulted to a port Dust's dev server does not use. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Six pieces of work that turned out to be one: Dust could not be driven from a sheet, and every attempt to drive it found something else that was not there.
The checkers opponent
CHECKERS.PRPis the whole game except the one thing a game needs — it draws the board, validates the drag, plays the taunts, counts the dead and decides who won, then asksCHECKERS.DLLwhat the opponent does. Unimplemented,pluginfxanswered 0 andplayerturnwent straight back to true: the player moves, nothing happens, and the game looks like it is still waiting for them.Nothing is guessed — the script is the authority on the 64-cell board, the three-character move, and the difference between a hop CHAIN and ALTERNATIVES with jumps first. Negamax with alpha-beta at the depth the game asks for, ties broken on the session's rng so a seeded run plays the same game twice. One quirk is kept because the game has it visibly: a man that crowns mid-chain keeps jumping, as a king, and may jump backwards.
The strongest test replays every generated move through an independent transcription of the script's own
goodmove/goodjump/makemove.An idle channel says "none"
currentvoice ()andcurrentsound ()answered"", and Dust asks the question the other way round in nine places:"" != "none"is true for ever. Six of those are inCHECKERS.PRPand one is inwin ()on the path taken when the player loses — so checkers worked until Bolivar won and then hung with his gloat half-said. The other three are the flute room.It was already found once and worked around: a playthrough rung monkeypatched the builtin, which left the suite green on a room the browser hung in. That patch goes.
The editor hears its own keystrokes
Dust's shortcut handlers listened on
windowwithout asking whether the focused element owned the key, so every character typed into the sheet went to the game too. The workbench suites drove this withpage.fill, which dispatches no key events — so the bug was invisible to the tests written over it.Sheet grammar, and two workbench controls
or/and/!with parentheses, because routes have disjunctive stops. Three conditions the routes ask and the grammar could not:choosing,puppet,star.<name>.global.<name>with no comparison now means "set to something", since""is a real value in this game.A mute that sits outside the game — a multiplier on the sink rather than a write the next room change silently undoes, and a mute rather than
setSuspendedso a muted run takes exactly as long as a loud one. And a click on a line number moves the execution pointer: loud about not having moved the game, refused outright while a run is in flight.The run sheet and its transcriber
Eight Dust verbs (
goto,meet,takeInHand,give,offer,talkOut,doorAt,loadSave) and a tool that writes the sheet from the golden thread's own rungs.The transcriber used to scan for calls it knew and emit nothing for the rest, which cannot tell "the rung does not do that" from "I do not read that" — five separate bugs were that one bug. Inverted, every call is transcribed, named in a table as something that reads or asserts, or marked; the marked count is a measurement that can only fall by transcribing something or declaring it.
The sheet has now been driven
A full sweep of all 54 legs (
npm run speedrun:legs -w dust) finishes 12, and lands the rest on a small number of families rather than 42 separate faults:sayfires before the conversation opensuntil:condition — fixed hereThe sweep found one bug of its own: the transcriber negated an already-translated condition by prefixing
!, soglobal.blackout != -1became!global.blackout != -1— the accessor form negated and then compared, which never comes true. Two mine legs waited on it and pressed an arrow key some 3,675 times apiece.negate()now parenthesises anything that is not a bare atom.This is not a continuous run and its total is not a time. Every leg still opens by loading the save it starts from, so each inherits a perfect state; joining them is the next milestone, and the 12-leg door cluster is the thing standing in front of it. Notably
doorAt(… owner: salout)succeeds on day 1 and fails on day 3 from an identical standpoint, so that cluster is at least partly the world's own state rather than the verb.Releasing
Dust
0.3.19and Titanic0.9.71— Titanic because the shared engine changed (audio, scheduler, plugins) and its speedrun page gets the same two controls.Full suite green: 835 tests across 74 files.
🤖 Generated with Claude Code