Files
systemic-game-design-course/course/12-combat/expansion/README.md
T
Lucas WintherandClaude Opus 5.5 fb9d0a4eb4 feat: Module 12, Combat (attack as one Sequence, Taunt, defender-first as a queue priority, end of turn; Bonechewer Brawler as the Expansion Drop)
Adds course/12-combat: lesson, scenarios, nine test_12 tests in both tracks,
the cumulative reference (Module 11's plus combat) and the drop. The Damage
action carries per-target hits with a priority; react stable-sorts its Queue by
priority before order of play, so the triggers answering the defender's Damage
Event go first. orderAttack/canAttack/endTurn join the harness (orderAttack
because attack(id) already reads Attack). endOfTurn triggers fire, so Drakkari
Enchanter's data is live. Test Bell is scoped to Module 12's cards. Harness
contract, catalog, research notes and earlier lessons' forward pointers follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 13:34:20 +01:00

2.0 KiB

Module 12 Expansion Drop: Bonechewer Brawler

The lesson's Expansion Drop beat explains the task. This file holds the scenario, the test locations and the check.

  • Card: Bonechewer Brawler, 2-mana 2/3 Neutral minion, "Taunt. Whenever this minion takes damage, gain +2 Attack." (Bonechewer Brawler, hearthstone.wiki.gg).
  • Tests: tests/rust/test_12x.rs and tests/ts/test_12x.test.ts. Copy the one for your track into your workspace's tests/.
  • Reference answer: solution/cards/bonechewer-brawler.json, one card file for both tracks. Copy it into your workspace's cards/. Nothing under src/engine/ changes, and neither does the harness. It is not in course/cards/, so start-module doesn't hand it to you.
  • Check: commit your workspace before you start, then run node tools/expansion-check.mjs <your-workspace> <that-commit> from the course repository. It fails if any file under src/engine/ changed.

Every clause of the card is a word the course already has: the keyword taunt (Unstable Ghoul has it), the trigger selfDamaged (Acolyte of Pain) and the effect gainAttack on self (Frothing Berserker). Before this module the engine kept taunt and did nothing with it. Now combat reads it.

Scenario

test_12x_bonechewer_brawler

  • Setup: newGame with a deck of 5 Test Filler for each player. Summon Bonechewer Brawler for player 1 (id W), then Loot Hoarder for player 1 (id H), then Harvest Golem for player 0 (id A). Call clearLog().
  • Assertions 1: canAttack(A, H) is a string containing Taunt: the Brawler protects the Hoarder.
  • Action: orderAttack(A, W).
  • Assertions 2:
    • The log is exactly, in this order: Damaged{target: W, amount: 2}, Damaged{target: A, amount: 2}, Triggered{source: W, trigger: "damaged"}, StatsChanged{id: W, attack: 4, health: 1}.
    • health(A) is 1: the Golem took 2, the Brawler's Attack when the damage was dealt. The +2 Attack comes from a trigger, after the simultaneous damage.
    • attack(W) is 4.