Two-player decks shed LIFESAVER, as the card itself instructs
"Not applicable in a 2-player game." — the card face. New games deal from deck revision 2, which removes it when exactly two wizards sit down; three or more keep it. The revision travels in GameConfig and the persisted start line, and stored games without one replay against the original build — changing an existing game's deck composition would scramble its deterministic replay. Test pins all three cases: removed at two, present at three, present in legacy two-player games. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
26c4b6efdf
commit
efec82e88d
@@ -200,6 +200,12 @@ export interface GameConfig {
|
||||
* standees: green, red, magenta, blue, light blue, yellow). Defaults to
|
||||
* seat order. */
|
||||
colors?: number[];
|
||||
/**
|
||||
* Deck revision. Absent = the original build, which stored games replay
|
||||
* against forever. Revision 2 removes LIFESAVER from two-player decks
|
||||
* ("Not applicable in a 2-player game." — the card face).
|
||||
*/
|
||||
deckRev?: number;
|
||||
}
|
||||
|
||||
export interface GameState {
|
||||
@@ -2895,7 +2901,11 @@ export function createGame(config: GameConfig): { state: GameState; events: Game
|
||||
})),
|
||||
);
|
||||
|
||||
const [deckShuffled, rng3] = shuffle(rng, buildDeck(config.sets));
|
||||
let fullDeck = buildDeck(config.sets);
|
||||
if ((config.deckRev ?? 1) >= 2 && n === 2) {
|
||||
fullDeck = fullDeck.filter((c) => c.cardId !== "lifesaver");
|
||||
}
|
||||
const [deckShuffled, rng3] = shuffle(rng, fullDeck);
|
||||
rng = rng3;
|
||||
const deck = [...deckShuffled];
|
||||
const discard: CardInstance[] = [];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { applyCommand, activePlayer, boardView } from "../src/game";
|
||||
import { applyCommand, activePlayer, boardView, createGame } from "../src/game";
|
||||
import { cellKey, edgeKey, hasLineOfSight, neighbor, type Side } from "../src/board";
|
||||
import type { CardInstance } from "../src/cards";
|
||||
import { newGame, must, giveCard, toRound2, faceOff } from "./helpers";
|
||||
@@ -314,3 +314,17 @@ describe("stored-log compatibility", () => {
|
||||
expect(state.players.find((p) => p.id === defender)!.life).toBe(15 - 5); // fireball: 2 + the 3
|
||||
});
|
||||
});
|
||||
|
||||
describe("deck revisions", () => {
|
||||
it("revision 2 removes LIFESAVER at two players, and only there", () => {
|
||||
const two = createGame({ playerIds: ["a", "b"], seed: 9, sets: ["basic", "expansion1"], deckRev: 2 });
|
||||
const inGame = (s: typeof two.state) =>
|
||||
[...s.deck, ...s.discard, ...s.players.flatMap((p) => p.hand)].some((c) => c.cardId === "lifesaver");
|
||||
expect(inGame(two.state)).toBe(false);
|
||||
// Three players keep it; old two-player games (no deckRev) keep it too.
|
||||
const three = createGame({ playerIds: ["a", "b", "c"], seed: 9, sets: ["basic", "expansion1"], deckRev: 2 });
|
||||
expect(inGame(three.state)).toBe(true);
|
||||
const legacy = createGame({ playerIds: ["a", "b"], seed: 9, sets: ["basic", "expansion1"] });
|
||||
expect(inGame(legacy.state)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user