switching paths: two routes to the same square

Builds docs/plans/switching-paths.md. A passing loop can offer two legal
routes between the same two squares, coupling different cars — the engine
only ever found one, an artifact of search order (Reported by Jesse, undo
379). exploreMoves now enumerates every simple route (per-path visited set,
capped at 4000 frontier nodes) and dedupes on outcome — destination, entry
side, and origin-tagged cars — rather than on reaching the square at all.

switch.move gains an optional `via: GridCoord` naming one intermediate
square on the chosen route; absent, it resolves exactly as before, so
every existing save and bot decision replays identically (575/575, then
579/579 with the new tests). Threaded through the label, the action-list
dedupe, the hover highlight (data-route), and the history (trayMoved.via).

Ruling recorded as Gap 14 in docs/rules/open-questions.md: the player may
choose the path; §A.4's "may not go around" a car does not reach a
different track the player declined to enter.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAt2YCXgd5qCjBcF2x34aK
This commit is contained in:
Jesse
2026-08-19 16:52:10 -04:00
co-authored by Claude Sonnet 5
parent 7932bb6ccf
commit fbaa3d4147
14 changed files with 440 additions and 36 deletions
+26
View File
@@ -28,6 +28,7 @@ left open, with the consequence noted.
| 11 | Track card orientation | **SUPERSEDED** — handedness is printed; both hands supplied |
| 12 | Balance targets | **INVALIDATED** — measured against a placeholder ruleset |
| 13 | Deck scaling by player count | **LARGELY DISSOLVED** — track is per-player by design |
| 14 | Two routes to the same square | **DECIDED** |
---
@@ -1003,3 +1004,28 @@ needs to get started, while giving each player material to build with.
**Not yet decided**, because it interacts with Gap 12's variance question: more facilities per player
would raise the ceiling on the runaway mode as well as the floor.
## Gap 14 — Two routes to the same square
**Status:** DECIDED.
**Reported by Jesse:** "There are times when a train can take two different paths to get to a
destination. See game at undo 379. Train 11 can go from Eastern limits to the straight at 1,1 two
different ways. It can go through the refinery and pick up the tanker on its nose, or it could go
straight and then make the curve and skip the refinery and not pick up the car. The player should
have the two different options."
**The question:** a passing loop — turnout off the main, curve down, industry, curve back up — offers
two legal routes between the same two squares, and they couple different cars. Does §A.4's "you may
not go around" a car standing on your track forbid taking the bypass?
**Ruling: the player may choose the path.** Different paths may pick up different cars, or none.
§A.4 says "cars **on your track**", and the industry spur is a different track — declining to enter
it is not going around the car standing on it, it is not going to that car's track at all, which is
ordinary railroading. The mandatory half still bites in full once a route is chosen: every car
standing on the chosen route couples, and there is no route that passes a car and leaves it.
**Built in v0.4.8** — see `docs/plans/switching-paths.md`. The engine enumerates every distinct
simple route, dedupes by outcome (destination, entry side, and origin-tagged cars) rather than by
destination alone, and an intent that would otherwise be ambiguous carries a `via` naming one
intermediate square on the chosen route.