Draw the board as track, split the site into three pages, and fix curve geometry
Adds SVG board rendering shared by the game and the replay, a splash page with a shareable replay directory, hover tooltips for reference detail, and makes curves two-port arcs so sidings and run-arounds can finally be built.
This commit is contained in:
+27
-14
@@ -23,6 +23,7 @@ import type {
|
||||
RollingStock,
|
||||
TrackCard,
|
||||
TrayId,
|
||||
TrackArc,
|
||||
TurnoutOrientation,
|
||||
} from './state.ts';
|
||||
import { carsOn, coordKey, isOperationalRail, spaceOn } from './state.ts';
|
||||
@@ -77,7 +78,12 @@ type PortPair = readonly [Port, Port];
|
||||
* - **facility** — a through track; the industry spur is the card's own spotting capacity rather
|
||||
* than a separate port.
|
||||
*/
|
||||
function connectionsFor(card: TrackCard): readonly PortPair[] {
|
||||
/**
|
||||
* Exported so the board can DRAW what the engine believes. Deriving the rails from anything else
|
||||
* would let a picture disagree with the rules about whether two cards join — which is the one thing
|
||||
* a track diagram exists to settle.
|
||||
*/
|
||||
export function connectionsFor(card: TrackCard): readonly PortPair[] {
|
||||
switch (card.geometry.kind) {
|
||||
// Neither a Modifier nor a Space-use card is track: nothing connects, no train may enter.
|
||||
case 'modifier':
|
||||
@@ -108,18 +114,22 @@ function connectionsFor(card: TrackCard): readonly PortPair[] {
|
||||
[o.stem, o.diverge],
|
||||
];
|
||||
}
|
||||
// A CURVE joins the through track to one diverging side — handedness is printed on the
|
||||
// card, not chosen (the design supplies both hands). A SHARP curve is geometrically the
|
||||
// same but costs two Moves to cross.
|
||||
// Curves and turnouts diverge DOWNWARD from the through track (Q7); handedness decides
|
||||
// which end of the through track the leg leaves from, not whether it goes up or down.
|
||||
/**
|
||||
* A CURVE IS AN ARC between two adjacent edges — one connection, no through track. The
|
||||
* printed cards show exactly this (docs/tracks.png, rows 3-4): a single sweep from edge to
|
||||
* edge with nothing running past it.
|
||||
*
|
||||
* It was previously modelled as a through track PLUS a diverging leg, which is a turnout —
|
||||
* and made curves topologically identical duplicates of them. Worse, every leg diverged
|
||||
* south, so no piece anywhere reached NORTH except an n-s straight. A district could
|
||||
* therefore only ever be a vertical column: no siding, no parallel track, no run-around.
|
||||
*
|
||||
* A SHARP curve is the same shape and costs two Moves to cross.
|
||||
*/
|
||||
case 'curved':
|
||||
case 'sharpCurved': {
|
||||
const stem = card.geometry.hand === 'left' ? 'w' : 'e';
|
||||
return [
|
||||
['e', 'w'],
|
||||
[stem, 's'],
|
||||
];
|
||||
const arc = card.geometry.arc ?? (card.geometry.hand === 'left' ? 'sw' : 'se');
|
||||
return [[arc[0] as Port, arc[1] as Port]];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,7 +154,10 @@ export const DEFAULT_TURNOUT: TurnoutOrientation = { stem: 'e', through: 'w', di
|
||||
* §A.1's constraint is preserved: a turnout's two legs still never join each other. Only the card's
|
||||
* rotation is free.
|
||||
*/
|
||||
const ARCS: readonly TrackArc[] = ['ne', 'nw', 'se', 'sw'];
|
||||
|
||||
export type TrackVariant = {
|
||||
arc?: TrackArc;
|
||||
axis?: 'ew' | 'ns';
|
||||
turnout?: TurnoutOrientation;
|
||||
bypass?: Port;
|
||||
@@ -167,9 +180,9 @@ export function variantsFor(geometry: TrackGeometry): TrackVariant[] {
|
||||
return TURNOUT_VARIANTS.map((t) => ({ turnout: t }));
|
||||
case 'curved':
|
||||
case 'sharpCurved':
|
||||
// Handedness is printed on the card, and the leg always goes down (Q7), so nothing is left
|
||||
// to choose — one variant.
|
||||
return [{ axis: 'ew' }];
|
||||
// All four rotations. A two-port arc has no handedness that survives turning — its mirror IS
|
||||
// one of its rotations — so the printed hand governs supply, not what can be built.
|
||||
return ARCS.map((arc) => ({ arc }));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user