Rev 26: BUTT-HEAD's ram is priced into the blow, so STRENGTH, AMPLIFY, and the counters weigh it (KWUZ)

Ramses cast STRENGTH, then an amplified BUTT-HEAD three squares off, and
the goat dealt three: the ram was applied in onResolved, past the pipe
that doubles, soaks, and adds. Older games keep the bare distance.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
Eric Wagoner
2026-09-23 08:16:30 -04:00
co-authored by Claude Fable 5.1
parent 5193b12b8e
commit 31bfcf7bc0
2 changed files with 61 additions and 2 deletions
@@ -895,3 +895,47 @@ describe("cards that keep their whole promise (rev 21)", () => {
}
});
});
describe("the goat's ram is a physical blow like any other (rev 26)", () => {
/** A goat and its mark a charge apart, the goat holding STRENGTH, AMPLIFY, and the ram. */
function charged(deckRev?: number) {
let { state } = createGame({ playerIds: ["goat", "mark"], seed: 42, sets: ["basic", "expansion1"], ...(deckRev ? { deckRev } : {}) });
state = toRound2(state);
const goat = activePlayer(state);
const mark = state.players.find((p) => p.id !== goat.id)!;
const bh = giveCard(state, goat.id, "butt-head");
giveCard(state, goat.id, "amplify", "A", 1);
giveCard(state, goat.id, "strength", "S", 2);
giveCard(state, goat.id, "number-2", "N", 3);
state = must(state, goat.id, { type: "cast", instanceId: "strength#S", numberInstanceIds: ["number-2#N"] });
// Any square the charge reaches at two or more steps will do.
for (const key of Object.keys(boardView(state).cells)) {
if (key === cellKey(goat.position)) continue;
const [x, y] = key.split(",").map(Number) as [number, number];
const trial = structuredClone(state);
trial.players.find((p) => p.id === mark.id)!.position = { x, y };
const r = applyCommand(trial, goat.id, {
type: "cast", instanceId: bh.instanceId, target: { kind: "player", playerId: mark.id }, amplifyInstanceIds: ["amplify#A"],
});
if (!r.ok) continue;
const done = applyCommand(r.state, mark.id, { type: "pass" });
if (!done.ok) continue;
const ram = done.events.find((e) => e.type === "rammed");
if (!ram || ram.type !== "rammed" || ram.distance < 2) continue;
return { events: done.events, distance: ram.distance, state: done.state, goat: goat.id, mark: mark.id };
}
throw new Error("no square a charge of two or more reaches on this board");
}
it("STRENGTH and AMPLIFY each double the ram", () => {
const { events, distance, state, mark } = charged();
expect(events).toContainEqual(expect.objectContaining({ type: "damaged", player: mark, amount: distance * 4 }));
expect(state.players.find((p) => p.id === mark)!.life).toBe(15 - distance * 4);
});
it("older games dealt the bare distance, whatever rode the cast", () => {
const { events, distance, state, mark } = charged(25);
expect(events).toContainEqual(expect.objectContaining({ type: "damaged", player: mark, amount: distance }));
expect(state.players.find((p) => p.id === mark)!.life).toBe(15 - distance);
});
});