@game-hub/game-stoneage
v0.1.1
Published
Stone Age (Hans im Glück) as a Game Hub game package — extracted from the platform monorepo into a standalone repository that depends on the published @game-hub/kernel and @game-hub/ui-kit, following the Track D out-of-repo pattern proven by game-labyrint
Readme
@game-hub/game-stoneage
Stone Age (Hans im Glück, Bernd Brunnhofer 2008) as a Game Hub game package: a 2–4 player worker-placement Euro. Each round you send your people to the board's places to gather wood, brick, stone and gold (rolling dice, boosted by tools), grow your tribe, raise your food production, then spend what you gathered on buildings (instant points) and civilization cards (effects now, points at the end) — and feed everyone before the next round. The game ends when the card deck can't refill its display or a building stack runs out; the most points wins.
It is feature-complete (roadmap SA0–SA15): the full game, an illustrated zoomable board, a bot, the 2–3 player rule adjustments, and the one hidden-information redaction it has (the face-down civ-card draw pile).
A standalone game package
This repository is a standalone extraction from the Game Hub platform monorepo, built on the pattern the
platform proved with @game-hub/game-labyrinth (Track D): a game lives in its own repo and depends on the
platform's two published pieces from the public npm registry, exactly as any third-party game would.
| Package | Version | Why |
| ------------------ | -------- | ----------------------------------------------------------------------- |
| @game-hub/kernel | ^1.2.0 | contracts + primitives (GameError, record, makeSeating, Viewer) |
| @game-hub/ui-kit | ^1.0.0 | the shared board chrome + the game-facing REST helpers (./client) |
Both are 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. There is no dependency
on, path into, or workspace:/file: link back to the platform monorepo.
The Game Hub platform consumes this package like any other dependency: its exports resolve to the compiled
dist/ below, it is registered with one config entry, and it ships in the hub's production image as a
playable game — with no alias, path mapping or build shim reaching back into this repo.
The four-subpath package shape
Every Game Hub game exports the same four subpaths over TypeScript source:
src/
engine/ the pure rules core — no I/O, no Date, no Math.random. 100% coverage gate.
core/ constants (rulebook-sourced), domain types, the GameError subclass
actions/ one file per action: place, gather, take, use, build, acquireCard, feed
internal/ shared helpers (placement, feeding, buildings, cards, tools, scoring, the kernel record binding)
view.ts the per-viewer projection (near-identity; redacts only the face-down civ-card deck to a count)
tests/ one file per concern
module/ the backend seam — the GameModule: createGame wiring, parseAction, the error map, the
server-only gather-roll route (dice from ctx.rng), the bot runner
client/ the UI seam — the GameClient + the illustrated, zoomable board
Board.tsx / BoardMap.tsx / CardRow.tsx / PlayerPanel.tsx / Status.tsx
art/ procedural inline-SVG scene + piece icons (Scene.tsx, index.tsx)
bot/ the AI — the placement/policy model + self-play + a strength bench. 90% coverage gate../engineis the authoritative rules — pure and deterministic; randomness is injected (createGame({ rng }), and the dice via the module's roll route). It answers to a 100% coverage gate../moduleis theGameModulethe backend registers;./clientis theGameClientthe UI renders../botproducesActions the engine then validates — opinions, not rules. It answers to a 90% gate.
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 gates (engine 100%, bot 90%)
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 the check that runs against the built dist/ rather than TS source, and is what catches
it. CI (.github/workflows/ci.yml) runs exactly those commands, 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.
Rules
The rulebook PDF is not in this repository — it is copyrighted, so it stays local (gitignored) and the
code cites page numbers in comments instead. To work on the rules locally, drop the PDF in
reference_materials/ — see reference_materials/README.md. The
illustrations here are drawn fresh as procedural SVG; nothing is traced.
The full slice history, rules digest and rulings live in ROADMAP.md.
Licence
BSD-3-Clause (see LICENSE). Not affiliated with or endorsed by the publisher.
