v0.4.5 - fix bug with not being able to drop cars

This commit is contained in:
Jesse
2026-08-14 23:22:00 -04:00
parent 6655e20ea8
commit 98f93e6691
5 changed files with 191 additions and 9 deletions
+22 -6
View File
@@ -138,6 +138,23 @@ function farPort(card: TrackCard | undefined, entry: Port): Port {
return (card ? exitsFrom(card, entry)[0] : undefined) ?? opposite(entry);
}
/**
* THE PORT A TRAIN BACKS OUT BY — the other end of the card it is standing on.
*
* Not `opposite(facing)`. A train stands on a two-port card (never a turnout: §A.1 forbids
* finishing a Move on one), and backing up means leaving by whichever of those two ports it is not
* facing. On a straight those coincide; on a curve they never do, because a curve joins ADJACENT
* edges — a crew facing north on a north-west curve backs out through WEST, and `opposite('n')` is
* a south port the card does not have.
*
* The consequence of getting this wrong is total: `exploreMoves` returns nothing at all from a port
* the card lacks, so the crew simply cannot back up. It could round a curve and never come off it,
* which is enough to make a siding unreachable and setting out a cut impossible.
*/
function reversePort(s: GameState, player: PlayerIndex, from: GridCoord, facing: Port): Port {
return farPort(areaOf(s, player).grid.get(coordKey(from)), facing);
}
/** A tray's facing, expressed as the port it would leave by going forward. */
function facingPort(s: GameState, trayId: TrayId): Port {
const tray = s.trays.get(trayId);
@@ -1118,11 +1135,7 @@ function destinationsFor(
) {
const tray = s.trays.get(trayId)!;
const facing = facingPort(s, trayId);
// Reversing is the OPPOSITE port, whichever it is. This was hardcoded to flip between east and
// west, so a crew facing north or south reversed to 'e' — a port a north-south card does not
// have — and could never back out of a district spur. Combined with a facing that was itself
// derived from an east/west direction, it stranded 29 of 62 leftover crews on north-south track.
const exit: Port = reverse ? opposite(facing) : facing;
const exit: Port = reverse ? reversePort(s, player, from, facing) : facing;
return reachableDestinations(
{
area: areaOf(s, player),
@@ -1161,7 +1174,10 @@ export function movesFor(
};
const facing = facingPort(s, trayId);
const forward = exploreMoves(ctx, from, facing);
const back = exploreMoves(ctx, from, opposite(facing));
// The card's other end, not the compass opposite — see `reversePort`. This is what the board
// highlights, so getting it wrong hides half the crew's legal moves rather than merely refusing
// one: the squares behind the train never light up at all.
const back = exploreMoves(ctx, from, reversePort(s, player, from, facing));
const to = new Map<string, GridCoord>();
for (const d of [...forward.destinations, ...back.destinations]) to.set(coordKey(d.coord), d.coord);