Waves spend their force as they travel (rev 24); bots wield Destroy Wall
Rules rev 24: a waterwall wave that finds a victim dist cells from its source has only range-dist spaces of push left — a range-2 wave throws its adjacent victim two spaces but a victim at its far edge only one, and spent force never converts into crush damage. Applies to WATERWALL and both STONE TO WATER waves; older revisions keep the flat full-range wash so stored games replay unchanged. Automatons now use DESTROY WALL on the march: two BFS distance maps (from the bot, from its objectives) price every visible wall by the shortcut its removal opens; the bot blasts when it saves 4+ steps of walking — or when no road exists at all — never standing beside the blast unless trapped and healthy. The card also leaves the shed pile: discardValue 2 -> 6. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138A8CjeQRpvzKxuMfz1Bqc
This commit is contained in:
co-authored by
Claude Fable 5
parent
dfd7a2ff56
commit
2d53ba4bc0
@@ -5,6 +5,7 @@ import {
|
||||
type GameState,
|
||||
type PlayerId,
|
||||
} from "../src/game";
|
||||
import { edgeKey } from "../src/board";
|
||||
import { viewFor } from "../src/view";
|
||||
import { automatonCommand, automatonFallback, type AutomatonStyle, type AutomatonTier } from "../src/automaton";
|
||||
|
||||
@@ -214,3 +215,48 @@ describe("the clockwork honors absorb's fine print", () => {
|
||||
expect(cmd).toEqual({ type: "counteract", instanceId: "blunt#T" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("the clockwork wields destroy wall", () => {
|
||||
it("blasts its way out when no road leads to the gold", () => {
|
||||
let { state } = createGame({ playerIds: ["bot", "other"], seed: 42, sets: ["basic"], deckRev: 24 });
|
||||
while (actingSeat(state) !== "bot") {
|
||||
const r = applyCommand(state, actingSeat(state), { type: "endTurn", draw: 0 });
|
||||
if (!r.ok) throw new Error(r.error);
|
||||
state = r.state;
|
||||
}
|
||||
const bot = state.players.find((p) => p.id === "bot")!;
|
||||
// Brick the bot into a one-square cell far from everything.
|
||||
bot.position = { x: 4, y: 4 };
|
||||
for (const side of ["N", "S", "E", "W"] as const) {
|
||||
state.edgeOverrides[edgeKey(bot.position, side)] = "wall";
|
||||
}
|
||||
state.players.find((p) => p.id === "other")!.position = { x: 0, y: 0 };
|
||||
bot.hand[0] = { instanceId: "destroy-wall#T", cardId: "destroy-wall" };
|
||||
const cmd = automatonCommand(viewFor(state, "bot"), "hunter", "archmage");
|
||||
expect(cmd).toMatchObject({ type: "cast", instanceId: "destroy-wall#T" });
|
||||
// And the engine accepts the blast it chose.
|
||||
const r = applyCommand(state, "bot", cmd!);
|
||||
expect(r.ok).toBe(true);
|
||||
});
|
||||
|
||||
it("values destroy wall above the chaff when forced to discard", () => {
|
||||
let { state } = createGame({ playerIds: ["bot", "other"], seed: 42, sets: ["basic"], deckRev: 24 });
|
||||
const bot = state.players.find((p) => p.id === "bot")!;
|
||||
bot.hand = [
|
||||
{ instanceId: "destroy-wall#T", cardId: "destroy-wall" },
|
||||
{ instanceId: "buddy#T", cardId: "buddy" },
|
||||
{ instanceId: "ugly#T", cardId: "ugly" },
|
||||
{ instanceId: "fear#T", cardId: "fear" },
|
||||
{ instanceId: "full-shield#T", cardId: "full-shield" },
|
||||
{ instanceId: "fireball#T", cardId: "fireball" },
|
||||
{ instanceId: "number-3#T", cardId: "number-3" },
|
||||
{ instanceId: "troll#T", cardId: "troll" },
|
||||
];
|
||||
state.pendingDiscard = "bot";
|
||||
const cmd = automatonCommand(viewFor(state, "bot"), "hunter", "archmage");
|
||||
expect(cmd?.type).toBe("discard");
|
||||
if (cmd?.type === "discard") {
|
||||
expect(cmd.instanceIds).not.toContain("destroy-wall#T");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user