- Rename course/11-break-your-engine to 13-break-your-engine and update every link and mention; start-module explains that Module 13 has no tests to start. - tools/diff-tracks.mjs (npm run diff:tracks): stages both references of a module, generates a seeded random SpellGrid level and script per seed, pipes it to the Rust and TypeScript CLIs and diffs their output; crashes on one or both sides fail. stagePlay takes a module number. - register_assembled: the TypeScript track compared only the text of an existing Assembled card, so a base re-registered with a new cost (or a different split) silently rewrote the earlier card. It now compares every field the engine reads, as Rust does (Modules 5-10). New test_05_assemble_never_rewrites_an_earlier_card. - SpellGrid CLIs: coordinates and chooser numbers are 1-9 plain digits in both tracks (Rust accepted signs, TypeScript accepted "+2", "0x2", "1e0"); Rust quotes card names verbatim instead of with Debug escapes; Rust joins the minion list with "; " instead of rewriting ", ". Two new CLI tests per track. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Module 9 reference engine (TypeScript)
The course's own engine as of the end of Module 9. Part I's card engine is unchanged, and all of its tests still pass. The Part II engine in src/engine/puzzle/ now plays a whole turn: movement, parse, conversion, parse, hazards, win, parse. Module 8's reference was the starting point, so a diff between the two shows what this module adds.
cards/(at the package root; not in this directory): Part I's card data, a copy ofcourse/cards/staged bytools/verify-references.mjs. Part II doesn't read it.src/engine/puzzle/: the Part II engine. It names no noun: the engine's own words are the operatorsIS,AND,NOT, the meta-nounTEXT, the propertyPUSHof the base ruleTEXT IS PUSH, the propertyYOU, which picks the objects that move, and from Module 9 the propertiesWIN(the win check) andFLOAT(which objects share a tile).turn.ts(new): the turn.Phase,TURN(the phases in order) andHazard(content's rule for what destroys what).level.ts: a level in play. Module 9 addsLevel.step, which runs the phases ofTURN; the conversion phase (X IS Yin place,X IS Xlocks,X IS NOT Xdeletes, and... IS NOT Ykeeps every object it names from becoming aY, ashasPropertysettles negation per object); the hazards phase, which runs content's hazards in order; the win check;Level.sharing(the other objects on a tile at the same float level);wonandlost. TheLevelconstructor takes the hazards.moveYouis now movement, then a reparse.grid.ts: Module 9 addsBoard.setKind.word.ts: Module 9 addsLexicon.thingNamed(what a conversion into a noun makes).movement.ts,parse.ts,resolve.ts: unchanged.index.ts: the module's exports.
- The rest of
src/engine/: Part I, unchanged. src/content/hazards.ts(new) andsrc/content/index.ts: the hazards, in the order the engine runs them:SINK,HOT/MELT,DEFEAT. What destroys what at the end of a turn is content, not engine.src/content/movement.ts: unchanged.src/content/words.json: Module 9 addsHOT,MELT,SINK,DEFEATandFLOAT, andALL, the Module 7 Expansion Drop, which the Module 9 tests need.src/harness.ts: the adapter the course tests call. Module 9 addsstep,wonandlostonBaba.
Run it with the module's tests: node tools/verify-references.mjs 09 from the course repository.