SpellGrid capstone: Part I's card engine and Part II's grid engine joined at three seams (rules -> aura data, hazard victims -> one destroy Sequence, Sequence end -> write text, remove the dead, reparse). Three levels with scripted solutions, a naive-timing test via a harness-only hook that reverses the END_OF_PHASE list, CLIs in both tracks, the BATTLECRY IS DOUBLE Expansion Drop, SpellGrid cards and words in course/cards and the catalog, level schema cards/hand, and tools staging/copying of module levels. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Module 10 reference engine (TypeScript)
The course's own engine as of the end of Module 10: Part I's card engine and Part II's grid engine, joined by SpellGrid. Every test from Modules 1–9 still passes. Module 9's reference was the starting point, so a diff between the two shows what this module adds.
cards/andlevels/(at the package root; not in this directory): copies ofcourse/cards/andcourse/10-spellgrid/levels/, staged bytools/verify-references.mjs.src/engine/spellgrid.ts(new): the only file that imports both engines.SpellGridkeeps aWorldand apuzzle.Levelin step through the three seams:ruleAuras(rules in force → auras), the destroy function (hazard victims → onedestroySequence → the Death Creation Step), andsequence(after each Sequence: writeTextSpawnedtext, take dead minions off the grid, reparse). It also keeps one id space: a minion's id is its grid object's id.- Part I (
src/engine/, outsidepuzzle/):queue.ts:END_OF_PHASE(["deathCreation", "auraUpdate"]) is the list of steps at the end of the outermost Phase, read from the world. ThespawnTextaction records aTextSpawnedevent.world.ts:setRuleAuras/ruleAuras(auras that no minion carries),setEndOfPhase/endOfPhase,entityCount.aura.ts: an Aura Update adds the rule auras for both players;activationsapplies atriggerCap.card.ts: thetriggerCapaura (not in the card format: only SpellGrid makes it).effect.ts:spawnTextis an action, not an error.event.ts:TextSpawned.
- Part II (
src/engine/puzzle/):level.ts:DestroyandremoveAll.stepandmoveYoutake an optionalDestroy, which movement, conversion (X IS NOT X) and hazards hand their victims to.reparseis public.grid.ts:Board.reserveId, andcards/handinLevelDef(read only by SpellGrid).
src/content/seams.jsonandseams()(new): which nouns name a trigger (DEATHRATTLE) and which properties multiply one (DOUBLE, twice,stacking: "max").words.jsonadds the SpellGrid nouns,DEATHRATTLEandDOUBLE.index.tsnow readscards/withnode:fs, so the command line, which runs without Vite, reads the same cards.src/harness.ts: Module 10 addsnewSpellGridand theSpellGridinterface, including the test hookwithNaiveAuraTiming(), which reversesEND_OF_PHASEfor one game.src/cli.ts(new): the command line,npx tsx src/cli.ts levels/l1-double-tap.jsonfrom a staged workspace (it needscards/andlevels/).w a s dmove,c <card> [x y]casts a spell (at the minion on that tile) or plays a minion (onto that empty tile),qquits.tests/cli.test.tsplays it from a script; it is the reference's own check, not a course test.
Run it with the module's tests: node tools/verify-references.mjs 10 from the course repository.