- 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>
54 lines
3.5 KiB
Markdown
54 lines
3.5 KiB
Markdown
# Systemic Design Course
|
|
|
|
A free, hands-on course on **systemic game design**. You learn by rebuilding the rules machinery of two games, then extending it:
|
|
|
|
- **Part I: Hearthstone** (cards, zones, triggers, death phases, auras, text assembly)
|
|
- **Part II: Baba Is You** (rules as pushable text, parsing, movement, win/lose)
|
|
- **Part III: SpellGrid**, a small original game that combines both systems
|
|
- **Module 13: Break your own engine**, an optional closing module on testing what the tests miss: mutation testing, invariants, metamorphic relations, undo and replay, and a level solver
|
|
|
|
Every module follows the same beats: Observe, Predict, Spec, Build, Name it, Expansion Drop, Stress it, Further reading. The Expansion Drop is the test of your design: you add new content without touching the engine. Stress it is optional: edge cases and stretch exercises that push the design past what the module's tests check.
|
|
|
|
## Two paths
|
|
|
|
- **Designer path**: read the lessons and scenarios, predict outcomes, and reason about the design.
|
|
- **Builder path**: also make the tests pass in your own engine.
|
|
|
|
## Mentor path
|
|
|
|
Prefer a Socratic partner? Install the [mentor skill](mentor/README.md) and let your AI agent guide you without writing your code.
|
|
|
|
## Two tracks
|
|
|
|
Builder path exercises exist in **Rust** and **TypeScript**. Both tracks have identical test names, matching the scenarios in each module's `scenarios.md`.
|
|
|
|
## Quick start
|
|
|
|
From the course repository:
|
|
|
|
```sh
|
|
node --version # Node 22 or later for the course tooling (CI uses 24)
|
|
npm run test:tools # tooling tests
|
|
npm run check # test names agree across Rust, TS and scenarios
|
|
```
|
|
|
|
Create your workspace from the template for your track (Module 00, "Before Module 1: choose your track", has the details). Copy the whole directory with `cp -r` so the hidden `.course.json` comes along:
|
|
|
|
```sh
|
|
cp -r workspace-template/rust ~/my-engine # or workspace-template/ts
|
|
cd ~/my-engine && git init && git add -A && git commit -m "start from template" && cd -
|
|
node tools/start-module.mjs 01 ~/my-engine
|
|
```
|
|
|
|
`start-module` copies a module's tests for your track (from your workspace's `.course.json`) and refuses to start a module if you have not yet taken the previous one. Module 00 uses the `naive-engine/` project and is not started this way, and Module 13 has no tests to start.
|
|
|
|
Other tools: `tools/coupling-audit.mjs <workspace>` (engine code must not name specific cards or nouns), `tools/expansion-check.mjs <workspace> <baseRef>` (an Expansion Drop must change content only), `tools/verify-references.mjs [NN]` (reference solutions pass the tests; `--expansion` also checks every Expansion Drop solution), `tools/play-spellgrid.mjs <rust|ts> [l1|l2|l3]` (play the Module 10 capstone, SpellGrid, on the command line with the reference engine), `tools/solve-spellgrid.mjs [l1|l2|l3|level.json]` (search a SpellGrid level for its shortest solution by brute force; `--never-rule "X IS Y"` asks whether it can be solved without a given rule; see Module 13), `tools/diff-tracks.mjs [--seeds N] [--seed S] [--module NN] [--dir D] [--keep]` (differential fuzzer: plays random SpellGrid levels and scripts on the Rust and the TypeScript reference CLIs and fails on the first seed where they print different text or one of them crashes; `npm run diff:tracks`).
|
|
|
|
## Licence
|
|
|
|
MIT, for both prose and code. See [LICENSE](LICENSE). The course is and stays free.
|
|
|
|
## Disclaimer
|
|
|
|
Not affiliated with Blizzard or Hempuli. See the [disclaimer](site/src/content/docs/disclaimer.md) (also the Disclaimer page of the course website).
|