feat: robustness in Module 5 (nesting guard, Death Phase limit, well-formed Assemble, multi-summon order)
A trigger is never queued while that same (minion, trigger) is resolving;
more than DEATH_PHASE_LIMIT = 64 Death Phases is an error ('Death Phase
limit'); Assemble refuses and never offers ill-formed text (wellFormed /
well_formed); summon count > 1 places tokens in summon order. Modules 05-10,
both tracks. Four new Module 5 tests, a tools test that every shipped card
is well-formed, lessons 2, 3, 5, 13 and harness.md updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
372c99c306
commit
21fb40c6f5
81 files changed
+1880
-528
No files matched your search
@@ -345,16 +345,16 @@ Optional. Each question pushes the design past what this module's tests check. P
|
||||
|
||||
<details><summary>Discussion</summary>
|
||||
|
||||
It never stops on its own. The Bomb's trigger damages the Bomb, which queues the Bomb's trigger, inside itself. The reference engine at this module recurses until the language runtime gives up: in the TypeScript track that is a `RangeError` after about two thousand log events, with the Bomb at −936 Health. A stack overflow in the middle of a Sequence also leaves the world half-changed, which is worse than the crash.
|
||||
It never stops on its own. The Bomb's trigger damages the Bomb, which queues the Bomb's trigger, inside itself. The reference engine at this module recurses until the language runtime gives up: in the TypeScript track that is a `RangeError` after about two thousand log events, with the Bomb at −936 Health. The references of Modules 3 and 4 recurse the same way. A stack overflow in the middle of a Sequence also leaves the world half-changed, which is worse than the crash.
|
||||
|
||||
Depth-first resolution by plain recursion is the right model and it has this cost: nothing bounds the depth. Two real games answer in two different ways.
|
||||
|
||||
- **Forbid it by rule.** The Advanced rulebook: "A trigger cannot be nested inside itself" ([Advanced rulebook](https://hearthstone.wiki.gg/wiki/Advanced_rulebook)). In engine terms: keep the set of (source, trigger) pairs that are resolving right now, and don't queue an entry that is already in it. Try it. With the guard, the Bomb damages everything once and the Sequence ends: its trigger is on the stack when its own damage lands, so it isn't queued again.
|
||||
- **Allow it and name the outcome.** Magic: The Gathering has no such guard. Rule 104.4b: "If a game ... somehow enters a 'loop' of mandatory actions, repeating a sequence of events with no way to stop, the game is a draw." ([Comprehensive Rules](https://magic.wizards.com/en/rules)). An engine for that game has to *detect* the loop, which is harder than preventing it.
|
||||
|
||||
Either way the decision belongs in the engine's one resolution loop, and it is a rule of the game, not an implementation detail: two Bombs under the guard still hit each other once each, and a player can see that.
|
||||
Either way the decision belongs in the engine's one resolution loop, and it is a rule of the game, not an implementation detail: two Bombs under the guard still set each other off, six activations in all, and a player can see that. Work out the six before you check them in Module 5's scenario.
|
||||
|
||||
The guard is not part of the course's tests, and the reference doesn't have it; [Module 3](../03-death-step/lesson.md) says why it can wait. If you add it, add a test with it.
|
||||
The guard is not part of this module's tests, and the references of Modules 2 to 4 don't have it: a one-off card like the Bomb can't be written in a test until cards are data. It arrives in [Module 5](../05-assemble/lesson.md#the-harness), where `test_05_trigger_does_not_nest_in_itself` registers two Bombs and checks every activation; from Module 5 on, every reference has it. If you add it now, add a test with it.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
Reference in new issue
Block a user