Rev 14: a pit's rim is a ledge, and the walker chooses the way off (GDCN)

The table's reading of CREATE PIT: a thin ledge runs around the rim,
and "jumping over" is edging along it, trying not to fall. So on a 2,
3, or 4 the walker lands on an open square beside the pit — the only
one if there is one, otherwise the one they name (a click on that
square sends `exit` with the move) — and a pit with no way off cannot
be entered from there at all, refused before any die is rolled. The
FAQ's diagonal crossing at an intersection is this rule.

Older games bounced the walker back and charged the stride; they keep
that under their frozen revision, so GDCN's seven bounces replay. The
clockwork's path search treats a pit as a road when any side of its
rim is open and names the exit its path leaves by. Tests pin the fork,
the single way off, the closed pit under both revisions, and the
brain's route.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
Eric Wagoner
2026-09-04 16:18:42 -04:00
co-authored by Claude Fable 5.1
parent 0750fe3f21
commit c6913ea1bc
4 changed files with 161 additions and 21 deletions
+23 -8
View File
@@ -155,6 +155,13 @@ interface PathResult {
distance: number;
/** A locked door stands on the first step of this path. */
doorAhead?: { cell: Cell; side: Side };
/** The first step lands on a pit: the side of its rim the path leaves by. */
exit?: Side;
}
/** The move that takes a path's first step, naming the pit exit when there is one. */
function stepAlong(path: PathResult): Command {
return { type: "move", direction: path.dir, ...(path.exit ? { exit: path.exit } : {}) };
}
/** One walkable step for the clockwork's pathfinding; null = impassable.
@@ -582,13 +589,17 @@ function wallBlastTarget(
* when the clockwork can unlock them; the first such door is reported so the
* key gets used before the boot.
*/
/** Can a wizard entering the pit at `pit` heading `dir` land beyond it —
* the same test the maze applies: a cell there, no wall between, no stone. */
/** 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. */
function pitLandable(view: GameView, pit: Cell, dir: Side): boolean {
const beyond = neighbor(pit, dir);
return !!view.board.cells[cellKey(beyond)] &&
(view.board.edges[edgeKey(pit, dir)] ?? "open") === "open" &&
view.squareContents[cellKey(beyond)]?.kind !== "stone";
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";
});
}
export function pathToward(
@@ -642,15 +653,19 @@ export function pathToward(
}
if (!found) return null;
let cursor = found;
let second: Side | undefined;
for (;;) {
const hop = cameBy.get(cursor)!;
if (hop.prev === cellKey(from)) {
const firstIsPit = view.squareContents[cursor]?.kind === "pit";
return {
dir: hop.dir,
distance: depth,
...(hop.viaDoor ? { doorAhead: { cell: from, side: hop.dir } } : {}),
...(firstIsPit && second ? { exit: second } : {}),
};
}
second = hop.dir;
cursor = hop.prev;
}
}
@@ -1728,7 +1743,7 @@ export function automatonCommand(
return { type: "move", direction: dir };
}
const approach = pathToward(view, self.position, new Set([cellKey(crossing.near)]), { canUnlock });
if (approach) return { type: "move", direction: approach.dir };
if (approach) return stepAlong(approach);
}
}
// Or bank one now, when a crossing beats the walk by enough to spend a card.
@@ -1839,7 +1854,7 @@ export function automatonCommand(
if (run) return { type: "cast", instanceId: run.instanceId, params: { points } };
}
}
return { type: "move", direction: path.dir };
return stepAlong(path);
}
}
}