Rev 22: the self-stack resolves — one wizard cannot bounce to himself
A democratic monster touching its own creator opens a stack where one player wears both hats, and doPass matched the attacker first: every pass "bounced the exchange to the defender" — the same wizard — forever. Room 928D livelocked there at seq 83; once the boot kick revived its pump, the automaton politely passed 389 times. At rev 22 a self-stack pass RESOLVES (the claw lands, the game moves on); older ledgers hold their recorded bounces and replay so. 928D itself was repaired by truncating the no-op run and lifting it to rev 22, verified clean and unstuck on a copy before installation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
4a75370de5
commit
7372bfc5e8
@@ -5071,7 +5071,13 @@ function doPass(prev: GameState, playerId: PlayerId): CommandResult {
|
||||
const state = clone(prev);
|
||||
const stack = state.stack!;
|
||||
const events: GameEvent[] = [];
|
||||
if (playerId === stack.attackerId) {
|
||||
// A self-stack (a democratic monster touching its own creator) has one
|
||||
// party wearing both hats: their pass RESOLVES — bouncing it "to the
|
||||
// defender" would hand the exchange back to themselves forever
|
||||
// (rules rev 22; older ledgers hold those no-op bounces and replay so).
|
||||
const selfStack =
|
||||
stack.attackerId === stack.defenderId && (state.config.deckRev ?? 1) >= 22;
|
||||
if (playerId === stack.attackerId && !selfStack) {
|
||||
// The attacker declining to answer bounces the exchange back so the
|
||||
// defender may stack further counters — but not past a total stop:
|
||||
// re-prompting then reads as "your shield failed" and goads the table
|
||||
|
||||
@@ -683,3 +683,40 @@ describe("collapsing walls crush monsters too (rules rev 21)", () => {
|
||||
expect(state.creatures.find((c) => c.id === "tr1")!.damage).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("the self-stack resolves on a pass (rules rev 22)", () => {
|
||||
function selfTouch(deckRev: number) {
|
||||
let { state } = createGame({ playerIds: ["a", "b"], seed: 42, sets: ["basic", "expansion1"], deckRev });
|
||||
state = toRound2(state);
|
||||
const creator = activePlayer(state);
|
||||
state.creatures.push({
|
||||
id: "dm1", kind: "democratic-monster", controllerId: creator.id,
|
||||
position: { x: creator.position.x - 1, y: creator.position.y },
|
||||
damage: 0, maxDamage: 5, movesPerTurn: 3, movementUsed: 0,
|
||||
attackUsed: false, justCreated: false,
|
||||
wallPassesPerTurn: 0, wallPassUsed: 0, scorchedThisTurn: [],
|
||||
});
|
||||
const r = applyCommand(state, creator.id, { type: "moveCreature", creatureId: "dm1", direction: "E" });
|
||||
if (!r.ok) throw new Error(r.error);
|
||||
return { state: r.state, creator: creator.id };
|
||||
}
|
||||
|
||||
it("rev 22: passing your own monster's touch takes the claw and moves on", () => {
|
||||
const { state, creator } = selfTouch(22);
|
||||
if (!state.stack) return; // a wall between: the touch never opened
|
||||
expect(state.stack.attackerId).toBe(creator);
|
||||
expect(state.stack.defenderId).toBe(creator);
|
||||
const r = applyCommand(state, creator, { type: "pass" });
|
||||
if (!r.ok) throw new Error(r.error);
|
||||
expect(r.state.stack).toBeNull();
|
||||
expect(r.state.players.find((p) => p.id === creator)!.life).toBe(13);
|
||||
});
|
||||
|
||||
it("rev 21 keeps its recorded bounce, no-op as it was", () => {
|
||||
const { state, creator } = selfTouch(21);
|
||||
if (!state.stack) return;
|
||||
const r = applyCommand(state, creator, { type: "pass" });
|
||||
if (!r.ok) throw new Error(r.error);
|
||||
expect(r.state.stack).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -54,7 +54,7 @@ export interface Room {
|
||||
const rooms = new Map<string, Room>();
|
||||
|
||||
/** Rules revision new games are dealt under (stored games keep their own). */
|
||||
const RULES_REV = 21;
|
||||
const RULES_REV = 22;
|
||||
|
||||
const ROOM_CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ class LocalGame {
|
||||
seed,
|
||||
sets: expansion ? ["basic", "expansion1"] : ["basic"],
|
||||
...(colors ? { colors } : {}),
|
||||
deckRev: 21,
|
||||
deckRev: 22,
|
||||
};
|
||||
const { state, events } = createGame(config);
|
||||
for (const e of events) {
|
||||
|
||||
Reference in New Issue
Block a user