// Strict full-ledger replay: ANY refused command is a hard failure. import { readFileSync } from "fs"; import { createGame, applyCommand } from "../packages/engine/src/game.ts"; const lines = readFileSync(process.argv[2], "utf8").trim().split("\n").map((l) => JSON.parse(l)); const meta = lines.find((l) => l.kind === "meta"); const start = lines.find((l) => l.kind === "start"); if (!start) { console.log(`OK ${process.argv[2].split("/").pop()}: lobby only, nothing to replay`); process.exit(0); } // The lobby roster: joins add seats, kicks remove them, the start freezes it. const roster = new Set([meta.hostId]); for (const l of lines) { if (l.kind === "join") roster.add(l.name); if (l.kind === "kick") roster.delete(l.name); if (l.kind === "start") break; } const playerIds = [...roster]; let { state } = createGame({ playerIds, seed: meta.seed, sets: start.expansion ? ["basic", "expansion1"] : ["basic"], colors: start.colors, deckRev: start.deckRev }); let n = 0; for (const l of lines) { if (l.kind !== "command") continue; const r = applyCommand(state, l.playerId, l.command); if (!r.ok) { console.log(`FAIL ${process.argv[2].split("/").pop()} seq ${l.seq}: ${r.error}`); process.exit(1); } state = r.state; n++; } console.log(`OK ${process.argv[2].split("/").pop()}: ${n} commands replay clean`);