Expand game engine, replay, tests, and documentation
This commit is contained in:
+34
-6
@@ -9,6 +9,7 @@
|
||||
import type {
|
||||
CarType,
|
||||
Direction,
|
||||
MainlineKind,
|
||||
FreightKind,
|
||||
GameLength,
|
||||
ModifierKind,
|
||||
@@ -65,11 +66,14 @@ export type CardGeometry =
|
||||
geometry: TrackGeometry;
|
||||
turnout?: TurnoutOrientation;
|
||||
axis?: TrackAxis;
|
||||
bypass?: 'n' | 's' | 'e' | 'w';
|
||||
/** Curves are printed left- or right-handed (design supply); not chosen on placement. */
|
||||
hand?: 'left' | 'right';
|
||||
}
|
||||
| { kind: 'office' }
|
||||
| { kind: 'limits' }
|
||||
| { kind: 'facility'; facility: FreightKind; axis?: TrackAxis };
|
||||
| { kind: 'facility'; facility: FreightKind; axis?: TrackAxis }
|
||||
/** Not track — a Modifier sits beside a Facility and raises its capacity (§9). */
|
||||
| { kind: 'modifier'; modifier: ModifierKind };
|
||||
|
||||
export type TrackCard = {
|
||||
geometry: CardGeometry;
|
||||
@@ -165,7 +169,7 @@ export function isOperationalRail(card: TrackCard): boolean {
|
||||
|
||||
export type NodeRef =
|
||||
| { at: 'divisionPoint'; side: Direction }
|
||||
| { at: 'mainline'; index: number; region: number }
|
||||
| { at: 'mainline'; index: number }
|
||||
| { at: 'grid'; owner: PlayerIndex; coord: GridCoord };
|
||||
|
||||
export type CrewTray = {
|
||||
@@ -186,11 +190,15 @@ export type CrewTray = {
|
||||
// The Division
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export type Region = { occupant: TrayId | null };
|
||||
/**
|
||||
* A train part-way across a Mainline card. Crossing time is measured in STAGES (Q1/Q2), so the
|
||||
* regions the placeholder used are gone — the cells printed on the cards are decoration.
|
||||
*/
|
||||
export type Transit = { tray: TrayId; stagesRemaining: number; direction: Direction };
|
||||
|
||||
export type DivisionNode =
|
||||
| { kind: 'divisionPoint'; side: Direction; holding: TrayId[] }
|
||||
| { kind: 'mainline'; regions: Region[] }
|
||||
| { kind: 'mainline'; card: MainlineKind; transits: Transit[] }
|
||||
| { kind: 'office'; owner: PlayerIndex };
|
||||
|
||||
/** Ordered west to east. For N players: N Office nodes and N+1 Mainline cards. */
|
||||
@@ -206,7 +214,17 @@ export type CardKind =
|
||||
| { kind: 'office'; tier: OfficeTier }
|
||||
| { kind: 'freightFacility'; facility: FreightKind }
|
||||
| { kind: 'modifier'; modifier: ModifierKind }
|
||||
| { kind: 'track'; geometry: TrackGeometry };
|
||||
| { kind: 'track'; geometry: TrackGeometry }
|
||||
/**
|
||||
* The four categories recovered from the design. They are in the deck so the composition is
|
||||
* right; their BEHAVIOUR is not implemented yet (implications.md §10 is still open), so
|
||||
* `check` rejects playing them and they can only be discarded.
|
||||
*/
|
||||
| { kind: 'spaceUse'; key: string }
|
||||
| { kind: 'enhancement'; key: string }
|
||||
| { kind: 'mainlineModifier'; key: string }
|
||||
| { kind: 'maneuver'; key: string }
|
||||
| { kind: 'action'; key: string };
|
||||
|
||||
export type Card = { id: CardId; kind: CardKind };
|
||||
|
||||
@@ -342,6 +360,16 @@ export type GameState = {
|
||||
yards: Yards;
|
||||
/** Index 0 = Stage 1. A train number, or null for an empty slot. */
|
||||
timetable: (number | null)[];
|
||||
/**
|
||||
* §7 — Extra Trains played from hand, waiting for a free Crew Tray. An Extra is not scheduled:
|
||||
* it runs once, immediately, then its card goes to the Salvage Yard (§2.3).
|
||||
*/
|
||||
pendingExtras: number[];
|
||||
/**
|
||||
* Q9 — train numbers ordered to run a second section. The next New Train Phase makes up an
|
||||
* identical train behind the first, if a Crew Tray is free.
|
||||
*/
|
||||
pendingSecondSections: number[];
|
||||
clock: Clock;
|
||||
turn: TurnState;
|
||||
/** Transient: trains already moved in the current Mainline Phase. Cleared when it ends. */
|
||||
|
||||
Reference in New Issue
Block a user