Rev 7: sight passes only through the aisle warps
The cards settle what the morning's feature got wrong. DIMENSIONAL WARP: "There is no L.O.S. through the warp." The AUTO WARP: "for all purposes, treat the connected board edges as though they are adjacent" — an explicit grant, for edges that physically meet at a corner. The lettered wraparounds get no grant at all: their rule is about walking. So the pattern is that warps carry movement, not sight, unless the text says otherwise — and only the aisle corners say otherwise. Warps now carry an aisle flag (the 3-player AUTO WARP and the 5-player corner arcs); hasWarpLineOfSight honors only those, with the adjacency geometry — diagonals through the corner mouth — unchanged from this morning where it rightfully applies. Games begun before rev 7 keep the wider sight their logged commands were validated against, via a legacy flag boardView stamps from the game's rules revision. Tests pin all three states: lettered mouths see nothing, aisle mouths see the corner fan, legacy games see everything they used to. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
8af8962330
commit
493f3bc567
@@ -27,6 +27,13 @@ export interface Warp {
|
||||
from: { cell: Cell; side: Side };
|
||||
/** ...you arrive at this cell (entering through `side`). */
|
||||
to: { cell: Cell; side: Side };
|
||||
/**
|
||||
* An aisle (AUTO) warp joins two edges that physically meet at a corner:
|
||||
* "for all purposes, treat the connected board edges as though they are
|
||||
* adjacent" — including line of sight. The lettered wraparounds carry
|
||||
* movement only; no rule grants sight across the map.
|
||||
*/
|
||||
aisle?: boolean;
|
||||
}
|
||||
|
||||
export interface AssembledBoard {
|
||||
@@ -38,6 +45,8 @@ export interface AssembledBoard {
|
||||
/** Cells that are part of the map (all cells inside placed sectors). */
|
||||
cells: Record<string, true>;
|
||||
warps: Warp[];
|
||||
/** Pre-rev-7 games saw through every warp; their replays keep that sight. */
|
||||
sightThroughAllWarps?: boolean;
|
||||
/** Per player-sector info, in placement order. */
|
||||
homes: Cell[];
|
||||
treasureSpaces: Cell[][];
|
||||
@@ -119,7 +128,7 @@ export interface AssemblyOptions {
|
||||
* openings pair straight across the map — correct for the 4p square; the
|
||||
* standard 2p column crosses its side openings and must pass pairs.
|
||||
*/
|
||||
warpPairs?: [OpeningRef, OpeningRef][];
|
||||
warpPairs?: ([OpeningRef, OpeningRef] | [OpeningRef, OpeningRef, "aisle"])[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,11 +213,12 @@ export function assembleBoard(
|
||||
};
|
||||
|
||||
if (options.warpPairs) {
|
||||
for (const [a, b] of options.warpPairs) {
|
||||
for (const [a, b, kind] of options.warpPairs) {
|
||||
const ca = openingCell(a.sector, a.side);
|
||||
const cb = openingCell(b.sector, b.side);
|
||||
warps.push({ from: { cell: ca, side: a.side }, to: { cell: cb, side: b.side } });
|
||||
warps.push({ from: { cell: cb, side: b.side }, to: { cell: ca, side: a.side } });
|
||||
const aisle = kind === "aisle" ? { aisle: true as const } : {};
|
||||
warps.push({ from: { cell: ca, side: a.side }, to: { cell: cb, side: b.side }, ...aisle });
|
||||
warps.push({ from: { cell: cb, side: b.side }, to: { cell: ca, side: a.side }, ...aisle });
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < placements.length; i++) {
|
||||
@@ -363,6 +373,7 @@ export function hasWarpLineOfSight(
|
||||
};
|
||||
const EPS = 1e-9;
|
||||
for (const w of board.warps) {
|
||||
if (!w.aisle && !board.sightThroughAllWarps) continue;
|
||||
const mouthA = w.from.cell;
|
||||
const sideA = w.from.side;
|
||||
const mouthB = w.to.cell;
|
||||
|
||||
Reference in New Issue
Block a user