Station Master

A railroad operations game set in the era of timetable-and-train-order railroading (18401950), being built as a multiplayer browser game with an authoritative server.

You are the Station Master of a lineside Office on a shared eastwest Division. Trains run to a timetable with no radios — just pocket watches and written orders. You switch cars, work freight and passengers, and take your turn as Superintendent deciding whether it is safe to clear a following train into an occupied Subdivision. Get that wrong and two trains meet at speed.

Status

Design complete, implementation just begun.

  • Rules — fully specified. Ten gaps in the original prototype rules found and resolved.
  • Card faces — every card's printed values specified.
  • Architecture — six documents, including a 20-component build plan.
  • Code — build step 1 of 12 complete: card catalogue, state model, seeded RNG.

The MVP target is solitaire, one Office, a fixed number of Days, with a server talking to a single browser.

Layout

station-master/
├── docs/
│   ├── rules/          ← the ruleset, card reference, glossary, decision record
│   └── architecture/   ← how it is built, and what the pieces are
├── src/
│   └── engine/         ← pure rules engine: no I/O, no clock, deterministic from a seed
└── test/

Start with docs/design.md — it indexes everything.

Development

Requires Node 22.18+, which runs TypeScript directly by type stripping. There is no build step.

npm install
npm test          # node --test
npm run typecheck # tsc --noEmit

Because Node strips types rather than compiling them, the codebase is restricted to erasable syntax: no enum, no parameter properties, no namespaces. tsconfig.json enforces this.

Design notes worth knowing

  • The rules engine is pure. No I/O, no clock, no sockets, and all randomness derives from one stored seed — so any game is exactly replayable, and a full game can be driven in a unit test with no server at all.
  • It has two entry points, not one. apply(state, intent) for player actions, and advance(state) for everything the game does on its own — Mainline movement, collisions, the Stage clock, Superintendent rotation.
  • State is fold(events). The event log is the source of truth, which is what gives reconnection, restart recovery and post-game replay from a single decision.
  • Never call Math.random(). One ambient random call silently breaks replay.
S
Description
station master train game
Readme
2.7 MiB
Languages
TypeScript 98.8%
HTML 1.2%