Rev 17: a warp mouth on a pit's rim is a way off it (M4Q7)
Kestrel stood beside a pit dug against the board's west edge, walls north and south of it, and was told there was nothing to land on — the rim check counted only neighbouring squares, and the warp mouth beyond the pit is not one. From rev 17 a mouth on the rim is an exit like any square, named at a fork like any other; every older game takes the mouth when no square offers, so the refused crossing goes through as it stands. The jump through a mouth carries `via: "warp"`: the board shimmers both mouths instead of streaking across the maze, and the reel cuts rather than gliding. The automaton leaves a pit only by its exits. Three tests walk the rim under both readings. 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
dc4d2a1221
commit
461c1a5c83
@@ -592,14 +592,22 @@ function wallBlastTarget(
|
||||
/** Can a wizard entering the pit at `pit` heading `dir` get off its rim
|
||||
* — the maze's own test: some side other than the way back with a cell
|
||||
* there, no wall between, and no stone on it. */
|
||||
/** The sides a walker may leave a pit by, having entered it heading
|
||||
* `dir`: neighbouring squares first; a warp mouth on the rim only when
|
||||
* no square offers, the reading every revision accepts. */
|
||||
function pitExits(view: GameView, pit: Cell, dir: Side): Side[] {
|
||||
const footing = (d: Side) => {
|
||||
if (d === opposite(dir)) return null;
|
||||
const t = stepTarget(view.board, pit, d);
|
||||
if (t.kind === "blocked" || view.squareContents[cellKey(t.to)]?.kind === "stone") return null;
|
||||
return t.kind;
|
||||
};
|
||||
const floor = SIDES.filter((d) => footing(d) === "step");
|
||||
return floor.length > 0 ? floor : SIDES.filter((d) => footing(d) === "warp");
|
||||
}
|
||||
|
||||
function pitLandable(view: GameView, pit: Cell, dir: Side): boolean {
|
||||
return SIDES.some((d) => {
|
||||
if (d === opposite(dir)) return false;
|
||||
const beyond = neighbor(pit, d);
|
||||
return !!view.board.cells[cellKey(beyond)] &&
|
||||
(view.board.edges[edgeKey(pit, d)] ?? "open") === "open" &&
|
||||
view.squareContents[cellKey(beyond)]?.kind !== "stone";
|
||||
});
|
||||
return pitExits(view, pit, dir).length > 0;
|
||||
}
|
||||
|
||||
export function pathToward(
|
||||
@@ -632,6 +640,11 @@ export function pathToward(
|
||||
// direction; with nothing to land on, the maze bounces the leaper
|
||||
// back and charges the stride. Goal or waypoint, that is no road.
|
||||
if (hazard === "pit" && !pitLandable(view, to, dir)) continue;
|
||||
// Out of a pit only by one of its exits.
|
||||
if (view.squareContents[cellKey(c)]?.kind === "pit") {
|
||||
const entry = cameBy.get(cellKey(c))?.dir;
|
||||
if (entry && !pitExits(view, c, entry).includes(dir)) continue;
|
||||
}
|
||||
seen.add(k);
|
||||
cameBy.set(k, { prev: cellKey(c), dir, viaDoor });
|
||||
if (goals.has(k)) { found = k; break; }
|
||||
|
||||
Reference in New Issue
Block a user