Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc31555625 | ||
|
|
f308a2d94d |
+141
@@ -19,6 +19,147 @@ page as `v0.1.0 · <sha> · <date>`, so what is deployed can always be identifie
|
||||
|
||||
---
|
||||
|
||||
## 0.8.0.16 — 2026-09-20
|
||||
|
||||
The Quickstart put where a tester can actually reach it, the last place that still told the old
|
||||
story about the Interchange, and the release that carries v0.8.0.15's corrected card text into a
|
||||
package.
|
||||
|
||||
### The Quickstart is published beside the game
|
||||
|
||||
v0.8.0.15 wrote a Quickstart for a tester who has never played, and then left it in `docs/`, where
|
||||
a tester does not look — reachable only by someone who already has the repository. Nobody being
|
||||
handed the box has it.
|
||||
|
||||
`build-web.ts` now copies `docs/StationMaster-Quickstart.md` into `dist/quickstart.md`, and the
|
||||
splash page offers it under the three doors: *New to Station Master? Read the Quickstart guide.*
|
||||
**Not a fourth door** — reading the guide is not a way to play, and giving it equal weight in that
|
||||
grid would say it is.
|
||||
|
||||
**Copied, never re-written.** The Markdown document stays the one copy. A hand-written HTML twin
|
||||
would drift from it on the first edit, which is exactly the failure #15a was raised about and
|
||||
exactly what the v0.8.0.15 pass spent its time undoing: four references a month and two minor
|
||||
versions out of date.
|
||||
|
||||
**Served as plain text, which is honest rather than good.** Tables render as rows of pipes and the
|
||||
links do not click. Rendering it into a styled page wants a small Markdown converter and is filed
|
||||
as **TODO #109**; this is the fifteen-minute version that gets the guide in front of testers for
|
||||
this round instead of leaving them without one.
|
||||
|
||||
Two things had to be true and neither is checked by `tsc`, so both are tests:
|
||||
|
||||
- **The link has to find the file.** The href on the splash page and the filename the build writes
|
||||
are two strings with nothing connecting them — rename the document and the build quietly
|
||||
publishes nothing while the page keeps offering a link that 404s. The test asserts the guide is
|
||||
in `dist`, is the guide, and is the file the splash page names. The build also warns loudly
|
||||
rather than silently skipping a missing document.
|
||||
- **A `.md` file must not arrive as a download.** The server's MIME fallback is
|
||||
`application/octet-stream`, which a browser saves instead of displaying, so the link would hand a
|
||||
tester a file rather than a page. `'.md': 'text/plain; charset=utf-8'` was added to the table in
|
||||
`http.ts`, and the test reads that table out of the source rather than asserting on a copy of it,
|
||||
which would pass while the real one was wrong.
|
||||
|
||||
### `sortsCars` says what it gates, not what the card prints
|
||||
|
||||
Asked directly after v0.8.0.15 — does everything now agree? — and the audit turned up one place that
|
||||
did not. The field's own doc comment read:
|
||||
|
||||
```ts
|
||||
/** Interchange: "Sort cars in new order". */
|
||||
sortsCars: boolean;
|
||||
```
|
||||
|
||||
which names the printed text as though it were the flag's meaning. Nothing reads it to permit a
|
||||
sort. Its two readers — `resolveExtraStart` in `apply.ts` and the enumeration in `legal.ts` — both
|
||||
ask the same question, which is whether this is the one Mainline card with a Yard Limit and
|
||||
therefore the one an Extra may be made up and started on.
|
||||
|
||||
**Comment only, and worth the bump because of where it is.** This is what a developer reads before
|
||||
using the flag, and it is the most likely source of the sentence v0.8.0.15 had to correct — the one
|
||||
telling players, on the board, that cars could be sorted at an Interchange. The name is kept for its
|
||||
link to the card face, and the comment now says outright that the name is not the meaning.
|
||||
|
||||
Everything else already agreed, and was checked rather than assumed: `mainlineDescription`, the
|
||||
generated `rules/as-built.md`, the Mainline deck reference and the Rules FAQ all say the printed
|
||||
sorting is unimplemented and that a consist is re-ordered at a Small Yard. `rules/implications.md`
|
||||
is the deliberate exception and was already correct — it transcribes what the card face prints and
|
||||
then records that the concept is "still unimplemented".
|
||||
|
||||
### The reference stamps, and what was filed rather than done
|
||||
|
||||
The five documents v0.8.0.15 stamped — the Quickstart, the Rules, Components, the Home deck and the
|
||||
Mainline deck — plus `docs/design.md` now read **v0.8.0.16**, and the Rules book's implementation
|
||||
note about `card.discard` says the same. They describe this build because the audit above re-checked
|
||||
them against it, not because the number was swept forward: the point of the stamp is that it was
|
||||
earned, and a stamp bumped without a reading is worth less than none.
|
||||
|
||||
**TODO #109** holds the part deliberately not done — rendering the guide into a styled page instead
|
||||
of serving it as plain text. `build-web.ts`'s comment names that number rather than gesturing at
|
||||
"the next step", so the file and the worklist cannot drift apart the way the references just did.
|
||||
|
||||
### Why this is a release at all
|
||||
|
||||
One player-visible change, the link to the guide, and one that only a developer reads. The
|
||||
Interchange correction a player actually sees on the board is v0.8.0.15's; this is the version the
|
||||
wrapper bundles, so that correction reaches the box rather than only the repository — and a tag
|
||||
with an uncommitted comment sitting on top of it is not a thing to package.
|
||||
|
||||
## 0.8.0.15 — 2026-09-20
|
||||
|
||||
The reference documentation brought up to the game as it actually runs, ahead of the next round of
|
||||
testing, plus a **Quickstart** to hand a tester who has never played. One live bug fell out of the
|
||||
pass.
|
||||
|
||||
### The Interchange advertised an action the game does not have
|
||||
|
||||
`mainlineDescription` told players "Cars may be sorted into any new order here." It is the printed
|
||||
capability and **it has never been implemented** — nothing reads `sortsCars` to permit a sort. Its
|
||||
one live use is identifying the card an Extra may be made up on, because the Interchange is the
|
||||
Mainline card with a yard.
|
||||
|
||||
That sentence was not only in the documentation. `view.ts` renders this text as a Mainline card's
|
||||
`what`, so it is what a player reads **on the board**, and the generated card reference printed a
|
||||
"Sorts cars: yes" column beside it. A card advertising a button that does not exist sends a player
|
||||
hunting for it and then concluding the game is broken. The description now says what the card does,
|
||||
the generated column is headed "Extra may start", and the Rules FAQ answer says the card used to
|
||||
claim otherwise.
|
||||
|
||||
### The documents
|
||||
|
||||
Four hand-written references had not been touched since **v0.6.2** and were a month and two minor
|
||||
versions stale. Each now carries the build it describes at the top. The `v0.4.5` in their filenames
|
||||
is the prototype rules edition they were first written against, and the names are kept deliberately:
|
||||
`src/`, `CHANGELOG.md` and `docs/rules/` all cite them, and several of those citations are historical
|
||||
records of what a document said at the time.
|
||||
|
||||
- **`StationMaster-Quickstart.md` — NEW.** What the game is, how you win, the shape of a Stage, what
|
||||
is on the screen, a first twenty minutes, the things that surprise new players, and what to report.
|
||||
Written for somebody about to play rather than somebody building it.
|
||||
- **Rules.** §3.4 replaced outright — the `firstToTarget` / `highestAfterDays` victory model and the
|
||||
`target`-bearing length presets have not existed since 2026-08; it is a free `days` count and a
|
||||
combined floor of `3 × players × days`, with extended play. §3.5 and §6 said there was no lobby, no
|
||||
server and no multiplayer; there is. All three optional rules are implemented now, and "Sister
|
||||
Trains" never existed. Crossing time is regions, not mph. Extras are started where the player
|
||||
chooses. Added: the coach ratchet, the Small Yard's nose sorting, the make-up shortfall report, and
|
||||
the screen's names for the last two phases, which differ from the rules' names.
|
||||
- **Home deck.** Restructured around how the deck is *used* — piles, hand limit, reshuffle,
|
||||
placement, upgrades, what a modifier grant means — and defers every per-card table to
|
||||
`rules/as-built.md`. The counts table came out under TODO #15a, Jesse's own ruling: counts move
|
||||
with balance, and that table had been wrong for a month. Also corrected: industry track length
|
||||
(four cars like any card, not the box count), modifier grants, and `emptiesOnly`, which is enforced.
|
||||
- **Mainline deck.** Said cards were dealt "with replacement" in its opening paragraph and corrected
|
||||
itself four sections later; priced crossings in mph; and listed the Uncontrolled Siding as a
|
||||
passing card, which it is not — it puts an arriving train in the siding a region behind.
|
||||
- **Components.** Verified against `ROLLING_STOCK_SUPPLY`; the table was right. Added the Crew Tray,
|
||||
Whistle Post and Limits supplies, the coach ratchet, and what the Fedora actually carries.
|
||||
- **`docs/design.md`**, the index everything starts from, said **v0.4.3**, "what is not: the server",
|
||||
and 493 tests. It now lists the player-facing references — which it never has — so the Quickstart
|
||||
is findable, and marks `card-reference.md` superseded rather than presenting it as the card spec.
|
||||
|
||||
**`rules/as-built.md` needed no correction beyond the Interchange**, which is the point of generating
|
||||
it: `npm run build:cards` rewrites it from `content.ts` and `test/card-reference.test.ts` fails if
|
||||
the checked-in file disagrees. Everything hand-maintained around it had drifted; it had not.
|
||||
|
||||
## 0.8.0.14 — 2026-09-17
|
||||
|
||||
Six reports from the Day 2-3 playtest of v0.8.0.13. Two were the same shortage seen from opposite
|
||||
|
||||
@@ -582,6 +582,23 @@ What the project says about itself, and what it ships alongside the code.
|
||||
- [ ] **#88** — `card-reference.md`'s industry table may still be stale beyond Grocer's Warehouse and
|
||||
the Oil Refinery. See **Reference · #88**.
|
||||
|
||||
- [ ] **#109** — **Render the published Quickstart instead of serving it as plain text.** v0.8.0.16
|
||||
publishes `docs/StationMaster-Quickstart.md` to `dist/quickstart.md` and links it from the
|
||||
splash page, served as `text/plain` — so a tester reads the guide's tables as rows of pipes and
|
||||
its links do not click. That was the fifteen-minute version, taken deliberately to get the
|
||||
guide in front of testers for this round rather than to leave them without one.
|
||||
|
||||
**Copy the document, do not re-write it.** A hand-written HTML twin drifts from the Markdown on
|
||||
the first edit, which is the whole argument of #15a. The step is a small Markdown-to-HTML
|
||||
converter in `scripts/build-web.ts` writing `quickstart.html` beside the game, styled like the
|
||||
splash page — headings, lists, tables, links and code spans are the whole of what the guide
|
||||
uses. The `.md` MIME entry in `src/server/http.ts` and the two assertions in
|
||||
`test/web.test.ts` (`the Quickstart guide reaches the site`) move to the rendered file with it.
|
||||
|
||||
**Cost:** an afternoon, most of it in the converter's table and list handling. No dependency —
|
||||
a Markdown library would be the only runtime dependency this project has, and the guide uses a
|
||||
small enough subset that it is not worth becoming the first.
|
||||
|
||||
---
|
||||
|
||||
## Reference — measurements, rulings and rejected approaches
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
# Station Master — Components and Markers
|
||||
|
||||
**Rules implementation reference: v0.4.5**
|
||||
**Scope:** non-card physical components and supplies modeled by the v0.4.5 game. Card-created facilities, workers, deck piles, hand state, timetable state, and other markers are documented with their cards or in the rules book.
|
||||
**Describes the game as built at v0.8.0.16** (2026-09-20). Previously stamped "v0.4.5", the
|
||||
prototype rules edition these references were first written against; the filename keeps that stamp
|
||||
because `src/`, `CHANGELOG.md` and `docs/rules/` all cite this file by name.
|
||||
|
||||
**Scope:** non-card physical components and supplies. Card-created facilities, workers, deck piles,
|
||||
hand state, timetable state and other markers are documented with their cards or in the
|
||||
[rules book](StationMaster-Rules-v0.4.5.md).
|
||||
|
||||
The figures below are checked against `ROLLING_STOCK_SUPPLY` and the supply constants in
|
||||
`src/engine/content.ts`. They are physical inventory rather than deck tuning, which is why they are
|
||||
printed here at all — per-category CARD counts are deliberately not published anywhere (TODO #15a).
|
||||
|
||||
## Rolling stock
|
||||
|
||||
@@ -19,6 +28,14 @@ Rolling stock has a type and a load state. In the interface, coloured cars are l
|
||||
|
||||
The engine is not rolling stock and does not count against the four-car Crew Tray limit.
|
||||
|
||||
## Other supplies
|
||||
|
||||
| Component | Count | Note |
|
||||
| --- | ---: | --- |
|
||||
| Crew Trays | players + 3 | Engine and tray are one combined resource; there is no "engine without a tray". |
|
||||
| Whistle Post cards | 4 | Every player starts on one; it is not drawn from the deck. |
|
||||
| Limits signs | 8 | "2N + spares", so relocating one is never a supply question. |
|
||||
|
||||
## The two yards
|
||||
|
||||
### Division Yard
|
||||
@@ -34,7 +51,16 @@ When a train completes a run or is destroyed, its caboose returns to the Divisio
|
||||
|
||||
### Classification Yard
|
||||
|
||||
The Classification Yard collects used rolling stock: cars displaced by boarding passengers, cars cleared from inbound red boxes, cars removed by unjamming a facility, ordinary cars from completed or destroyed trains, and empty cars replaced by a completed outbound freight load. It is not a player-selectable source. It returns to service only when the Division Yard is empty.
|
||||
The Classification Yard collects used rolling stock: cars displaced by boarding passengers, cars
|
||||
cleared from inbound red boxes, cars removed by unjamming a facility, ordinary cars from completed
|
||||
or destroyed trains, and empty cars replaced by a completed outbound freight load. It is not a
|
||||
player-selectable source. It returns to service only when the Division Yard is empty.
|
||||
|
||||
> **This is a one-way ratchet for COACHES, and it decides games.** Boarding sends an emptied coach
|
||||
> here; detraining takes a fresh empty out of the *Division* Yard. Nothing returns a coach to the
|
||||
> Division Yard except the bare-yard refill — and a yard kept topped up with freight cars returning
|
||||
> from industries may never run bare. Measured over one three-Day game, every coach was here by the
|
||||
> middle of Day 2 and stayed. See [Rules](StationMaster-Rules-v0.4.5.md) §4.6.
|
||||
|
||||
## Crew Trays and trains
|
||||
|
||||
@@ -44,7 +70,16 @@ Within a tray, the engine can pull, push, or be between cars while switching. To
|
||||
|
||||
## Fedora
|
||||
|
||||
The **Fedora** is the physical marker for the Superintendent. The player with it resolves following-train clearance decisions and takes the 5-Revenue penalty for a Mainline collision caused by an unsafe clearance. The initial holder is the first player tied for the highest Superintendent setup D12 roll. The Fedora passes to the next seat to the left at each third-stage shift change.
|
||||
The **Fedora** is the physical marker for the Superintendent. The player with it resolves
|
||||
following-train clearance decisions, takes the Yard Office and Red Flag questions, and is the player
|
||||
every round round the table starts with. A Mainline collision is their fault by §10, and costs 5
|
||||
Revenue. The initial holder is the first player tied for the highest Superintendent setup D12 roll.
|
||||
|
||||
It passes at the end of Stages 3, 6, 9 and 12 — every third Stage, at the Supervisor Shift. Since
|
||||
v0.8.0.13 the handover is **announced on screen and written into the history**: it is the one thing
|
||||
in the game that changes hands on the clock rather than because somebody did something, so nobody is
|
||||
watching for it. Note that the Supervisor Shift refreshes every Laborer and Porter *every* Stage
|
||||
while the Fedora moves only every third.
|
||||
|
||||
## D12 and seeded randomness
|
||||
|
||||
|
||||
@@ -1,162 +1,153 @@
|
||||
# Station Master — Home Deck
|
||||
|
||||
**Rules implementation reference: v0.4.5**
|
||||
**Scope:** every card associated with the Home Office deck, including cards catalogued in the source but deliberately excluded from the dealt deck.
|
||||
**Describes the game as built at v0.8.0.16** (2026-09-20). Previously stamped "v0.4.5", the
|
||||
prototype rules edition these references were first written against; the filename keeps that stamp
|
||||
because `src/`, `CHANGELOG.md` and `docs/rules/` all cite this file by name.
|
||||
|
||||
## Dealt card catalogue
|
||||
**Scope:** the Home Office deck — how it is dealt, drawn, discarded and reshuffled, and what the
|
||||
rules are for playing each kind of card out of it.
|
||||
|
||||
The live Home Office deck contains **213 cards** in every currently supported mode. The rules for its setup, drawing, discarding, reshuffling, and hand limit are in the rules book, section 4.2.
|
||||
> **Per-card facts live in [`rules/as-built.md`](rules/as-built.md)**, which is GENERATED from
|
||||
> `src/engine/content.ts` and checked by `test/card-reference.test.ts`, so it cannot disagree with
|
||||
> the game. Read it for every card's name, effect, placement and whether its printed effect actually
|
||||
> resolves yet. This document is how the deck WORKS; that one is what is in it.
|
||||
>
|
||||
> **No card counts appear here, deliberately** (TODO #15a, Jesse's call 2026-08-22): counts move
|
||||
> with play balance, so a document printing them is answering a question that has a different answer
|
||||
> after the next retune. Where a count matters it is rendered as a yes/no — whether the deck deals
|
||||
> the card at all — which is a fact about the design. This page used to print a full counts table
|
||||
> and it was wrong for a month before anyone noticed.
|
||||
|
||||
| Active category | Cards |
|
||||
| --- | ---: |
|
||||
| Track | 96 |
|
||||
| Office upgrades | 14 |
|
||||
| Freight facilities | 27 |
|
||||
| Facility modifiers | 23 |
|
||||
| Train cards | 22 |
|
||||
| Enhancements | 18 |
|
||||
| Mainline modifiers | 7 |
|
||||
| Maneuvers | 6 |
|
||||
| **Total dealt** | **213** |
|
||||
## The piles
|
||||
|
||||
The catalogue also contains 12 space-use cards and 10 action cards. All 22 are excluded from every v0.4.5 dealt deck because their opponent-directed play rules are not implemented. They are listed at the end of this document for completeness.
|
||||
- **Home Office deck** — face down. The pile a Draw comes from.
|
||||
- **Three Departments** — face-up discard piles. A discard goes onto one, which is precisely so a
|
||||
rival may take it; a Draw may take the top card of a Department instead of the deck.
|
||||
- **Salvage Yard** — where a played-out card ends up. An Extra's card goes here after its run.
|
||||
|
||||
## Track cards — 96
|
||||
When the Home Office deck runs out it is rebuilt from the Salvage Yard and **all three Departments
|
||||
in full**, reshuffled from the seeded stream. A **spent timetabled train** is not collected — its
|
||||
number is on the timetable and it cannot run twice — but a *discarded* train was never played and
|
||||
is still runnable, so it comes back.
|
||||
|
||||
Track cards are ordinary Home Office cards, not a separate personal supply. A placed card must connect to existing rail. The Running Track is the horizontal row from Limit to Limit; cards there must carry an east–west through route. Placing track on a Limit extends the Running Track and moves that Limit outward.
|
||||
## Hand and turn
|
||||
|
||||
| Card | Copies | Implemented placement facts |
|
||||
| --- | ---: | --- |
|
||||
| Straight track | 32 | East–west Operational Rail. |
|
||||
| Curved track, right | 16 | A 45° curve; can rotate 180°, but cannot flip. Right-hand geometry is fixed to the `ne_sw` diagonal. |
|
||||
| Curved track, left | 16 | A 45° curve; can rotate 180°, but cannot flip. Left-hand geometry is fixed to the `nw_se` diagonal. |
|
||||
| Turnout, right | 16 | East–west through route plus one 45° branch. It may be passed through but is not Operational Rail, so a train cannot end a Move on it. |
|
||||
| Turnout, left | 16 | Same operational rules; opposite fixed diagonal. |
|
||||
| Sharp curved track, right | 0 | Catalogued but not dealt. |
|
||||
| Sharp curved track, left | 0 | Catalogued but not dealt. |
|
||||
The hand limit is **three**, or four while you hold a Red Flag. You may not end a turn over the
|
||||
limit: play a card or discard one to a Department. Some cards cannot be discarded at all — an Extra
|
||||
never can, and a timetabled train cannot when the `discardTimetabled` house rule is off — so a hand
|
||||
of nothing but those has exactly one way forward, which is to play one.
|
||||
|
||||
A turnout may upgrade an existing straight, or a curve whose arc is exactly the turnout’s diverging arc. An upgrade is forbidden if the existing card holds standing cars or an enhancement. All other occupied squares are unavailable.
|
||||
The opening deal is a house-rule choice made when the game is dealt. The default (`threeRandom`) is
|
||||
three cards from one shuffled deck; `threeTrackThreeOther` deals three track and three others from
|
||||
two separately shuffled piles, deliberately over the hand limit, so the first turn is spent choosing
|
||||
which district you can afford to build.
|
||||
|
||||
## Offices — 14
|
||||
Drawing is one of the three Local Operations options — see [Rules](StationMaster-Rules-v0.4.5.md)
|
||||
§4.2. Taking the option lets you draw **and** play or discard within the same turn.
|
||||
|
||||
Every player begins at a Whistle Post, which is not drawn from the deck. Office cards are upgrades and must be played in sequence; they upgrade the existing Office rather than replacing its card or attached track.
|
||||
## Track cards
|
||||
|
||||
| Card | Copies | A/D tracks | Porters | Passenger outbound/inbound slots | Other effect |
|
||||
| --- | ---: | ---: | ---: | ---: | --- |
|
||||
| Depot | 8 | 2 | 1 | 1 / 1 | Becomes a Control Point and Passenger Facility. |
|
||||
| Station | 4 | 3 | 2 | 2 / 2 | Upgrade Depot only; Control Point and Passenger Facility. |
|
||||
| Terminal | 2 | 4 | 3 | 3 / 3 | Upgrade Station only; Control Point and Passenger Facility. |
|
||||
Track cards are ordinary Home Office cards, not a separate personal supply.
|
||||
|
||||
The Whistle Post has one A/D track, no porters, and no passenger slots. An Office upgrade preserves modifiers already applied to it.
|
||||
- A placed card must **connect to existing rail**: at least one neighbour must join it.
|
||||
- The **Running Track** is the row from Limit to Limit. A card placed there must carry an east–west
|
||||
through route, or it breaks the main.
|
||||
- Placing track **on a Limit sign** extends the Running Track and moves that sign outward. The sign
|
||||
is a physical card, so it moves rather than being left stranded mid-track.
|
||||
- **Nothing may be placed outside your Limits** — track, industries and, since v0.8.0.14, Modifiers
|
||||
too. Your district ends at its sign.
|
||||
- Curves and turnouts are printed left- or right-handed. A card may be turned 180° but never flipped
|
||||
over, so its 45° leg never changes diagonal.
|
||||
- A **turnout may upgrade** an existing straight, or a curve whose arc is exactly the turnout's
|
||||
diverging arc. Not if the card holds standing cars or an enhancement. Every other occupied square
|
||||
is unavailable.
|
||||
- A turnout may be **run through but not stopped on**: it is not Operational Rail, so a Move may not
|
||||
end there.
|
||||
|
||||
## Freight facilities — 27
|
||||
## Office cards
|
||||
|
||||
An industry may be placed only on a connected straight stub off the Running Track. Each facility begins with one Laborer and a three-box MEN | AT | WORK freight pipeline. Its industry track has capacity equal to its base outbound plus inbound capacity, with a minimum of one car.
|
||||
Every player begins at a **Whistle Post**, which is not drawn from the deck: one A/D track, no
|
||||
Porters, no passenger slots, and not a Control Point.
|
||||
|
||||
No Office Area may contain a duplicate industry, or both ends of a listed lockout pair.
|
||||
Office cards are **upgrades in strict sequence** — Whistle Post → Depot → Station → Terminal — and
|
||||
each upgrades the Office in place rather than replacing its card or its attached track. Modifiers
|
||||
already beside it are preserved. Each tier adds an A/D track, a Porter, and an outbound and inbound
|
||||
passenger slot; a Depot and above is a Control Point and a Passenger Facility.
|
||||
|
||||
| Card | Copies | Cars handled | Flow | Base boxes | Lockout in same Office Area |
|
||||
| --- | ---: | --- | --- | --- | --- |
|
||||
| Freight House | 6 | Boxcar | Outbound and inbound | 1 out / 1 in | Freight House; Grocer’s Warehouse |
|
||||
| Mine Tipple | 6 | Hopper | Outbound | 1 out / 0 in | Power Plant |
|
||||
| Refinery | 3 | Tank car | Outbound | 1 out / 0 in | Power Plant |
|
||||
| Power Plant | 6 | Hopper or tank car | Inbound | 0 out / 1 in | Mine Tipple; Refinery |
|
||||
| Packing Sheds | 3 | Reefer | Outbound | 1 out / 0 in | Grocer’s Warehouse |
|
||||
| Grocer’s Warehouse | 3 | Boxcar or reefer | Inbound | 0 out / 1 in | Packing Sheds; Freight House |
|
||||
An upgrade takes no placement: the Office is where it already is.
|
||||
|
||||
## Facility modifiers — 23
|
||||
## Freight facilities
|
||||
|
||||
A modifier occupies an empty square adjacent to an eligible facility. It is unique by modifier type within an Office Area. The implementation attaches it permanently to the first eligible adjacent facility found; it does not implement a per-Stage choice when one modifier touches more than one possible facility.
|
||||
An industry is placed on a connected straight **stub off the Running Track** — never on the Running
|
||||
Track itself, and never outside the Limits. A Facility carries its own rails, so placing one places
|
||||
track.
|
||||
|
||||
`+ out` adds green outbound capacity only where the host can load; `+ in` adds red inbound capacity only where the host can unload. A capacity increase at a freight facility also lengthens its industry track by the usable number of added slots. Worker increases always apply.
|
||||
Each begins with one Laborer and a three-box **MEN | AT | WORK** pipeline. No Office Area may hold a
|
||||
duplicate industry, or both ends of a lockout pair — a producer and the consumer of the same
|
||||
commodity cannot be built in one district.
|
||||
|
||||
| Modifier | Copies | Eligible host | Actual grant |
|
||||
| --- | ---: | --- | --- |
|
||||
| Waiting Area | 3 | Any Office | +1 outbound passenger slot; +1 Porter |
|
||||
| Restaurant | 2 | Any Office | +1 outbound passenger slot; +1 Porter |
|
||||
| Hotel | 1 | Any Office | +1 outbound passenger slot; +1 Porter |
|
||||
| Truck Dock | 2 | Freight House, Packing Sheds, Grocer’s Warehouse | +1 **inbound** slot; no Laborer |
|
||||
| Railroad Express Agency | 1 | Freight House | +1 outbound slot; +1 Laborer |
|
||||
| Forklifts | 2 | Freight House, Packing Sheds | +1 outbound slot; +1 Laborer |
|
||||
| Prep Plant | 1 | Mine Tipple | +1 outbound slot; +1 Laborer |
|
||||
| Coal Piles | 1 | Mine Tipple | +1 outbound slot; +1 Laborer |
|
||||
| Conveyor Belts | 1 | Mine Tipple | +1 outbound slot; +1 Laborer |
|
||||
| Pipelines | 1 | Refinery | +1 outbound slot; +1 Laborer |
|
||||
| Oil Depot | 1 | Refinery | +1 outbound slot; +1 Laborer |
|
||||
| Viscosity Breakers | 1 | Refinery | +1 outbound slot; +1 Laborer |
|
||||
| Transmission Lines | 1 | Power Plant | +1 Laborer |
|
||||
| Rotary Dumps | 1 | Power Plant | +1 Laborer |
|
||||
| Steam Turbines | 1 | Power Plant | +1 Laborer |
|
||||
| Ice House | 2 | Packing Sheds or Grocer’s Warehouse | +1 outbound slot; +1 Laborer |
|
||||
| Local Small Groceries | 1 | Grocer’s Warehouse | +1 Laborer |
|
||||
**An industry track holds four cars, like any other card.** It is *not* sized by the industry's box
|
||||
count. That distinction was a real bug: box count is how much WORK an industry can hold, not how
|
||||
much RAIL it has, and conflating the two invented a printed siding no industry card carries.
|
||||
|
||||
A bonus beside a facility that cannot use its direction is not usable and does not create a slot or lengthen the track — an outbound bonus beside an inbound-only facility, or the Truck Dock's inbound bonus beside the outbound-only Packing Sheds, which leaves that card with no effect at all. Likewise, passenger modifiers beside a Whistle Post add Porters but do not create an outbound slot until the Office becomes a Passenger Facility.
|
||||
## Facility modifiers
|
||||
|
||||
## Train cards — 22
|
||||
A Modifier sits on an empty square among the **nine spots around its host Facility** — and, since
|
||||
v0.8.0.14, **inside your Limits**, like everything else. It may not stand in the Running Track row.
|
||||
One of each kind per Office Area.
|
||||
|
||||
Playing a timetabled train card rolls the seeded D12 and places its number in the first open timetable slot at or after the result, wrapping around the 12-slot chart. It then runs at that Stage every Day. An Extra is queued and made up when a Crew Tray becomes available; v0.4.5 automatically launches Extras eastbound from the Western Division Point.
|
||||
**A Modifier adds a BOX, never room for a car.** A Truck Dock beside a Grocer's Warehouse gives it a
|
||||
second red box — somewhere for one more arriving load to be cleared to — and changes nothing about
|
||||
how many cars may be spotted there.
|
||||
|
||||
The listed consist is a maximum, not a minimum: a train may depart with fewer cars, but must not exceed the listed categories, put a car behind a caboose, or leave with the engine buried among cars. A Crew Tray holds no more than four rolling-stock cars.
|
||||
**A grant the host cannot use does nothing**, and the game says so rather than pretending. An
|
||||
outbound bonus beside an inbound-only facility, or the Truck Dock's inbound bonus beside the
|
||||
outbound-only Packing Sheds, is discarded — the latter leaving that card with no effect at all.
|
||||
Passenger modifiers beside a **Whistle Post** add Porters but create no outbound slot until the
|
||||
Office becomes a Passenger Facility; the panel reports that as DORMANT rather than claiming the
|
||||
facility "only receives", which was wrong in both directions.
|
||||
|
||||
> **Changed 2026-08-22 (Gitea#7), Jesse's call:** the coach counts on **1/2 Crack Limited** and
|
||||
> **5/6 The Sparrow** were swapped — the Limited drops from three coaches to two, the Sparrow rises
|
||||
> from two to three. This is a change to the CARDS, not a correction to this table: `Trains3.pdf` and
|
||||
> the transcription in [`rules/implications.md`](rules/implications.md) §5 still show the original
|
||||
> numbers, and are right about what the printed cards said. `src/engine/content.ts` and this table
|
||||
> carry what the game plays.
|
||||
## Train cards
|
||||
|
||||
| Train | Speed | Direction | Listed maximum consist | Implemented special rule |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1/2 Crack Limited | Fast | 1 west / 2 east | **2 coaches** | No switching; passenger work only at Terminals; expedited. |
|
||||
| 3/4 Express | Fast | 3 west / 4 east | 2 freight | May exchange at most one freight car at each grid location during its switching turn; expedited. |
|
||||
| 5/6 The Sparrow | Fast | 5 west / 6 east | **3 coaches** | No switching; expedited. |
|
||||
| 7/8 Local | Slow | 7 west / 8 east | 1 freight, 1 coach | Its coach may not be set out during switching. |
|
||||
| 9/10 Heavy Freight | Slow | 9 west / 10 east | 3 freight, 1 caboose | — |
|
||||
| 11/12 Drag Freight | Slow | 11 west / 12 east | 2 freight, 1 caboose | — |
|
||||
| X13 Appleseed Extra | Slow | Player choice printed; v0.4.5 launches east | 3 empty freight, 1 caboose | May drop cars but cannot pick up. The engine enforces no pickup, but does not enforce the printed empties-only consist restriction at make-up. |
|
||||
| X14 Fruit Growers Express | Fast | Player choice printed; v0.4.5 launches east | 2 reefers, 1 caboose | Expedited. The code enforces reefers-only; the printed extra loaded-reefer pickup is not a separate rule. |
|
||||
| X15 Yard Xfer | Slow | Player choice printed; v0.4.5 launches east | 2 freight, 1 caboose | — |
|
||||
| X16 Light Engine Move | Fast | Player choice printed; v0.4.5 launches east | No cars | No switching. |
|
||||
| X17 Campaign Train | Fast | Player choice printed; v0.4.5 launches east | 1 coach | No switching. First Office arrival lays over for speeches; later arrivals are expedited. |
|
||||
| X18 Circus Train | Slow | Player choice printed; v0.4.5 launches east | 2 freight, 1 coach, 1 caboose | No switching. The first Mainline Phase in which it remains stopped awards its current Office’s player 1 Revenue. |
|
||||
| X19 Military Train | Slow | Player choice printed; v0.4.5 launches east | 1 freight, 2 coaches | No switching; no passenger work; expedited. |
|
||||
| X20 Director’s Private Car | Slow | Player choice printed; v0.4.5 launches east | 2 freight, 1 coach | No passenger work. |
|
||||
| X21 Freight Extra | Slow | Player choice printed; v0.4.5 launches east | 3 freight, 1 caboose | — |
|
||||
| X22 Pee-Dee | Slow | Player choice printed; v0.4.5 launches east | 1 caboose | May pick up empty cars only. |
|
||||
Playing a **timetabled** train rolls the seeded D12 and puts its number in the first open timetable
|
||||
slot at or after the result, wrapping around the 12-slot chart. It then runs at that Stage **every
|
||||
Day**.
|
||||
|
||||
## Enhancements — 18
|
||||
Playing an **Extra** queues it; it is made up when a Crew Tray comes free, and **the player who
|
||||
played the card chooses where it starts and loads it as they choose** (§7). Where it may start is a
|
||||
house rule — `divisionPointsOnly`, `ownOffice`, or `anyOffice` (the default) — and the Interchange is
|
||||
also available, because it is the one Mainline card with a yard. An Extra runs once and its card
|
||||
goes to the Salvage Yard.
|
||||
|
||||
| Card | Copies | Placement | v0.4.5 behavior |
|
||||
| --- | ---: | --- | --- |
|
||||
| Interlocking | 2 | Bare Running Track straight | When the Office is full, an inbound train is held at the Limits instead of colliding. |
|
||||
| Facing Point Locks | 2 | Any card; requires an Interlocking somewhere in the district | Blocks Derail. Derail is unavailable in v0.4.5, so this remains dormant. |
|
||||
| Yard Office | 1 | Bare Secondary Track straight | A coachless inbound train is diverted to this track instead of occupying an A/D track. |
|
||||
| Small Yard | 1 | Bare Secondary Track straight | A train may spend one switching Move here to reorder its entire consist and put the engine at the nose. |
|
||||
| Water Column | 2 | Bare Running Track straight | Removes a Watertower. Watertower cards are unavailable, so this remains dormant. |
|
||||
| Overpass | 1 | Any card | No implemented effect. |
|
||||
| Telegraph | 3 | Bare Running Track straight | Once per Day, the Superintendent may add 4 to an opposing train’s number when that makes a facing clearance safe. |
|
||||
| Telephone | 2 | On a Telegraph | Same dispatch mechanism, +8 once per Day. |
|
||||
| Radio | 2 | On a Telephone | Same dispatch mechanism, +12 once per Day. |
|
||||
| ABS Signals | 2 | Any Mainline card | Prevents rear-end collisions and holds a following train short. |
|
||||
> The v0.4.5 behaviour of launching every Extra eastbound from the Western Division Point was
|
||||
> replaced in v0.6.2. The number no longer decides an Extra's direction; the start does.
|
||||
|
||||
An enhancement requiring a bare straight cannot share that straight with another such enhancement. Telephone and Radio are the explicit stackable chain.
|
||||
The listed consist is a **maximum, not a minimum**. A train may depart with fewer cars, but not with
|
||||
more, not of the wrong category, not with a car behind the caboose, and not with the engine buried
|
||||
among its own cars. A Crew Tray holds four pieces, and a caboose counts toward the four.
|
||||
|
||||
## Maneuvers — 6
|
||||
A train made up short of what its card calls for is reported as such, with what it wanted and why
|
||||
the yard could not supply it — see Rules §4.4.
|
||||
|
||||
| Card | Copies | Behavior |
|
||||
| --- | ---: | --- |
|
||||
| Red Flags | 5 | May be played at any time on a train stopped on a Mainline card. An approaching following train is held instead of moving into it. |
|
||||
| Flying Switch | 1 | During the owner’s switching option, spend one Move to roll a tail-end cut into a track-connected freight facility without moving the engine there. |
|
||||
| Poling | 0 | Catalogued but not dealt; no rule is implemented. |
|
||||
**Per-train consists and printed rules: [`rules/as-built.md`](rules/as-built.md) § Trains.** It
|
||||
carries the `empties only`, `reefers only`, `drop only` and `pick up empties only` restrictions,
|
||||
every one of which the engine enforces.
|
||||
|
||||
## Catalogued but not dealt — 22 opponent-directed cards
|
||||
## Enhancements, Mainline modifiers and Maneuvers
|
||||
|
||||
### Space-use cards — 12
|
||||
- **Enhancements** are played into your district and change what a square does — the **Small Yard**
|
||||
(re-order a consist for one Move), **Interlocking**, **Yard Office**, **ABS Signals** and the rest.
|
||||
`as-built.md` marks each one `live`, `dormantSolo` or `unbuilt`, which is the part only the
|
||||
implementation knows.
|
||||
- **Mainline modifiers** are played onto a Mainline card: the Heavy Grade helpers and Realignment.
|
||||
See [Mainline deck](StationMaster-Mainline-Deck-v0.4.5.md).
|
||||
- **Maneuvers** are held and spent: **Red Flags** and **Flying Switch** have their own actions.
|
||||
Poling is catalogued but its effect is recorded as "TBD in the source", so there is nothing to
|
||||
implement.
|
||||
|
||||
Bean House (1), Flop House (1), Watertower (1), Hobo Jungle (1), Section House (1), City Blocks (4), Engine Shops (1), Tenderloin District (1), and Engineer Cemetery (1) are excluded. Their source descriptions say they consume table space; Hobo Jungle additionally describes vandalism looting a passing boxcar. No placement or effect is available in v0.4.5.
|
||||
## Opponent-directed cards — not dealt
|
||||
|
||||
### Action cards — 10
|
||||
|
||||
Derail (2), Broken Coupler (1), Railroad Crossing (1), Per Diem Inventory (1), Demurrage Charge (1), Customer Complaints (1), Vandalism (1), Hotbox (1), and Outlawed (1) are excluded. Their printed target/effect text is catalogued in the code, but `card.play` rejects the categories as not implemented. Consequently, no card can currently be played on another player.
|
||||
|
||||
There are also two **Facing Point Locks** listed among Mainline modifiers. They are treated as the same grid enhancement as Facing Point Locks above, require an Interlocking, and are included in the active 213-card total.
|
||||
The **Action** and **Space-use** categories are opponent-directed and are **excluded from every
|
||||
dealt deck**, because their play rules are not implemented. They are catalogued in
|
||||
`as-built.md` so the composition is on record, and `check` refuses to play one. This is deliberate:
|
||||
silently accepting them would make a card look playable while doing nothing.
|
||||
|
||||
@@ -1,21 +1,62 @@
|
||||
# Station Master — Mainline Deck
|
||||
|
||||
**Rules implementation reference: v0.4.5**
|
||||
**Scope:** the tarot-sized Mainline cards placed between Offices. This is an implementation reference, not a transcription of earlier prototype rules.
|
||||
**Describes the game as built at v0.8.0.16** (2026-09-20). Previously stamped "v0.4.5", the
|
||||
prototype rules edition these references were first written against; the filename keeps that stamp
|
||||
because `src/`, `CHANGELOG.md` and `docs/rules/` all cite this file by name.
|
||||
|
||||
**Scope:** the tarot-sized Mainline cards placed between Offices — how the deck is dealt, what a
|
||||
card does to a train crossing it, and the Home Deck cards played onto one.
|
||||
|
||||
> **Per-card numbers live in [`rules/as-built.md`](rules/as-built.md)**, which is GENERATED from
|
||||
> `src/engine/content.ts` and checked by `test/card-reference.test.ts`, so it cannot disagree with
|
||||
> the game. This document explains how the deck is used; that one is the table of record. Where the
|
||||
> two ever differ, as-built is right.
|
||||
|
||||
## How Mainline cards work
|
||||
|
||||
At setup the game places one randomly selected Mainline card between each neighbouring pair of Offices and one beyond each end Office, between it and a Division Point. Thus, a game with *N* players has *N + 1* Mainline cards. The implementation selects types with replacement; it does not deal them from a shuffled finite deck.
|
||||
At setup the game lays one Mainline card between each neighbouring pair of Offices and one beyond
|
||||
each end Office, between it and a Division Point — so a game with *N* players uses **N + 1** cards.
|
||||
They are **dealt from a finite deck without replacement** (since v0.6.2), so no Division can hold
|
||||
two of a card printed once. The two Division Points are the fixed ends of the Division and are not
|
||||
in the deck: `buildDivision` lays them itself.
|
||||
|
||||
A train crossing a Mainline card spends Stages, rather than moving through its printed cells. A 60 mph card costs one Stage and a 30 mph card costs two. A Slow train adds one Stage to every crossing. On Hilly terrain, any train carrying at least one coach uses the passenger rate; a train carrying no coach uses the freight rate. No crossing can take less than one Stage.
|
||||
A card does not belong to either neighbouring Office. It is shared Division.
|
||||
|
||||
The card does not itself determine which player owns the adjacent Office. It is part of the shared Division.
|
||||
### Crossing time is REGIONS, not mph
|
||||
|
||||
## Physical card inventory in `Mainline Cards.pdf`
|
||||
A card is divided into **regions**, and a train advances **one region per Stage**. Crossing time is
|
||||
therefore `regions − startRegion`, and nothing else. **The printed mph is scenery.**
|
||||
|
||||
The supplied PDF has **ten** tarot-sized terrain cards. Plains appears twice; the other terrain types appear once each. It also includes two Division Point cards.
|
||||
This is the part most likely to be remembered wrong, because it used to work the other way: mph set
|
||||
the cost and a Slow train added a Stage to *every* card. It does not. Four things move a train's
|
||||
start region and nothing else does:
|
||||
|
||||
| Physical card | Copies in the PDF |
|
||||
1. **The card's own back region.** The Uncontrolled Siding and the Interchange print a back region
|
||||
that is not part of the road, so a train running through begins past it.
|
||||
2. **A card that prints a Fast and a Slow start — and only Hilly does.** A fast train starts one
|
||||
region along and crosses in 1 Stage; a slow one takes 2. **No other card reads a train's
|
||||
Fast/Slow rating at all.**
|
||||
3. **The permanent Heavy Grade modifiers**, each moving a train one region up the hill.
|
||||
4. **Occupancy**: arriving to find the card occupied can put a train in the siding, a region behind.
|
||||
|
||||
No crossing ever takes less than one Stage.
|
||||
|
||||
### Traffic
|
||||
|
||||
Only **Double Track** lets two trains stand on one card, so a following train is not held behind a
|
||||
slower one. Every other card holds one train at a time. The **Uncontrolled Siding** is not a passing
|
||||
card: arriving to find a train already there puts you in the siding a region behind it — you do not
|
||||
run into it, and it costs you the extra Stage instead.
|
||||
|
||||
Whether a following train may enter an occupied card at all is the Superintendent's ruling (§8.1,
|
||||
Rules §4.5). Getting it wrong is what causes collisions.
|
||||
|
||||
## The deck
|
||||
|
||||
Ten drawable cards — Plains twice, the other eight once each — plus the two Division Point cards,
|
||||
which are not drawn.
|
||||
|
||||
| Card | Copies |
|
||||
| --- | ---: |
|
||||
| Plains | 2 |
|
||||
| Curves | 1 |
|
||||
@@ -26,60 +67,62 @@ The supplied PDF has **ten** tarot-sized terrain cards. Plains appears twice; th
|
||||
| Tunnel | 1 |
|
||||
| Trestle | 1 |
|
||||
| Interchange | 1 |
|
||||
| East Division Point | 1 |
|
||||
| West Division Point | 1 |
|
||||
| East / West Division Point | 1 each, not dealt |
|
||||
|
||||
The PDF art labels this card “Yard”; this reference uses the implementation’s correct name, **Interchange**, to distinguish it from the Division Yard, Classification Yard, Yard Office, and Small Yard.
|
||||
The PDF art labels the Interchange "Yard". This reference uses **Interchange** throughout, to keep
|
||||
it apart from the Division Yard, the Classification Yard, the Yard Office and the Small Yard — five
|
||||
different things.
|
||||
|
||||
## Implemented Mainline card reference
|
||||
**Region counts and entry points per card are in [`rules/as-built.md`](rules/as-built.md).**
|
||||
|
||||
| Card | Implemented crossing time and feature |
|
||||
| --- | --- |
|
||||
| Plains | 60 mph; one Stage for Fast, two for Slow. The implementation has one Plains *type* rather than the PDF’s two physical copies. |
|
||||
| Curves | 30 mph; two Stages for Fast, three for Slow. |
|
||||
| Hilly | Passenger train: 60 mph. Freight-only train: 30 mph. Add one Stage if Slow. |
|
||||
| Heavy Grade | Starts at 30 mph; two Stages for Fast, three for Slow. The card prints “Player sets orientation”; **the game deliberately overrides that and rolls the uphill direction from the seed** — settled in v0.5.0 and re-confirmed 2026-08-23, see the note below. Grade modifiers can reduce the time, to a minimum of one Stage. |
|
||||
| Double Track | 60 mph. Printed capability: trains may pass. The traffic-resolution rule is in Rules §4.5. |
|
||||
| Uncontrolled Siding | 60 mph. Printed capability: trains may pass. The traffic-resolution rule is in Rules §4.5. |
|
||||
| Tunnel | 30 mph. |
|
||||
| Trestle | 60 mph. |
|
||||
| Interchange | 60 mph. A train may be reordered there only through the card’s printed “sort cars” concept; the current engine does **not** provide a Mainline sorting action for it. |
|
||||
## The Interchange, and what it does NOT do
|
||||
|
||||
### PDF/code mismatch — CORRECTED
|
||||
The Interchange prints a car-sorting capability. **It is not implemented, and never has been.** The
|
||||
one thing the card's `sortsCars` flag actually gates is that an **Extra Train may be made up and
|
||||
started here** — it is the Mainline card with a yard, which is why §7 allows it (v0.6.2). An Extra
|
||||
starting here begins in the back region and takes the extra Stage.
|
||||
|
||||
**Was:** `src/engine/content.ts` defines nine `MAINLINE_PROFILES` types: one Plains entry plus the eight other terrain types above. `setup.ts` selected uniformly from that nine-type list, **with replacement**. The second Plains card shown in `Mainline Cards.pdf` was therefore not represented as a duplicate card or as extra Plains weight in setup — and, worse than a weighting error, a Division could be dealt two Interchanges, two Tunnels or two Trestles, none of which the deck contains.
|
||||
Re-ordering a consist is done at a **Small Yard** in an Office Area, for one switching Move. See
|
||||
Rules §4.3.
|
||||
|
||||
**Now:** `MAINLINE_DECK` in `content.ts` is the inventory table above — ten drawable cards, Plains twice and the other eight once each — and `buildDivision` deals from it without replacement. The two Division Point cards are not in that deck: they are the fixed ends of the Division, laid by `buildDivision` itself rather than drawn.
|
||||
|
||||
The Interchange is what forced the correction. §7 lets an Extra be started at the Interchange "if one is on the board" (see `docs/rules/implications.md`, §7), which only reads as a rule if the board can hold at most one.
|
||||
|
||||
The executable state represents East and West Division Points as fixed end nodes, not as card records. They are functionally present at the ends of the Division, but are not represented as the two PDF cards in the deck/state model.
|
||||
> **Corrected 2026-09-20.** Until this pass the card said "Cars may be sorted into any new order
|
||||
> here" — on the board, in the tooltip a player reads, and in the generated reference. A card
|
||||
> advertising an action the game will not offer sends a player hunting for a button that does not
|
||||
> exist. The description now says what the card does.
|
||||
|
||||
## Heavy Grade modifiers
|
||||
|
||||
These cards come from the Home Office deck and are played onto a Mainline card during a player’s Draw option.
|
||||
Home Office cards, played onto a Mainline card during a player's Draw option.
|
||||
|
||||
| Card | Copies | Placement and actual effect |
|
||||
| --- | ---: | --- |
|
||||
| Brakeman | 1 | Heavy Grade only. Reduces a downhill crossing by one Stage. |
|
||||
| Airbrakes | 1 | Heavy Grade only, and Brakeman must already be on that card. Reduces a downhill crossing by one additional Stage. |
|
||||
| Helpers | 1 | Heavy Grade only. Reduces an uphill crossing by one Stage. |
|
||||
| Realignment | 2 | May be played only onto an unoccupied Mainline card. Changes Plains → Double Track, Curves → Plains, Uncontrolled Siding → Double Track, or Trestle → Uncontrolled Siding. It cannot be played on any other card type. |
|
||||
| Card | Placement and effect |
|
||||
| --- | --- |
|
||||
| Brakeman | Heavy Grade only. A **downhill** train starts one region further on. |
|
||||
| Airbrakes | Heavy Grade only, and **Brakeman must already be on that card**. A downhill train starts one region further again. |
|
||||
| Helpers | Heavy Grade only. An **uphill** train starts one region further on. |
|
||||
| Realignment | Only onto an **unoccupied** Mainline card. Plains → Double Track, Curves → Plains, Uncontrolled Siding → Double Track, Trestle → Uncontrolled Siding. No other card may be realigned. |
|
||||
|
||||
For the grade cards, “uphill” should be the direction selected by the player when the card is placed. In v0.4.5 it is the seeded `gradeUp` direction because setup has no player-choice step.
|
||||
|
||||
## What is not implemented
|
||||
|
||||
- There is no player choice or physical placement interaction for Mainline cards; setup deals them automatically from the seeded random stream. There **is** a finite draw pile as of v0.6.2 — the deck above, dealt without replacement, so no Division can hold two of a card printed once.
|
||||
- Interchange is catalogued as a “sort cars” card, but v0.4.5 has no operation that reorders a train on the Interchange. The Small Yard in an Office Area is the implemented sorting mechanism.
|
||||
- Interchange now has one player-facing use: an Extra Train may be **started** there, made up in its yard and highballing onto the Mainline when the Subdivision is clear (§7, v0.6.2). Car sorting remains unimplemented.
|
||||
A Heavy Grade is 3 regions, so it is 3 Stages to climb and 3 to run down before help. Modifiers
|
||||
never reduce a crossing below one Stage.
|
||||
|
||||
## Heavy Grade orientation is settled, not missing
|
||||
|
||||
Heavy Grade orientation is rolled from the seed rather than chosen by a player. **This is a decision, not a gap, and it is not awaiting a player-selection step.**
|
||||
Heavy Grade orientation is **rolled from the seed**, not chosen by a player. **This is a decision,
|
||||
not a gap.**
|
||||
|
||||
The card prints “(Up)” and “Player sets orientation”, which assumes the card has an owner. This one does not: `buildDivision` lays the Division as `DP · Mainline · Office · Mainline · … · DP`, so a Heavy Grade always sits **between two districts**, or beyond an end Division Point next to one — never inside a single player’s own district.
|
||||
The card prints "(Up)" and "Player sets orientation", which assumes the card has an owner. This one
|
||||
does not: the Division is laid `DP · Mainline · Office · Mainline · … · DP`, so a Heavy Grade always
|
||||
sits **between two districts**, or beyond an end Division Point beside one — never inside a single
|
||||
player's district.
|
||||
|
||||
Orientation is not cosmetic: Brakeman and Airbrakes each take a Stage off a train running **downhill**, Helpers takes one off a train running **uphill**, and odd-numbered trains run west while even run east. Turning the card around therefore decides which of those modifier cards are worth anything and which direction of traffic is favoured — permanently, for the whole game. Handing that to one of the two neighbours advantages them over the other, and no player has a fair claim to it.
|
||||
Orientation is not cosmetic. Brakeman and Airbrakes help a train running **downhill**, Helpers helps
|
||||
one running **uphill**, and odd-numbered trains run west while even run east. Turning the card
|
||||
around decides which modifiers are worth anything and which direction of traffic is favoured, for
|
||||
the whole game. Handing that to one of two neighbours advantages them over the other, and neither
|
||||
has a fair claim to it.
|
||||
|
||||
**Re-opened and closed again on 2026-08-23**, when the option of giving the choice to the Superintendent was considered and rejected. Jesse’s call: v0.5.0’s ruling stands. Rolling from the seed is deterministic, roughly even (51/49 east/west over 400 games), identical for solitaire and multiplayer, and keeps setup non-interactive — the game has no setup phase, so the question would have to interrupt play before the first Local Operations, in the minority of games that deal the card at all (20% at one player, rising to 50% at four).
|
||||
**Re-opened and closed again on 2026-08-23**, when giving the choice to the Superintendent was
|
||||
considered and rejected. Jesse's call: v0.5.0's ruling stands. Rolling from the seed is
|
||||
deterministic, roughly even (51/49 east/west over 400 games), identical in solitaire and
|
||||
multiplayer, and keeps setup non-interactive — the game has no setup phase, so the question would
|
||||
have to interrupt play before the first Local Operations, in the minority of games that deal the
|
||||
card at all (20% at one player, rising to 50% at four).
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
# Station Master — Quickstart
|
||||
|
||||
**For a tester who has never played. Describes the game as built at v0.8.0.16** (2026-09-20).
|
||||
|
||||
Read this once before you sit down. It is about twenty minutes of reading and will save you an hour
|
||||
of confusion. The deeper references are listed at the end.
|
||||
|
||||
---
|
||||
|
||||
## 1. What the game is
|
||||
|
||||
You are a railroad **Office** — a town — on a shared east–west main line called the **Division**.
|
||||
Everyone's Office sits in a row along it, west to east, with **Mainline cards** between them.
|
||||
|
||||
Two jobs run at once:
|
||||
|
||||
**Your own job, in your district.** Build track. Build industries and a passenger platform. Shunt
|
||||
cars around with a switching crew to get the right car to the right place, so that freight can be
|
||||
loaded and unloaded and passengers can get on and off. Every one of those completed pieces of work
|
||||
pays **Revenue**, which is the score.
|
||||
|
||||
**The shared job, out on the Division.** Scheduled trains run across everybody's territory on a
|
||||
timetable. They arrive at your Office, and you work them. When two trains want the same stretch of
|
||||
track, the player wearing the **Superintendent's Fedora** rules on whether the second may follow the
|
||||
first. Rule wrong and they collide, which costs 5 Revenue and counts against a limit that can end
|
||||
the game.
|
||||
|
||||
The tension the game is built around: **the useful work is local and slow, and the trains are shared
|
||||
and do not wait.**
|
||||
|
||||
---
|
||||
|
||||
## 2. How you win
|
||||
|
||||
The game runs a set number of **Days** — five by default. Each Day is **12 Stages**, which you can
|
||||
think of as two-hour clock periods from midnight.
|
||||
|
||||
At the end of the last Day:
|
||||
|
||||
1. **The table's combined Revenue is checked first**, against a floor of **3 × players × Days**. At
|
||||
three players over five Days that is 45. **Miss it and everybody loses**, however well you
|
||||
personally did. This is the number to watch.
|
||||
2. **Co-op:** meeting the floor is the win, together.
|
||||
3. **Competitive:** meeting the floor puts the game on, and the **highest individual Revenue** wins.
|
||||
4. **Solitaire:** meet the floor by yourself.
|
||||
|
||||
**Collisions can end it early and badly.** Breaching the collision limit stops play at once in a
|
||||
collective loss — the railroad has been declared unsafe. Default limits are 3 in one Day and 5 in
|
||||
the game.
|
||||
|
||||
**Falling short offers another Day** rather than just ending, so a game that misses the floor can be
|
||||
played on. A game stopped by collisions cannot.
|
||||
|
||||
### Where Revenue comes from
|
||||
|
||||
| Work | Pays |
|
||||
| --- | --- |
|
||||
| A passenger boarding at your platform | 1 |
|
||||
| A passenger getting off at your platform | 1 |
|
||||
| Completing an outbound freight load | 1 |
|
||||
| Completing an inbound freight unload | 1 |
|
||||
| A train completing its run across the whole Division | 0 by default, to every player |
|
||||
|
||||
Those rates are set when the game is dealt and can be changed. The default means **your score comes
|
||||
almost entirely from working cars in your own district** — trains passing through pay nothing by
|
||||
themselves.
|
||||
|
||||
---
|
||||
|
||||
## 3. The shape of a Stage
|
||||
|
||||
Every Stage runs five phases in this order. Only two of them are your turn.
|
||||
|
||||
| # | Phase | What happens |
|
||||
| --- | --- | --- |
|
||||
| 1 | **Local Operations** | **Your turn.** Choose ONE of three things (below). Each player in turn, starting with the Superintendent and working eastward. |
|
||||
| 2 | **New Train** | Trains due this Stage are made up from the Division Yard. The table takes turns adding one car each. |
|
||||
| 3 | **Mainline** | Automatic. Trains move, lowest number first. This is where clearance rulings and collisions happen. |
|
||||
| 4 | **Cargo** | **Your turn.** Your Laborers and Porters do their work — the loading, unloading, boarding and detraining that actually pays. |
|
||||
| 5 | **Supervisor Shift** | Automatic. Laborers and Porters refresh; expedited trains depart. Every third Stage the Fedora passes. |
|
||||
|
||||
### Local Operations: you get exactly one of these
|
||||
|
||||
- **Switch** — take a crew and shunt. **Six Moves** (five at night under Reduced Visibility). This is
|
||||
how cars physically get from the yard to an industry and back. Cars you run over are coupled up
|
||||
automatically, so plan the route.
|
||||
- **Draw** — take a card, then play and/or discard. This is how your district gets built: track,
|
||||
industries, Office upgrades, modifiers, train cards.
|
||||
- **Freight Agent** — one clerical act: stock a green outbound box, clear a red inbound box, or
|
||||
unjam a facility.
|
||||
|
||||
**You cannot do two of them in one Stage.** Choosing is most of the game. A Stage spent drawing is a
|
||||
Stage not spent switching.
|
||||
|
||||
> Passengers are **not** the Freight Agent's job. They board and get off in the **Cargo** phase, with
|
||||
> a **Porter**. Reaching for the wrong role and finding nothing there is the single most common new
|
||||
> player mistake, so each role now says on screen what it is for.
|
||||
|
||||
---
|
||||
|
||||
## 4. The screen
|
||||
|
||||
Left column, top to bottom:
|
||||
|
||||
- **The Division — west to east.** The shared main line: every Office and the Mainline cards between
|
||||
them, with trains drawn where they are. **West is always on the left**, and a train's engine is
|
||||
drawn as an arrow (◀ or ▶) showing which way it points.
|
||||
- **Your Office Area.** Your own grid of track cards. This is where switching happens. It folds away
|
||||
outside the phases that change it, unless you pin it open.
|
||||
- **History.** What has happened, most recent first.
|
||||
|
||||
Right column:
|
||||
|
||||
- **Your Move** — the buttons. If it is not your turn this is empty, and the board tells you who is
|
||||
acting.
|
||||
- **Cards in My Hand**, and the **Department decks** — three face-up discard piles anyone may draw
|
||||
the top of.
|
||||
- **The Yards.** The **Division Yard** is the live supply of cars. The **Classification Yard** is
|
||||
where used cars go, and it comes back **only when the Division Yard runs completely bare**.
|
||||
- **Timetable** — who is due out and when.
|
||||
- **Blocked — why nothing is moving.** *Read this panel.* When something will not work, this is
|
||||
where the game explains why, in rules terms.
|
||||
- **Facilities** — the load pipelines at each industry.
|
||||
|
||||
Across the top: your Revenue, the target, the Day and Stage, collisions, and the game code.
|
||||
|
||||
**When other players are acting**, their turns are replayed on your board a step at a time rather
|
||||
than arriving already rearranged, with a `[N behind]` counter, **Pause** and **Skip**. Your own moves
|
||||
are not replayed at you — they are already on your screen.
|
||||
|
||||
---
|
||||
|
||||
## 5. Your first twenty minutes
|
||||
|
||||
Play a **solitaire** game first. It needs no server and nobody else, and it is the same rules.
|
||||
|
||||
1. Open the site, choose **Play solitaire**, accept the defaults, **Deal**.
|
||||
2. **Stage 1 — Draw.** You start on a **Whistle Post** with almost nothing. Play a track card or two
|
||||
to extend your Running Track, and get a **Depot** down as soon as one appears: it is the upgrade
|
||||
that makes you a passenger facility and gives you a second A/D track.
|
||||
3. **Build one industry** on a stub off the main — not on the Running Track itself, which the game
|
||||
will not allow.
|
||||
4. **Play a train card** when you get one. It rolls onto the timetable and then runs at that Stage
|
||||
**every Day**.
|
||||
5. **When a train arrives at your Office**, switch cars to it or from it, and do the paying work in
|
||||
the **Cargo** phase.
|
||||
6. Watch the **Blocked** panel whenever you are stuck. It is usually one missing thing: no empty car
|
||||
spotted, no loaded car in the yard, a full box, a locked industry track.
|
||||
|
||||
Then play a Day or two of multiplayer with bots filling the other seats, to see the table take turns.
|
||||
|
||||
---
|
||||
|
||||
## 6. Things that surprise new players
|
||||
|
||||
- **A turnout cannot be stopped on.** You may run through it; you may not end a Move there.
|
||||
- **Coupling is mandatory.** Run over a standing car and you take it, whether or not you wanted it.
|
||||
- **An industry track locks while a load is on its MEN | AT | WORK boxes.** No train can enter,
|
||||
cross or work there until it clears.
|
||||
- **A modifier adds a BOX, never room for a car.** It raises how much work an industry can hold, not
|
||||
how much rail it has.
|
||||
- **Nothing may be built outside your Limits** — track, industries and modifiers alike. Your district
|
||||
ends at its sign, and the sign moves outward as your Running Track grows.
|
||||
- **A train must be made up to leave.** Engine at one end; if it has a caboose, the caboose at the
|
||||
far end. A train shunted out of shape sits at your Office until you fix it — a **Small Yard** will
|
||||
re-order a consist for one Move, and each option tells you whether the result can leave.
|
||||
- **Passengers may dry up completely.** Coaches move one way — boarding sends the emptied coach to
|
||||
the Classification Yard, and it only comes back when the Division Yard is bare, which may never
|
||||
happen. When it does, the yard panel warns you and passenger trains are made up empty. **This is
|
||||
the rules working as designed**, not a bug; report how it felt, not that it happened.
|
||||
- **Expedited trains leave the same Stage they arrived**, after the Cargo phase. Ordinary ones wait.
|
||||
|
||||
---
|
||||
|
||||
## 7. What to report
|
||||
|
||||
Most useful, in order:
|
||||
|
||||
1. **What you expected versus what happened**, with the Day and Stage. "Day 2 Stage 9, train 5 had
|
||||
no coaches" is worth more than "passengers seem broken".
|
||||
2. **Save the game** (the **Save replay** button) and send the file. A save is the seed and the moves
|
||||
made, so it replays exactly and the bug can be looked at directly.
|
||||
3. **Anything the screen did not explain.** If you had to guess a rule, that is a finding even when
|
||||
the game was right.
|
||||
4. **Anything you went looking for and could not find.**
|
||||
|
||||
Bugs go to the tracker; anything unclear in this guide is also worth saying.
|
||||
|
||||
---
|
||||
|
||||
## 8. Where to read more
|
||||
|
||||
| For | Read |
|
||||
| --- | --- |
|
||||
| The rules in full, with the FAQ | [Rules](StationMaster-Rules-v0.4.5.md) |
|
||||
| Every card, generated from the code | [`rules/as-built.md`](rules/as-built.md) |
|
||||
| How the Home Office deck is dealt and played | [Home deck](StationMaster-Home-Deck-v0.4.5.md) |
|
||||
| The Mainline cards and what they do to a train | [Mainline deck](StationMaster-Mainline-Deck-v0.4.5.md) |
|
||||
| Rolling stock, yards, trays, the Fedora | [Components](StationMaster-Components-v0.4.5.md) |
|
||||
@@ -1,7 +1,15 @@
|
||||
# Station Master — Rules
|
||||
|
||||
**First-draft rules reference for v0.4.5**
|
||||
**Authority:** observed v0.4.5 code paths and tests. Where a card face, prototype document, and executable behavior differ, this document reports executable behavior and marks unimplemented material.
|
||||
**Describes the game as built at v0.8.0.16** (2026-09-20). Previously stamped "v0.4.5", the
|
||||
prototype rules edition this reference was first written against; the filename keeps that stamp
|
||||
because `src/`, `CHANGELOG.md` and `docs/rules/` all cite this file by name.
|
||||
|
||||
**Authority:** observed code paths and tests. Where a card face, a prototype document and executable
|
||||
behaviour differ, this document reports **executable behaviour** and marks unimplemented material.
|
||||
Per-card numbers are not repeated here — [`rules/as-built.md`](rules/as-built.md) is generated from
|
||||
`src/engine/content.ts` and is the table of record.
|
||||
|
||||
**New to the game? Start with the [Quickstart](StationMaster-Quickstart.md).**
|
||||
|
||||
## 1. Overview and background
|
||||
|
||||
@@ -19,11 +27,14 @@ This book is divided as follows:
|
||||
6. the implemented multiplayer/engine status; and
|
||||
7. FAQs and implementation limits.
|
||||
|
||||
The companion references are [Mainline deck](StationMaster-Mainline-Deck-v0.4.5.md), [Home deck](StationMaster-Home-Deck-v0.4.5.md), and [components](StationMaster-Components-v0.4.5.md).
|
||||
The companion references are the [Quickstart](StationMaster-Quickstart.md) for a new player,
|
||||
[Mainline deck](StationMaster-Mainline-Deck-v0.4.5.md), [Home deck](StationMaster-Home-Deck-v0.4.5.md),
|
||||
[components](StationMaster-Components-v0.4.5.md), and the generated per-card table
|
||||
[`rules/as-built.md`](rules/as-built.md).
|
||||
|
||||
## 2. Definitions
|
||||
|
||||
| Term | Meaning in v0.4.5 |
|
||||
| Term | Meaning |
|
||||
| --- | --- |
|
||||
| A/D track | An Office arrival/departure capacity. A Whistle Post has 1; Depot, Station, and Terminal have 2, 3, and 4. |
|
||||
| Card location | A square in an Office Area grid. A train may end a switching Move only on Operational Rail. |
|
||||
@@ -43,7 +54,7 @@ The companion references are [Mainline deck](StationMaster-Mainline-Deck-v0.4.5.
|
||||
| Operational Rail | A card on which a train may finish a Move. Turnouts are pass-through only; a locked industry is not usable at all. |
|
||||
| Running Track | The east–west track between an Office Area’s Limits, including its Office. |
|
||||
| Secondary Track | All local rail inside the Limits that is not Running Track. |
|
||||
| Stage | One of twelve turns in a Day. Its phases are Local Operations, New Train, Mainline, Load/Unload, and Shift Change. |
|
||||
| Stage | One of twelve turns in a Day. Its phases are Local Operations, New Train, Mainline, Load/Unload and Shift Change. **On screen the last two are labelled "Cargo" and "Supervisor Shift"** — same phases, the names the interface uses. |
|
||||
| Subdivision | Mainline between Division Points or Control Points. Clearance checks look through the whole next Subdivision. |
|
||||
| Superintendent | The player with the Fedora. The role decides same-direction clearances and rotates every three Stages. |
|
||||
| Timetabled train | A numbered train card scheduled to one of the 12 Stage slots, then due at that slot each Day. Odd numbers go west; even numbers go east. |
|
||||
@@ -52,11 +63,18 @@ The companion references are [Mainline deck](StationMaster-Mainline-Deck-v0.4.5.
|
||||
|
||||
### 3.1 Starting a new game in the shipped client
|
||||
|
||||
The v0.4.5 browser page creates a **one-player solitaire Standard game**. Select **New game**, then choose:
|
||||
The front page has three doors: **Play multiplayer**, **Play solitaire**, and **Browse replays**.
|
||||
|
||||
**Solitaire** runs entirely in your own browser and needs nothing from the server. Select **New
|
||||
game**, then choose:
|
||||
|
||||
1. a numeric seed, or leave it blank for a fresh browser-generated seed;
|
||||
2. a starting hand;
|
||||
3. passenger, freight, and train-transit Revenue rates.
|
||||
3. passenger, freight, and train-transit Revenue rates;
|
||||
4. the number of Days, and the optional rules.
|
||||
|
||||
**Multiplayer** goes to the lobby — see §3.5. It needs the server, because the game is authoritative
|
||||
there rather than in any one browser.
|
||||
|
||||
The browser writes those choices into the URL. A particular game is defined by the **seed plus these house rules**, not the seed alone.
|
||||
|
||||
@@ -87,7 +105,7 @@ In a multi-player engine game, every player receives two seeded D12 rolls:
|
||||
- The **division roll** orders seats from low west to high east; equal results put the lower player index farther east.
|
||||
- The **Superintendent roll** gives the initial Fedora to the first player tied for highest.
|
||||
|
||||
The opening deal starts at the Superintendent’s seat and proceeds left (eastward in the engine’s seat ordering).
|
||||
The opening deal starts at the Superintendent’s seat and proceeds **eastward** — increasing seat index, which is how the Division map draws the table.
|
||||
|
||||
### 3.3 Saving, resuming, and replaying solitaire
|
||||
|
||||
@@ -97,25 +115,61 @@ The browser also stores the current local game and resumes it automatically when
|
||||
|
||||
**Undo is solitaire-only.** It removes the final accepted intent and rebuilds the game from the earlier history. Random outcomes are not rerolled: replaying the same action consumes the same seeded result. Undo can therefore change the player’s decision after seeing an outcome, but cannot fish for a different timetable die roll.
|
||||
|
||||
### 3.4 Engine game modes and endings
|
||||
### 3.4 Game modes and endings
|
||||
|
||||
The engine defines Solitaire, Competitive, and Co-op modes. The browser exposes only Solitaire.
|
||||
Three modes: **Solitaire**, **Competitive** and **Co-op**. All three are playable.
|
||||
|
||||
| Length | Target | Days |
|
||||
| --- | ---: | ---: |
|
||||
| Short | 10 | 3 |
|
||||
| Standard | 20 | 5 |
|
||||
| Campaign | 45 | 10 |
|
||||
**Length is a free `days` count**, not a preset. The old `short`/`standard`/`campaign` presets
|
||||
carried a `target` and were dropped in 2026-08; they survive only as a convenience argument for the
|
||||
simulation tooling, resolving to 3, 5 and 10 Days. The default is 5.
|
||||
|
||||
With `firstToTarget`, competitive mode ends when any individual reaches the target; co-op uses target × player count and total Revenue. With `highestAfterDays`, competitive mode requires the table’s combined Revenue to reach `3 × players × days`; otherwise everyone loses. If that floor is met, the highest individual score wins. Solitaire and co-op win only if their score reaches their target at the end of the length.
|
||||
**How a game ends and who wins:**
|
||||
|
||||
Competitive mode also ends in a collective loss after three collisions in one Day.
|
||||
1. **The timetable runs out** at the end of the last Day. Then:
|
||||
2. **The combined Revenue floor** is checked first — `3 × players × days`. Fall short and
|
||||
**everybody loses**, whatever anyone individually scored.
|
||||
3. **Co-op** wins as a table if the floor is met.
|
||||
4. **Competitive** is won by the **highest individual Revenue** once the floor is met.
|
||||
5. **Collisions end it early.** Breaching the per-Day or total collision limit ends play at once in a
|
||||
collective loss — the railroad has been declared unsafe.
|
||||
|
||||
The configuration contains four optional-rule flags. Only two have engine effects: **Reduced Visibility** gives five rather than six switching Moves in Stages 1, 2, 3, 11, and 12; **Emergency Toolbox** initially sets the Red Flags hand-limit status, allowing four cards. Sister Trains and Employee Rotation are represented in configuration/state design but are not executed by v0.4.5.
|
||||
**Extended play (Gitea#11).** Both days-based endings — running out of timetable, and closing short
|
||||
of the Revenue floor — offer **another Day**, because they are the same event seen twice: the last
|
||||
Day ended, and this is what the books say. A collision ending is **not** extendable, and neither is a
|
||||
collision breach during an extended Day. The official result is frozen when the timetable first ran
|
||||
out, so a railroad declared unsafe on Day 9 does not retract who won on Day 5.
|
||||
|
||||
### 3.5 Multiplayer setup status
|
||||
**Three optional rules, all implemented:**
|
||||
|
||||
There is no multiplayer lobby, room creation flow, remote server, invitation flow, or network session in v0.4.5. The engine can be called with multiple player names (and rejects a Solitaire configuration with more than one), but the delivered page always calls it for one local player. See section 6.
|
||||
| Rule | Effect |
|
||||
| --- | --- |
|
||||
| Reduced Visibility | Five switching Moves instead of six, in Stages 1, 2, 3, 11 and 12 — the night Stages. |
|
||||
| Employee Rotation | Every player moves one chair at the end of each Day. Revenue and the Fedora travel with the player; the Office Areas stay with the seats. |
|
||||
| Emergency Toolbox | Starts every player holding the Red Flags status, so the hand limit opens at four. |
|
||||
|
||||
(There is no "Sister Trains" flag. It appeared in an earlier draft of this document and never in the
|
||||
configuration.)
|
||||
|
||||
### 3.5 Multiplayer setup
|
||||
|
||||
Multiplayer is delivered and is what this package is for. The flow:
|
||||
|
||||
1. **Create or join.** The host creates a game and gets a **game code**; everyone else joins with
|
||||
that code. Seats fill as people arrive, and any seat left empty can be **filled with a bot**.
|
||||
2. **Start.** Once the host starts, that same page is where every player plays their turns and
|
||||
watches the table. There is nothing else to open.
|
||||
3. **The server is authoritative.** The game lives on the server, not in a browser: it survives a
|
||||
page reload, a browser restart and a service update, replaying its intent history to get back to
|
||||
where it was.
|
||||
4. **Your seat is a token in YOUR browser**, scoped to the origin you joined at. A reload finds it
|
||||
and puts you straight back. Clearing site data, a private window, or a different browser does not:
|
||||
the seat is still yours and still on the server, but that browser can no longer prove it is you.
|
||||
An administrator can mint a **single-use recovery link** (StartOS action **Restore a Seat**) that
|
||||
trades a code for the token and expires in 30 minutes.
|
||||
5. **Watching the table.** Other players' turns arrive as an ordered replay rather than as a board
|
||||
that has silently rearranged itself, with a `[N behind]` counter, Pause and Skip.
|
||||
|
||||
Solitaire needs none of this and runs with the page alone.
|
||||
|
||||
## 4. Basic game mechanics
|
||||
|
||||
@@ -124,13 +178,16 @@ There is no multiplayer lobby, room creation flow, remote server, invitation flo
|
||||
Each of 12 Stages follows this sequence:
|
||||
|
||||
```text
|
||||
1. Local Operations — each player, starting with the Superintendent and proceeding left
|
||||
2. New Train — make up due timetabled trains, then queued second sections and Extras
|
||||
3. Mainline — automatic train movement in numeric order
|
||||
4. Load/Unload — each player, starting with the Superintendent and proceeding left
|
||||
5. Shift Change — expedited departures, clocks/workers, and possibly the Fedora
|
||||
1. Local Operations — each player, starting with the Superintendent and proceeding eastward
|
||||
2. New Train — make up due timetabled trains, then queued second sections and Extras
|
||||
3. Mainline — automatic train movement in numeric order
|
||||
4. Load/Unload — each player, same order. On screen: "Cargo"
|
||||
5. Shift Change — expedited departures, workers. On screen: "Supervisor Shift"
|
||||
```
|
||||
|
||||
**"Proceeding left" is seat order, west to east**, which is how the Division map draws it — the
|
||||
screen says "eastward" for that reason, because a table has no shared left.
|
||||
|
||||
At a Shift Change, Laborers and Porters reset. The Fedora moves after Stages 3, 6, 9, and 12. At Day end, dispatch-device use resets, collision count resets, the Day and Stage roll over, and victory is checked.
|
||||
|
||||
### 4.2 Local Operations: choose one option
|
||||
@@ -139,13 +196,19 @@ On a player’s Local Operations turn, choose exactly one available option.
|
||||
|
||||
**Switch.** Select any Crew Tray currently in that player’s Office Area. It gets six Moves, or five under Reduced Visibility on the listed night Stages. A Move travels any connected distance in one direction and must end on Operational Rail. Reversing is a separate Move. A train may pass through a turnout but cannot stop on it. It may not share or pass through another train except through an Office with a free A/D track.
|
||||
|
||||
Standing cars couple automatically when the train reaches them; it may not pass them, and the resulting consist may not exceed four cars. Coupling forward places cars ahead of the engine; coupling while backing places them behind it. Setting out cars does not spend a Move, but the cut must come from an outer end of the consist and may not be left on the Office. A Small Yard can reorder a consist for one Move. Flying Switch spends one Move to roll a tail cut into a connected Freight Facility.
|
||||
Standing cars couple automatically when the train reaches them; it may not pass them, and the resulting consist may not exceed four cars. Coupling forward places cars ahead of the engine; coupling while backing places them behind it. Setting out cars does not spend a Move, but the cut must come from an outer end of the consist and may not be left on the Office. A **Small Yard** re-orders a consist for one Move, and since v0.8.0.14 may also place cars **ahead
|
||||
of the engine** — which is how a cut is set up to be shoved into a facing industry. Each option on
|
||||
the menu shows the train it would build, laid out west to east as the board draws it, and says
|
||||
whether the result may leave the Office or would be held there. Flying Switch spends one Move to
|
||||
roll a tail cut into a connected Freight Facility.
|
||||
|
||||
**Draw.** Take one card from the face-down Home Office or the exposed top of one Department pile. During this option, play eligible cards and/or discard cards to Department piles, then finish at the hand limit. Track, facilities, offices, modifiers, enhancements, and train cards have the placement or scheduling rules in the deck references. Mainline modifiers are played from this option as well.
|
||||
|
||||
**Freight Agent.** Make one of these operations, then the turn ends: stock one green outbound box from a matching loaded Division Yard car; clear one red inbound box to the Classification Yard; unjam one outbound, inbound, or MEN | AT | WORK load to the Classification Yard; or explicitly end without acting. Freight can be stocked only when an unclaimed empty matching car is already spotted at that industry. Passengers may wait in a green Office box without a train present.
|
||||
|
||||
Implementation note: `card.discard` is accepted by the v0.4.5 engine during Local Operations without checking that Draw was chosen. This unusual implementation behavior is not a separate published turn option.
|
||||
Implementation note, still true at v0.8.0.16: `card.discard` is accepted during Local Operations
|
||||
without checking that the Draw option was chosen — unlike `card.play`, which does check. This is an
|
||||
implementation quirk rather than a fourth published turn option.
|
||||
|
||||
### 4.3 Track, switching, and local safety
|
||||
|
||||
@@ -159,18 +222,36 @@ Train-card restrictions also apply while switching. No-switching trains cannot m
|
||||
|
||||
At the Stage shown on the timetable, the engine makes up the matching timetabled train if a Crew Tray is free. It starts at the Division Point appropriate to its direction. A train may receive matching loaded or empty cars from the Division Yard until its listed maximum consist is reached or no suitable car remains. It is permitted to leave under-strength.
|
||||
|
||||
A Second Section order on the due train creates another identical timetabled train behind it when a free tray is available. A played Extra is made up after timetabled trains and Second Sections when a tray is free; v0.4.5 automatically sends every Extra east from the Western Division Point.
|
||||
A Second Section order on the due train creates another identical timetabled train behind it when a
|
||||
free tray is available. A played Extra is made up after timetabled trains and Second Sections when a
|
||||
tray is free, and **the player who played the card chooses where it starts** — either Division
|
||||
Point, the Interchange, or an Office, according to the `extraStart` house rule — and loads it as they
|
||||
choose rather than going round the table.
|
||||
|
||||
A timetabled train's consist is built by the table: **starting with the Superintendent and working
|
||||
eastward, each player adds ONE car**, going round again until the train is full or the Division Yard
|
||||
holds nothing it can take.
|
||||
|
||||
**When the yard can supply nothing, the game says so.** The round is skipped — there is no point
|
||||
asking for a car that cannot be given — and the train is reported as made up short, naming what its
|
||||
card wanted, how many such cars are waiting in the Classification Yard, and how far the Division Yard
|
||||
is from bare. See §4.6 for why that happens to coaches in particular.
|
||||
|
||||
### 4.5 Mainline movement and Office arrival
|
||||
|
||||
Mainline movement is automatic and processes lower train numbers first; a timetabled train outranks an Extra with the same number. A train at a Division Point, at an Office A/D track, or already crossing a Mainline card attempts its applicable movement.
|
||||
|
||||
Crossing time comes from the Mainline card and train speed. On a normal card, a train must check the entire next Subdivision before entering it:
|
||||
**Crossing time is the card's REGIONS**, one per Stage — not its printed mph, which is scenery, and
|
||||
not the train's Fast/Slow rating, which only **Hilly** reads. See the
|
||||
[Mainline deck](StationMaster-Mainline-Deck-v0.4.5.md) reference for what moves a train's entry
|
||||
point. On a normal card, a train must check the entire next Subdivision before entering it:
|
||||
|
||||
- an opposing train normally blocks entry;
|
||||
- a following same-direction train asks the Superintendent to allow or deny clearance;
|
||||
- Red Flags or ABS Signals hold the follower automatically; and
|
||||
- passing cards allow entry without this occupancy check.
|
||||
- **Double Track** — the one card two trains may stand on — allows entry without that check. The
|
||||
**Uncontrolled Siding** is not a passing card: a train arriving to find it occupied takes the
|
||||
siding a region behind, which costs it the extra Stage instead of a collision.
|
||||
|
||||
An Office arrival normally takes a free A/D track. If the Office is full, the inbound train collides and the local Office player loses 5 Revenue; Interlocking instead holds it at the Limits. A coachless inbound train may divert to a Yard Office. Cars fouling the Running Track at the Office also cause a collision.
|
||||
|
||||
@@ -185,7 +266,22 @@ Passenger work occurs during Load/Unload, at a Depot, Station, or Terminal. Each
|
||||
- **Board:** replace an empty coach on an eligible train at the Office with a loaded coach from a green outbound box. The removed empty coach goes to the Classification Yard. Earn configured passenger Revenue.
|
||||
- **Detrain:** replace a loaded coach on an eligible train with an empty coach from the Division Yard, placing the loaded coach into an available red inbound box. Earn configured passenger Revenue.
|
||||
|
||||
Crack Limited trains permit passenger work at Terminals only. Military Train and Director’s Private Car permit no passenger work. A Whistle Post has no Porters.
|
||||
Crack Limited trains permit passenger work at Terminals only. Military Train and Director's Private
|
||||
Car permit no passenger work. A Whistle Post has no Porters.
|
||||
|
||||
> **Coaches travel one way, and it is worth knowing before you plan around passengers.** Boarding
|
||||
> sends the emptied coach to the **Classification** Yard; detraining draws a fresh empty out of the
|
||||
> **Division** Yard; and §2.2 returns the Classification Yard only when the Division Yard runs
|
||||
> completely bare. Measured over one three-Day game: sixteen coaches in the Division Yard at setup,
|
||||
> **none from Day 2 Stage 8 onward**, fifteen piled in Classification while the Division Yard held
|
||||
> steady at 46–47 freight cars and stopped draining — so the refill never fired and no passenger
|
||||
> could board or detrain anywhere for the rest of the game, while trains whose cards call for coaches
|
||||
> were made up empty.
|
||||
>
|
||||
> **This is the rules working as printed and the ruling is that it stands** (Jesse, 2026-09-17, the
|
||||
> same ruling Gitea#2 got: running out is part of the game). What changed is that the game now says
|
||||
> it — the yard panel warns while the shortage lasts, and a train made up short reports why. Whether
|
||||
> the ratchet should be broken is open as TODO #108, to be decided on a second game's evidence.
|
||||
|
||||
### 4.7 Freight work
|
||||
|
||||
@@ -197,43 +293,77 @@ For an **inbound unload**, a matching loaded car must be spotted at an inbound-c
|
||||
|
||||
## 5. Solitaire
|
||||
|
||||
The implemented game is solitaire: one named player, one Office Area, local browser execution, Standard length, highest-after-days victory, and the default house rules unless changed in New Game. The game has no AI opponent. “Multiplayer” gameplay does not occur locally by simulating other players.
|
||||
Solitaire is one named player and one Office Area, running entirely in the browser with no server.
|
||||
It is **not** the only implemented game any more — see §6 — but it is the one that needs nothing but
|
||||
the page.
|
||||
|
||||
Solitaire-specific features are:
|
||||
|
||||
- a local browser save, automatic resume, and JSON download/load;
|
||||
- unlimited step-by-step Undo back through accepted action history; and
|
||||
- **unlimited step-by-step Undo** back through accepted action history — multiplayer has none,
|
||||
because a shared game cannot be rewound under the other players; and
|
||||
- a seed/rules URL suitable for sharing or reproducing a game.
|
||||
|
||||
Automatic phases get a visible beat in solitaire too, so the board plays its own moves out rather
|
||||
than jumping.
|
||||
|
||||
The active deck removes all 22 opponent-directed cards. Therefore, the defensive cards whose only purpose is to answer them (Facing Point Locks and Water Column) can be placed but have no opportunity to fire; Overpass has no effect at all. The game still includes shared-rail mechanics such as Mainline clearance, but with one player no other player can occupy the Division.
|
||||
|
||||
To win the default game, finish Day 5 with at least 20 Revenue. A result below 20 is a loss. Train-transit Revenue defaults to zero, so the default score must principally come from passenger and freight work.
|
||||
To win the default one-player game, finish Day 5 having met the combined Revenue floor —
|
||||
`3 × players × days`, which at one player over five Days is **15**. Below it is a loss, and the game
|
||||
offers you another Day rather than simply ending. Train-transit Revenue defaults to zero, so the
|
||||
score has to come principally from passenger and freight work.
|
||||
|
||||
## 6. Multiplayer
|
||||
|
||||
### 6.1 What the engine supports
|
||||
|
||||
The engine has player, seat, score, Office Area, Director/Division, phase-order, co-op, and competitive-mode data for multiple named players. It deals each player a hand, creates one Office Area per seat, starts acting order at the Superintendent and proceeds left, and models the following multiplayer-specific outcomes:
|
||||
The rules engine has always supported multiple named players; since v0.7 the lobby, server and
|
||||
client around it are delivered too, so this section now describes a game people actually play. The
|
||||
engine has player, seat, score, Office Area, Division, phase-order, co-op and competitive-mode data
|
||||
for multiple named players. It deals each player a hand, creates one Office Area per seat, starts acting order at the Superintendent and proceeds eastward, and models the following multiplayer-specific outcomes:
|
||||
|
||||
- the D12 seating and Superintendent rolls described in section 3;
|
||||
- individual Revenue in competitive play and shared total Revenue in co-op;
|
||||
- a competitive collective loss after three collisions in one Day;
|
||||
- a collective Revenue floor for competitive highest-after-days games; and
|
||||
- a collective loss on breaching the collision limits;
|
||||
- the combined Revenue floor, `3 × players × days`; and
|
||||
- train-transit Revenue awarded to every player, if that revenue setting is nonzero.
|
||||
|
||||
The engine’s setup checks only that there is at least one player and that Solitaire has exactly one player. It does not enforce a maximum player count, although the test and design material exercise two through four players.
|
||||
|
||||
### 6.2 What is not delivered in v0.4.5
|
||||
### 6.2 What IS delivered
|
||||
|
||||
There is no implemented multiplayer game setup for end users: no server, lobby, invitation, room code, player join flow, authoritative remote state, or remote Session. The browser page creates a local single-player session only. Accordingly, there is no supported procedure for resuming a multiplayer game, and no multiplayer Undo.
|
||||
Everything in §3.5: a lobby with game codes, seating, bots filling empty chairs, an authoritative
|
||||
server that survives restarts and updates by replaying its intent history, per-seat reconnection, an
|
||||
administrator's single-use seat-recovery link, and an ordered replay of other players' turns on each
|
||||
player's own screen.
|
||||
|
||||
The 12 space-use cards and 10 action cards are not dealt in competitive or co-op either. The engine rejects attempts to play either category. Thus, **no card can currently be played on another player**. This includes Derail, Broken Coupler, Railroad Crossing, score-penalty cards, Vandalism, Hotbox, Outlawed, and all table-space cards. Facing Point Locks and Water Column are implemented only as dormant defences for these unavailable effects.
|
||||
The package also exposes administrative **actions** on StartOS — list games in progress, get the
|
||||
join secret, manage a game, restore a seat — documented in the wrapper repository rather than here.
|
||||
|
||||
### 6.3 Difference from solitaire, if a multi-player engine session is created
|
||||
### 6.3 What is NOT delivered
|
||||
|
||||
Players have separate local districts, hands, and scores, but they share the Home Office deck, Department piles, yards, timetable, Mainline, and traffic consequences. Turn order is sequential; one current actor acts at a time. The Superintendent role is attached to a player while Offices are attached to fixed seats. Employee Rotation is not active, so players do not actually change seats in v0.4.5.
|
||||
**No card may be played at another player.** The 12 space-use and 10 action cards are excluded from
|
||||
every dealt deck in every mode, and `check` rejects playing one. That includes Derail, Broken
|
||||
Coupler, Railroad Crossing, the score-penalty cards, Vandalism, Hotbox, Outlawed and all
|
||||
table-space cards. Facing Point Locks and Water Column exist only as dormant defences against
|
||||
effects nothing can currently cause, and Overpass has no effect at all.
|
||||
|
||||
The deck is still 213 cards and still excludes opponent-directed content. Therefore multi-player engine mode changes shared traffic, scores, turns, and win/loss evaluation—not card attacks or a remote user experience.
|
||||
**No multiplayer Undo.** A shared game cannot be rewound under the other players.
|
||||
|
||||
### 6.4 How multiplayer differs from solitaire
|
||||
|
||||
Players have separate districts, hands and scores, and share the Home Office deck, the Department
|
||||
piles, the yards, the timetable, the Mainline and every traffic consequence. One player acts at a
|
||||
time.
|
||||
|
||||
**A seat is not a player**, and the distinction is load-bearing. Offices belong to seats; Revenue,
|
||||
hands, the Fedora and identity belong to players. With **Employee Rotation** on, players move one
|
||||
chair at the end of each Day and take their Revenue and the Fedora with them, while the districts
|
||||
stay where they are.
|
||||
|
||||
So multiplayer changes shared traffic, scores, turn order and how the game is won or lost — not card
|
||||
attacks, which do not exist in any mode.
|
||||
|
||||
## 7. Frequently asked questions
|
||||
|
||||
@@ -271,20 +401,42 @@ It may be on Secondary Track rather than the Office, or be badly made up: its en
|
||||
|
||||
### Why did an expedited train leave after passenger/freight work?
|
||||
|
||||
Expedite means it departs in the Stage it arrived, but v0.4.5 waits until Shift Change so it remains present for that Stage’s Load/Unload phase.
|
||||
Expedite means it departs in the Stage it arrived, but the departure waits until Shift Change so the
|
||||
train is still present for that Stage's Load/Unload phase.
|
||||
|
||||
### Can I choose the direction of an Extra or a Heavy Grade?
|
||||
|
||||
Not in v0.4.5. The engine launches Extras eastbound from the Western Division Point. Heavy Grade orientation is seeded automatically at setup.
|
||||
**An Extra, yes** — the player who played the card chooses where it starts and which way it runs, and
|
||||
loads it as they choose (v0.6.2). **A Heavy Grade, no**: orientation is rolled from the seed. That is
|
||||
a decision rather than a gap — the card sits between two districts and belongs to neither, so handing
|
||||
the choice to one neighbour would advantage them permanently. See the Mainline deck reference.
|
||||
|
||||
### Can I use an Interchange to reorder a train?
|
||||
|
||||
No. The card is catalogued with a sorting concept, but there is no implemented Interchange sorting action. Small Yard is the available local sorting mechanism.
|
||||
**No, and the card used to claim otherwise.** Its printed car-sorting has never been implemented; the
|
||||
description was corrected on 2026-09-20 to stop advertising it. What the Interchange actually offers
|
||||
is the one Mainline card with a yard, so an **Extra may be made up and started there**. Re-ordering a
|
||||
consist is done at a **Small Yard** in a district.
|
||||
|
||||
### Can I play attack cards on another player?
|
||||
|
||||
No. All action and space-use cards are excluded from every dealt deck in v0.4.5 and their play is rejected.
|
||||
No. All action and space-use cards are excluded from every dealt deck in every mode, and playing one
|
||||
is rejected.
|
||||
|
||||
### Is multiplayer playable?
|
||||
|
||||
No. Multi-player state and rules-engine support exist, but the lobby, server, remote client, and opponent-directed card mechanics are not implemented.
|
||||
**Yes.** Lobby, game codes, seating, bots, an authoritative server that survives restarts, per-seat
|
||||
reconnection and a replayed view of everyone else's turns are all delivered — see §3.5 and §6. The
|
||||
opponent-directed cards remain unimplemented in every mode, so there are still no card attacks.
|
||||
|
||||
### Why did my passenger train arrive with no coaches?
|
||||
|
||||
Almost certainly the coach ratchet in §4.6: every coach has ended up in the Classification Yard,
|
||||
which comes back only when the Division Yard runs completely bare. The yard panel warns when this
|
||||
has happened, and a train made up short says so in the log.
|
||||
|
||||
### Why is every option in the Small Yard marked "HELD at the Office"?
|
||||
|
||||
Because that train is **already made up**, so every re-order on offer would break it — most often by
|
||||
moving the caboose off the rear, which §8.2 will not let a train depart with. If the train is *not*
|
||||
currently fit to run, at least one option will be marked "MADE UP, ready to leave".
|
||||
|
||||
+35
-21
@@ -20,13 +20,30 @@ These stay as-is. Everything below is derived from them.
|
||||
> placeholder for exactly this material, and the balance measurements in Gap 12 were taken against a
|
||||
> ruleset that does not match the design.
|
||||
|
||||
## For players and testers
|
||||
|
||||
Written to be handed to somebody who is about to play, rather than to somebody building the game.
|
||||
|
||||
| Document | What it is |
|
||||
| --- | --- |
|
||||
| [`StationMaster-Quickstart.md`](StationMaster-Quickstart.md) | **Start here if you have never played.** The point of the game, how a Stage runs, what is on screen, how you win, a first twenty minutes, and what to report. |
|
||||
| [`StationMaster-Rules-v0.4.5.md`](StationMaster-Rules-v0.4.5.md) | **The rules in full**, as the engine actually runs them, with a FAQ. |
|
||||
| [`rules/as-built.md`](rules/as-built.md) | **Every card, GENERATED from `src/engine/content.ts`** and checked by a test, so it cannot disagree with the game. The table of record for per-card facts. |
|
||||
| [`StationMaster-Home-Deck-v0.4.5.md`](StationMaster-Home-Deck-v0.4.5.md) | How the Home Office deck is dealt, drawn and played out. |
|
||||
| [`StationMaster-Mainline-Deck-v0.4.5.md`](StationMaster-Mainline-Deck-v0.4.5.md) | The Mainline cards, how the deck is dealt, and what a card does to a train crossing it. |
|
||||
| [`StationMaster-Components-v0.4.5.md`](StationMaster-Components-v0.4.5.md) | Rolling stock, the two yards, Crew Trays, the Fedora, the D12. |
|
||||
|
||||
The `v0.4.5` in four of those filenames is the **prototype rules edition they were first written
|
||||
against**, not the version they describe — each says at the top which build it is current to. The
|
||||
names are kept because `src/`, `CHANGELOG.md` and `docs/rules/` cite them.
|
||||
|
||||
## 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/card-reference.md`](rules/card-reference.md) | **⚠ SUPERSEDED** — an invented 52-card placeholder, kept for its economy summary and its history. For what is printed on every card, read [`rules/as-built.md`](rules/as-built.md), which is generated from the code. |
|
||||
| [`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. |
|
||||
@@ -49,23 +66,20 @@ must do.
|
||||
|
||||
## Current status
|
||||
|
||||
**v0.4.3.** 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.
|
||||
**v0.8.0.16.** Rules formalized, card faces specified, architecture documented, and the game
|
||||
playable **solitaire and multiplayer** in a browser against an authoritative server. 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 built.** The rules engine, the developer bot, the balance harness, the replay viewer, the
|
||||
playable page — and the server: lobby, game codes, seating, bots, per-seat reconnection, persistence
|
||||
by replaying the intent history, and an ordered replay of other players' turns on each player's own
|
||||
screen. It ships as a StartOS package. **999 fast tests and 35 simulation 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.
|
||||
**What is not.** The 22 opponent-directed cards — the Action and Space-use categories — are held out
|
||||
of every dealt deck until they have an implementation, along with the two defensive cards whose only
|
||||
purpose is to answer them. Real audio: everything the game plays is synthesised from oscillators.
|
||||
|
||||
**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
|
||||
@@ -85,17 +99,17 @@ on that. [`architecture/protocol.md`](architecture/protocol.md) §3 has the reas
|
||||
`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.
|
||||
per Day — but **inbound work bypasses them**, which is where the game's variance comes from.
|
||||
|
||||
**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.
|
||||
Running alongside, and independent of all of it: **print-and-play components.**
|
||||
[`rules/as-built.md`](rules/as-built.md) carries every card face as the game actually deals it, 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]`.
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ card prints are what it costs to cross. Where a train *enters* is what the rules
|
||||
train on Hilly, a Heavy Grade with Helpers, or a card whose back region is a siding rather than
|
||||
part of the road all change the entry point rather than the card's length.
|
||||
|
||||
| Card | Regions | Default entry | Fast / slow entry | Trains may pass | Sorts cars |
|
||||
| Card | Regions | Default entry | Fast / slow entry | Trains may pass | Extra may start |
|
||||
| --- | ---: | ---: | --- | :---: | :---: |
|
||||
| Plains | 1 | 0 | — | — | — |
|
||||
| Curves | 2 | 0 | — | — | — |
|
||||
@@ -92,7 +92,7 @@ the Division and are not dealt. What each card does, in the words the game uses
|
||||
- **Uncontrolled Siding** — 2 regions — one Stage each. · A train with the card to itself starts past the back region and is across in 1 Stage. · UNCONTROLLED SIDING — arrive to find a train already here and you take the siding, a region behind it. You are not in the same place, so you do not run into it; it costs you the extra Stage instead. · One train at a time — anything following has to wait for it to clear.
|
||||
- **Tunnel** — 2 regions — one Stage each. · 2 Stages for every train. · One train at a time — anything following has to wait for it to clear.
|
||||
- **Trestle** — 1 region — one Stage each. · 1 Stage for every train. · One train at a time — anything following has to wait for it to clear.
|
||||
- **Interchange** — 2 regions — one Stage each. · A train with the card to itself starts past the back region and is across in 1 Stage. · An Extra beginning its run here starts in the back region and takes the extra Stage. · One train at a time — anything following has to wait for it to clear. · Cars may be sorted into any new order here.
|
||||
- **Interchange** — 2 regions — one Stage each. · A train with the card to itself starts past the back region and is across in 1 Stage. · An Extra beginning its run here starts in the back region and takes the extra Stage. · One train at a time — anything following has to wait for it to clear. · This is the one Mainline card with a yard, so an Extra Train may be made up and started here. Its printed car-sorting is NOT implemented — a consist is re-ordered at a Small Yard in a district.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "station-master",
|
||||
"version": "0.8.0.14",
|
||||
"version": "0.8.0.16",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Station Master — a railroad operations game",
|
||||
|
||||
@@ -126,7 +126,7 @@ w('card prints are what it costs to cross. Where a train *enters* is what the ru
|
||||
w('train on Hilly, a Heavy Grade with Helpers, or a card whose back region is a siding rather than');
|
||||
w('part of the road all change the entry point rather than the card\'s length.');
|
||||
w();
|
||||
w('| Card | Regions | Default entry | Fast / slow entry | Trains may pass | Sorts cars |');
|
||||
w('| Card | Regions | Default entry | Fast / slow entry | Trains may pass | Extra may start |');
|
||||
w('| --- | ---: | ---: | --- | :---: | :---: |');
|
||||
for (const m of MAINLINE_PROFILES) {
|
||||
const ss = m.speedStarts ? `${m.speedStarts.fast} / ${m.speedStarts.slow}` : '—';
|
||||
|
||||
@@ -188,6 +188,27 @@ if (existsSync(imageSrc)) {
|
||||
for (const f of readdirSync(imageSrc)) copyFileSync(join(imageSrc, f), join(imageOut, f));
|
||||
}
|
||||
|
||||
/**
|
||||
* The Quickstart, published beside the game so a tester can reach it from the box.
|
||||
*
|
||||
* COPIED, NOT RE-WRITTEN. `docs/StationMaster-Quickstart.md` is the one copy; a hand-written HTML
|
||||
* twin would drift from it on the first edit, which is the whole lesson of TODO #15a and of the
|
||||
* 2026-09-20 documentation pass that found four references a month out of date.
|
||||
*
|
||||
* SERVED AS PLAIN TEXT for now, which is honest rather than good: tables render as pipes and the
|
||||
* links do not click. Rendering it into a styled page needs a small Markdown converter and is
|
||||
* filed as TODO #109 — this is the fifteen-minute version that gets the guide in front of testers
|
||||
* for this round rather than leaving them without one.
|
||||
*/
|
||||
const guideSrc = join(root, 'docs/StationMaster-Quickstart.md');
|
||||
if (existsSync(guideSrc)) {
|
||||
copyFileSync(guideSrc, join(dist, 'quickstart.md'));
|
||||
} else {
|
||||
// Loud rather than silent: a missing guide is a broken link on the splash page, and the build is
|
||||
// the only place that can still notice.
|
||||
console.error('WARNING: docs/StationMaster-Quickstart.md is missing — the splash link will 404');
|
||||
}
|
||||
|
||||
// A tiny note for whoever unzips this later and wonders what it needs.
|
||||
writeFileSync(
|
||||
join(dist, 'README.txt'),
|
||||
|
||||
+33
-2
@@ -589,7 +589,20 @@ export type MainlineProfile = {
|
||||
speedStarts?: { fast: number; slow: number };
|
||||
/** Double Track: "Trains may pass". */
|
||||
trainsMayPass: boolean;
|
||||
/** Interchange: "Sort cars in new order". */
|
||||
/**
|
||||
* Interchange only. The card prints "Sort cars in new order" — **and that is not what this flag
|
||||
* does**, which is why it is worth spelling out where the field is declared.
|
||||
*
|
||||
* The printed sorting has never been implemented: nothing reads this to permit a sort, and a
|
||||
* consist is re-ordered at a Small Yard in a district (`switch.sortConsist`). What this actually
|
||||
* marks is the one Mainline card with a Yard Limit, and therefore the one an Extra may be made up
|
||||
* and started on (`apply.ts` § resolveExtraStart, `legal.ts`).
|
||||
*
|
||||
* Named for the printed text, and kept that way deliberately — renaming it would lose the link to
|
||||
* the card face — but the name has already misled once: `mainlineDescription` grew a sentence
|
||||
* telling players cars could be sorted here, which reached the board and the generated card
|
||||
* reference before it was caught on 2026-09-20.
|
||||
*/
|
||||
sortsCars: boolean;
|
||||
/** Named entry points printed on the card; some are unlocked by modifier cards. */
|
||||
entryPoints: readonly string[];
|
||||
@@ -805,7 +818,25 @@ export function mainlineDescription(kind: MainlineKind, gradeUp: Direction = 'ea
|
||||
parts.push('One train at a time — anything following has to wait for it to clear.');
|
||||
}
|
||||
|
||||
if (p.sortsCars) parts.push('Cars may be sorted into any new order here.');
|
||||
/**
|
||||
* WHAT THE INTERCHANGE ACTUALLY DOES, which is not what it prints.
|
||||
*
|
||||
* This said "Cars may be sorted into any new order here." — the printed capability, shown to
|
||||
* players on the board (`view.ts` renders this as a Mainline card's `what`) and printed in the
|
||||
* generated card reference. It is not implemented and never has been: nothing reads `sortsCars`
|
||||
* to permit a sort. Its one live use is identifying the card an Extra may be made up on, because
|
||||
* the Interchange is the Mainline card with a yard (`apply.ts` § resolveExtraStart).
|
||||
*
|
||||
* Found while bringing the reference documents up to date, 2026-09-20. A card that advertises an
|
||||
* action the game will not offer is worse than one that says nothing — a player goes looking for
|
||||
* a button that does not exist and concludes the game is broken.
|
||||
*/
|
||||
if (p.sortsCars) {
|
||||
parts.push(
|
||||
'This is the one Mainline card with a yard, so an Extra Train may be made up and started here. ' +
|
||||
'Its printed car-sorting is NOT implemented — a consist is re-ordered at a Small Yard in a district.',
|
||||
);
|
||||
}
|
||||
return parts.join(' · ');
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,14 @@ const MIME: Record<string, string> = {
|
||||
'.json': 'application/json; charset=utf-8',
|
||||
'.png': 'image/png',
|
||||
'.svg': 'image/svg+xml',
|
||||
/**
|
||||
* The Quickstart guide, published by `build-web.ts` as `quickstart.md`.
|
||||
*
|
||||
* text/plain ON PURPOSE. The fallback below is `application/octet-stream`, which makes a browser
|
||||
* DOWNLOAD the file instead of showing it — so without this line the splash page's "read the
|
||||
* guide" link hands a tester a file to save rather than a page to read.
|
||||
*/
|
||||
'.md': 'text/plain; charset=utf-8',
|
||||
};
|
||||
|
||||
const HEARTBEAT_MS = 20_000;
|
||||
|
||||
@@ -27,6 +27,11 @@ h1{font-size:32px;margin:0 0 2px;letter-spacing:.02em}
|
||||
a.door:hover{border-color:#4d6fa8;background:#1f2733;transform:translateY(-1px)}
|
||||
.door h2{font-size:17px;margin:0 0 5px;color:#9fb6d8}
|
||||
.door p{margin:0;color:var(--dim);font-size:13px;line-height:1.5}
|
||||
/* Not a fourth door: reading the guide is not a way to play, and giving it equal weight in the
|
||||
grid would say it is. A line under the doors, where somebody who does not know what to click
|
||||
will already be looking. */
|
||||
.newhere{margin:16px 2px 0;color:var(--dim);font-size:13px;line-height:1.55}
|
||||
.newhere a{color:#9fb6d8}
|
||||
.door .go{display:inline-block;margin-top:11px;font-size:12px;color:#5aa9e6}
|
||||
.door.disabled .go{color:var(--dim)}
|
||||
a.door.disabled{pointer-events:none}
|
||||
@@ -96,6 +101,11 @@ footer{margin-top:26px;color:var(--dim);font-size:11px;display:flex;gap:18px;fle
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p class="newhere">New to Station Master?
|
||||
<a href="./quickstart.md">Read the Quickstart guide</a> — what the game is, how you win,
|
||||
how a Stage runs, what is on the screen, and a first twenty minutes. About twenty minutes to
|
||||
read, and it will save you an hour of guessing.</p>
|
||||
|
||||
<div class="rule"></div>
|
||||
|
||||
<footer>
|
||||
|
||||
@@ -5509,3 +5509,43 @@ describe('a Division Yard with no coaches is a reportable condition', () => {
|
||||
assert.equal(waiting.loaded + waiting.empty, 2, 'the waiting coaches were miscounted');
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Quickstart is published beside the game, so a tester on the box can reach it.
|
||||
*
|
||||
* WHY THIS IS A TEST. The link on the splash page is a plain href to a file the BUILD copies out of
|
||||
* `docs/`. Nothing else connects the two: rename the document, or move it, and the build quietly
|
||||
* publishes nothing while the splash page keeps offering a link that 404s. Neither `tsc` nor any
|
||||
* other test would notice — the whole failure lives between a file name and a string.
|
||||
*/
|
||||
describe('the Quickstart guide reaches the site', () => {
|
||||
it('is published into dist and linked from the splash page', () => {
|
||||
const guide = join(dist, 'quickstart.md');
|
||||
assert.ok(existsSync(guide), 'the build did not publish quickstart.md');
|
||||
|
||||
const text = readFileSync(guide, 'utf8');
|
||||
assert.match(text, /^# Station Master — Quickstart/, 'quickstart.md is not the guide');
|
||||
assert.match(
|
||||
text,
|
||||
/Describes the game as built at v/,
|
||||
'the guide does not say which build it describes',
|
||||
);
|
||||
|
||||
const splash = readFileSync(join(dist, 'index.html'), 'utf8');
|
||||
assert.match(splash, /href="\.\/quickstart\.md"/, 'the splash page does not link the guide');
|
||||
});
|
||||
|
||||
it('is served as text rather than handed over as a download', () => {
|
||||
/**
|
||||
* The server's MIME fallback is `application/octet-stream`, which a browser downloads instead of
|
||||
* displaying — so the link would hand a tester a file to save rather than a page to read. The
|
||||
* table is read straight out of the source: asserting on a copy of it would pass while the real
|
||||
* one was wrong.
|
||||
*/
|
||||
const http = readFileSync(join(root, 'src/server/http.ts'), 'utf8');
|
||||
const table = http.slice(http.indexOf('const MIME'), http.indexOf('const HEARTBEAT_MS'));
|
||||
assert.match(table, /'\.md':\s*'text\/plain/, 'a .md file would be served as a download');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user