Rev 22: TELEPORT crosses the maze's outer edge anywhere
A teleporter ignores walls, and the outer edge is no more than a wall to it: a line leaving the maze at any square's edge re-enters at the opposite edge on the same row or column, one space on. The lettered openings connect as they always did. Older games crossed the edge only at the openings, and replay so; U3U2, dealt at rev 21 with no teleport yet cast, was re-stamped to 22 at its table's request. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
7a6aa3d2a1
commit
803c51415e
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { applyCommand, activePlayer, boardView, createGame, gameLos, type GameState } from "../src/game";
|
||||
import { applyCommand, activePlayer, boardView, createGame, gameLos, wallIgnoringDistance, type GameState } from "../src/game";
|
||||
import { cellKey, edgeKey, neighbor, opposite, SIDES, stepTarget, type Cell, type Side } from "../src/board";
|
||||
import type { CardInstance } from "../src/cards";
|
||||
import { eligibleCellsFor, sightedCellsFor, viewFor } from "../src/view";
|
||||
@@ -786,3 +786,38 @@ describe("teleport's reach wraps through the board's openings", () => {
|
||||
expect(r.ok).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("teleport across the maze's outer edge (rev 22)", () => {
|
||||
function atTheTopEdge(deckRev?: number) {
|
||||
const config = { playerIds: ["alice", "bob"], seed: 42, sets: ["basic", "expansion1"] as ("basic" | "expansion1")[], ...(deckRev ? { deckRev } : {}) };
|
||||
const state = toRound2(createGame(config).state);
|
||||
const me = activePlayer(state);
|
||||
const view = boardView(state);
|
||||
// A square on the top row whose north side is plain edge, not a lettered mouth.
|
||||
const top = Object.keys(view.cells).map((k) => k.split(",").map(Number) as [number, number])
|
||||
.filter(([x, y]) => y === 0 && !view.warps.some((w) => w.from.cell.x === x && w.from.cell.y === y && w.from.side === "N"))
|
||||
.find(([x]) => !state.squareContents[`${x},0`])!;
|
||||
me.position = { x: top[0], y: 0 };
|
||||
const column = Object.keys(view.cells).map((k) => k.split(",").map(Number) as [number, number]).filter(([x]) => x === top[0]).map(([, y]) => y);
|
||||
const bottom = { x: top[0], y: Math.max(...column) };
|
||||
giveCard(state, me.id, "teleport");
|
||||
return { state, me, bottom };
|
||||
}
|
||||
|
||||
it("re-enters at the bottom of the same column, one space on", () => {
|
||||
const { state, me, bottom } = atTheTopEdge();
|
||||
expect(wallIgnoringDistance(boardView(state), me.position, bottom, true)).toBe(1);
|
||||
expect(eligibleCellsFor(viewFor(state, me.id), "teleport")!.has(cellKey(bottom))).toBe(true);
|
||||
const r = applyCommand(state, me.id, { type: "cast", instanceId: "teleport#T", target: { kind: "cell", cell: bottom } });
|
||||
expect(r.ok).toBe(true);
|
||||
if (r.ok) expect(r.state.players.find((p) => p.id === me.id)!.position).toEqual(bottom);
|
||||
});
|
||||
|
||||
it("older games cross only at the lettered openings", () => {
|
||||
const { state, me, bottom } = atTheTopEdge(21);
|
||||
expect(wallIgnoringDistance(boardView(state), me.position, bottom)).toBeGreaterThan(4);
|
||||
expect(eligibleCellsFor(viewFor(state, me.id), "teleport")!.has(cellKey(bottom))).toBe(false);
|
||||
const r = applyCommand(state, me.id, { type: "cast", instanceId: "teleport#T", target: { kind: "cell", cell: bottom } });
|
||||
expect(r.ok).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user