@game-hub/game-russianrailroads
v0.1.1
Published
Russian Railroads (Ultimate Railroads, big-box edition) as a Game Hub game package — built OUTSIDE the platform monorepo, against the published @game-hub/kernel and @game-hub/ui-kit, on the pattern the Labyrinth pilot proved. Four subpath exports over Typ
Readme
@game-hub/game-russianrailroads
Russian Railroads (the Ultimate Railroads big-box edition, Hans im Glück) as a Game Hub game package: a 2–4-player worker-placement Euro over seven rounds where you send workers to push track markers along three private routes, buy locomotives that gate how much of a route scores, advance an industry track fed by flipped-locomotive factories, and hire reusable engineers — chasing the most points when the last round scores.
It is built outside the Game Hub platform monorepo, against the published @game-hub/kernel and
@game-hub/ui-kit — the pattern the Labyrinth pilot proved.
Every dependency on the platform is a registry install; there is no path, link or file: dep reaching
back into the monorepo. If the published contract has a hole in it, this repo's CI is where it shows up.
The package shape
A game is four subpath exports behind game-agnostic hosts, so adding one to a host is additive:
| Subpath | What it is |
| ----------- | ------------------------------------------------------------------------------------------ |
| ./engine | the pure rules core — no Date, no Math.random, no mutation. 100% coverage gate. |
| ./module | the backend seam — the GameModule: createGame wiring, parseAction, error map, redaction |
| ./client | the UI seam — the GameClient + the (lazy, code-split) board that renders inside the ui-kit chrome |
| ./bot | the AI (RR10). A typed stub today; its 90% gate arrives with the first real bot code |
The two platform packages are both peer dependencies (the host provides one copy) and dev
dependencies (so this repo builds and tests standalone). @game-hub/ui-kit and react are optional
peers — only ./client needs them, so a backend host installing this game for ./module alone is not told
it owes React.
Status
Base game + art (RR0–RR9) complete. The engine implements the full seven-round game: worker placement,
the five-colour ascending track ladder (no leapfrogging), locomotives with cascading upgrade chains, the
industry track and factories, engineers (the sliding strip + majority), per-round scoring off the valuation
tile, and final scoring with the secret end-bonus cards. ./module and ./client are complete and the
board is illustrated ("The Permanent Way" art pass, RR9).
Still queued (these land in future slices):
- RR9b — board-UI revamp. It should borrow the Labyrinth pilot's board-UI findings. Its testids are the
Game Hub's e2e contract; see the note in
CLAUDE.md. - RR10 — the bot. A greedy baseline over the (barely-redacted) view.
./botis a typed stub until then, andvitest.config.tsgrows itssrc/bot/**90% gate in the same slice.
The full slice history, rules digest and rulings live in ROADMAP.md.
Layout
src/
engine/ the pure rules core — 100% coverage gate
core/ constants, types, errors, and the rulebook-transcribed data
internal/ shared helpers (setup randomness, scoring, the kernel record()/seating bindings)
actions/ one file per mechanic (place, moveTrack, locomotive, factory, engineer, …)
tests/ one file per concern
module/ the backend seam — the GameModule: createGame wiring, parseAction, the error map, viewFor
client/ the UI seam — the GameClient + the board and panels
art/ "The Permanent Way" art primitives (RR9)
bot/ the AI (RR10). Typed stub today; 90% coverage gate enabled with it.Running it
Requires Node 22 (.nvmrc) and pnpm (the version is pinned in packageManager).
pnpm install # resolves @game-hub/* from the public registry
pnpm test # vitest + the coverage gate (engine 100%; bot 90% from RR10)
pnpm test:watch
pnpm typecheck # strict TS across all four subpaths
pnpm lint # ESLint 9 flat config — real hazards, not a second typecheck
pnpm format:check # Prettier (hand-wrap Markdown; *.md is Prettier-ignored)
pnpm build # tsc → dist/ (JS + .d.ts + inline-source maps), what publishConfig points at
pnpm pack:smoke # pack, install outside this repo, play a game under plain node, typecheck a consumer⚠️ Relative imports in the shipped sources carry an explicit .js extension ('../engine/index.js') —
tsc emits them verbatim and Node ESM resolves neither extensions nor directories, so extensionless ones
would produce a tarball that throws on a host's first import while every command above stayed green.
pack:smoke is what catches it.
CI (.github/workflows/ci.yml) runs exactly those, in that order, on a runner with no access to the
platform monorepo.
Using this package in a host, before it is published: pnpm pack here, then depend on the tarball — the
same vendored-tarball loop the Game Hub uses for the Labyrinth pilot.
Rules
The Ultimate Railroads rulebook PDF is not in this repository — it is copyrighted, so it stays local
(gitignored) and the code cites page numbers instead. See
reference_materials/README.md for how to obtain it. Mechanics aren't
copyrightable; the illustrations are, so every asset here is drawn fresh in the house style.
Licence
BSD-3-Clause (see LICENSE). Not affiliated with or endorsed by Hans im Glück or its
publishers.
