Rev 18: the democratic monster's claw outlives the first wizard

Its one-attack-per-round refreshed inside beginTurnFor when the turn
index matched the roll-off winner's seat — and only the living begin
turns, so once that wizard fell the claw never refreshed again: one
attack, then a lifetime of harmless bumping. The refresh now lives at
the true round boundary in the turn-advance loop, which passes dead
seats without skipping the count. Earlier ledgers keep their gummed
monster and replay so.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-17 16:51:44 -04:00
co-authored by Claude Fable 5
parent d89d995daf
commit 3b0aa01867
4 changed files with 43 additions and 5 deletions
+27
View File
@@ -589,3 +589,30 @@ describe("the wave carries monsters (rules rev 17)", () => {
throw new Error("setup: no horizontal wall with a floor below");
});
});
describe("the democratic monster's claw survives the first wizard's death (rules rev 18)", () => {
it("refreshes each round even with the roll-off winner dead", () => {
let { state } = createGame({ playerIds: ["a", "b", "c"], seed: 42, sets: ["basic", "expansion1"], deckRev: 18 });
state = toRound2(state);
const firstId = state.players[state.turn.firstIndex]!.id;
const dm = {
id: "dm1", kind: "democratic-monster" as const, controllerId: state.players[0]!.id,
position: { x: 0, y: 0 }, damage: 0, maxDamage: 5, movesPerTurn: 3,
movementUsed: 0, attackUsed: true, justCreated: false,
wallPassesPerTurn: 0, wallPassUsed: 0, scorchedThisTurn: [] as string[],
};
state.creatures.push(dm);
// The roll-off winner falls.
const first = state.players.find((p) => p.id === firstId)!;
first.alive = false;
first.finalHand = [...first.hand];
// Walk a full round of the survivors: the claw must refresh.
for (let i = 0; i < 4 && state.creatures[0]!.attackUsed; i++) {
const active = activePlayer(state);
const r = applyCommand(state, active.id, { type: "endTurn", draw: 0 });
if (!r.ok) throw new Error(r.error);
state = r.state;
}
expect(state.creatures[0]!.attackUsed).toBe(false);
});
});