Files
station-master/docs/design.md
T

110 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Station Master — design index
Station Master is a tabletop railroad-operations game set in the era of timetable-and-train-order
railroading (1840–1950), being developed into a multiplayer digital game: players connect from a
web browser and an authoritative server manages lobbies and game state.
## Source of record
- `StationMasterPrototypeRules.pdf` — the original prototype rules
- `StationMaster-PrototypeTurnChart.pdf` — the 12-Stage turn chart
- `Deck cards2.xlsx` — the complete card list (115-card deck, 104 track pieces, 12 start cards)
- `Mainline Cards.pdf` — the ten Mainline card types with their speeds and entry points
- `Trains3.pdf` — all 22 train cards with names, speed class, consist and operating rules
- `tracks.png` — card art for track and industry cards
These stay as-is. Everything below is derived from them.
> **The last four arrived after the engine was built** and expand the game roughly threefold. See
> [`rules/implications.md`](rules/implications.md): much of what is recorded below as decided was a
> placeholder for exactly this material, and the balance measurements in Gap 12 were taken against a
> ruleset that does not match the design.
## Rules
| Document | What it is |
| --- | --- |
| [`rules/rules-v0.1.md`](rules/rules-v0.1.md) | Faithful markdown transcription of the PDFs. No corrections. The baseline everything diffs against. |
| [`rules/rules-v0.2.md`](rules/rules-v0.2.md) | **The working ruleset.** v0.1 with all ten gaps resolved, each change marked with its gap number. |
| [`rules/card-reference.md`](rules/card-reference.md) | What is printed on every card, plus the economy summary. The spec an engine or a print-and-play layout consumes. |
| [`rules/glossary.md`](rules/glossary.md) | Every defined term, alphabetized. |
| [`rules/open-questions.md`](rules/open-questions.md) | All thirteen gaps, each with the options considered, the decision, and the rationale. |
| [`rules/implications.md`](rules/implications.md) | **Read this first.** What the four recovered design files (`Deck cards2.xlsx`, `Mainline Cards.pdf`, `Trains3.pdf`, `tracks.png`) change — and which decisions they supersede. |
## Architecture
Target is **small self-hosted** scale: a handful of concurrent games, hosted on a website or as a
StartOS service. Stack is deliberately undecided; these documents describe what any implementation
must do.
| Document | What it is |
| --- | --- |
| [`architecture/components.md`](architecture/components.md) | **The project plan.** All 20 buildable pieces — where each runs, when, MVP vs finished size — plus dependency graph and build order. |
| [`architecture/overview.md`](architecture/overview.md) | Why the server is authoritative; state/intent/event split; transport; the rules-engine boundary. |
| [`architecture/game-state.md`](architecture/game-state.md) | The entity model, and the constraints that are easy to lose. |
| [`architecture/protocol.md`](architecture/protocol.md) | Intents, events, and per-player view redaction. |
| [`architecture/lobby-and-sessions.md`](architecture/lobby-and-sessions.md) | Create/join, seating, reconnection, persistence. |
| [`architecture/deployment.md`](architecture/deployment.md) | Plain web host vs StartOS service — and the one constraint that differs. |
| [`architecture/multiplayer.md`](architecture/multiplayer.md) | **The multiplayer build plan.** The authoritative server as a layer added on top, with solitaire unchanged and server-free — plus every decision taken, listed for review. |
## Current status
**v0.4.1.** Rules formalized, card faces specified, architecture documented, and the game playable
solitaire in a browser. See [`../CHANGELOG.md`](../CHANGELOG.md) for what each version changed and
[`../TODO.md`](../TODO.md) for what is open; this section is the shape of the project, not a
running tally, because a hand-maintained tally is what drifted last time.
**What is built.** The rules engine, the developer bot, the balance harness, the replay viewer and
the playable page — components 1–7, 17 and 18 of
[`architecture/components.md`](architecture/components.md). A game can be saved, shared, replayed and
stepped back through. **493 tests.**
**What is not.** The server. Phases 0 and 1 of
[`architecture/multiplayer.md`](architecture/multiplayer.md) landed in v0.4.0 — seat and player are
separate, turn state is per player, and the page talks to a `Session` rather than to the engine, so a
`RemoteSession` drops in without the page changing. Phase 2 onward is **deliberately held** until the
two provisional rules introduced in v0.3.0 have been played at a table: changing a rule after the wire
format is live costs far more than changing it before. Also unbuilt: the 22 opponent-directed cards
and real audio.
**Balance is not where it should be, and no conclusion should be read from the revenue numbers yet.**
The rebalance pass is deliberately deferred until the rules stop moving — card counts, industry counts
and the track mix all need moving together. `TODO.md` carries the standing distortions and the
measurements behind them.
**Three rules are genuinely open**, and they are the reason the README does not claim the rules are
finished: where the Local's coach stands while its engine works (a §A.4 question, not a train-card
question); Poling, the one card in the deck with no defined behaviour; and whether a Heavy Grade's
orientation is rolled or chosen at setup. Thirteen gaps in the prototype rules were found and closed;
these three came after.
**The event log narrates; the intents reconstruct.** Settled in v0.4.0 after the documentation had
claimed `state = fold(events)` for months. It is not true — the phase driver mutates state and then
describes it — so the canonical record is `{ seed, history: Intent[] }` and persistence will be built
on that. [`architecture/protocol.md`](architecture/protocol.md) §3 has the reasoning;
`test/events.test.ts` pins it.
**The economy, in one line:** Local Operations actions are the main currency — one per Stage, twelve
per Day — but **inbound work bypasses them**, which is where the game's variance comes from. See
`card-reference.md` §7.
**Stack: TypeScript**, chosen so the engine runs in both the server and the browser — one
implementation of the movement rules, and instant affordances without a round-trip. Node 22 runs
TypeScript natively, so there is no build step during development, which also means **erasable syntax
only**: no `enum`, no parameter properties, no namespaces.
Running alongside, and independent of all of it: **print-and-play components.** `card-reference.md`
specifies every card face, so layout and art are the only remaining work before a table playtest —
which answers the one question simulation cannot, whether it is fun.
Run the harness with `node src/sim/harness.ts [games] [length]`.
**Watch a game:** `node src/sim/replay.ts --seed 1234 [--length standard] [--out replay.html]`
writes a self-contained HTML file — open it in any browser and step through the game. It shows the
Division, the Office Area grid, every facility's boxes and `MEN|AT|WORK` track, plain-English
narration of each event, and a **Blocked** panel explaining why nothing is moving.
Running alongside, and independent of all of it: **print-and-play components.** `card-reference.md`
specifies every card face, so layout and art are the only remaining work before a table playtest —
which answers the one question simulation cannot, whether it is fun.