feat: Module 11 becomes 13; diff-tracks fuzzer; align the two reference tracks
- 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>
This commit is contained in:
1 parent
b36f4605cc
commit
06a1b34c5d
32 files changed
+645
-55
No files matched your search
@@ -34,6 +34,12 @@ const canonical = (value: unknown): string =>
|
||||
: v,
|
||||
);
|
||||
const sameText = (a: CardDef["text"], b: CardDef["text"]): boolean => canonical(a) === canonical(b);
|
||||
/** Two definitions the engine can't tell apart (it ignores `fictional` and `game`). */
|
||||
const sameCard = (a: CardDef, b: CardDef): boolean => {
|
||||
const read = (d: CardDef) =>
|
||||
canonical([d.name, d.type, d.cost, d.attack ?? 0, d.health ?? 0, d.noun ?? null, d.text, d.split]);
|
||||
return read(a) === read(b);
|
||||
};
|
||||
|
||||
/**
|
||||
* The base card with `half` of its text replaced by the same half of `donor`.
|
||||
@@ -66,8 +72,8 @@ const changes = (base: CardDef, donor: CardDef, half: Half): boolean => {
|
||||
export function registerAssembled(world: World, def: CardDef): string {
|
||||
const stem = def.name.replace(/\)$/, "");
|
||||
let name = def.name;
|
||||
// Same base, so the same name and stats: only the text can differ.
|
||||
for (let n = 2; world.hasCard(name) && !sameText(world.card(name).def.text, def.text); n++) {
|
||||
// A different card is any difference the engine reads: the text, the split, or a base re-registered since.
|
||||
for (let n = 2; world.hasCard(name) && !sameCard(world.card(name).def, { ...def, name }); n++) {
|
||||
name = `${stem} ${n})`;
|
||||
}
|
||||
world.registerAssembled({ ...def, name });
|
||||
|
||||
Reference in new issue
Block a user