# Station Master A railroad operations game set in the era of timetable-and-train-order railroading (1840–1950), being built as a multiplayer browser game with an authoritative server. You are the Station Master of a lineside Office on a shared east–west 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/ ├── CHANGELOG.md ← what changed and why, in detail, commit to commit ├── TODO.md ← open questions, provisional numbers, things to come back to ├── docs/ │ ├── rules/ ← the ruleset, card reference, glossary, decision record │ ├── architecture/ ← how it is built, and what the pieces are │ └── design/ ← board layout studies and rendering samples ├── public/replays/ ← saved games published to the site's replay directory ├── scripts/ ← build and deploy the static site ├── src/ │ ├── engine/ ← pure rules engine: no I/O, no clock, deterministic from a seed │ ├── sim/ ← bot, harness, replay, board rendering │ └── web/ ← the playable site: splash, game, replay viewer └── test/ ``` Start with [`docs/design.md`](docs/design.md) — it indexes everything. Commit messages stay high level; [`CHANGELOG.md`](CHANGELOG.md) carries the reasoning and the measurements, and [`TODO.md`](TODO.md) is what we have decided not to forget. ## Development Requires **Node 22.18+**, which runs TypeScript directly by type stripping. There is no build step. ```sh 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.