201 lines
11 KiB
Markdown
201 lines
11 KiB
Markdown
# CB-RES-0001: game-state kernel
|
||
|
||
capability: game.kernel.authoritative-state
|
||
status: approved # adversarial review 2026-07-31: challenge + response in history/
|
||
tier: L (structural L, chaos d10=9 → no override)
|
||
runnable-baseline: invoked — harness in research/CB-RES-0001-harness/boardgame-io/
|
||
review-trail: history/260731-game-kernel-{research,challenge,response}.md
|
||
|
||
Survey of the best existing implementations of a **turn/phase game-state
|
||
kernel**: deterministic authoritative state, command → validation → events,
|
||
simultaneous commit/reveal, hidden information, replay. Conducted
|
||
2026-07-31; revised same day after adversarial review.
|
||
|
||
---
|
||
|
||
## Candidates
|
||
|
||
### 1. boardgame.io 0.50.2 (JS/TS) — measured
|
||
|
||
The most direct comparator: a declarative turn-based game engine.
|
||
|
||
- **Data model:** single plain-object `G` (game state) + framework `ctx`
|
||
(turn/phase bookkeeping). Game defined declaratively: `setup`, `moves`,
|
||
`phases`, `turn.stages`.
|
||
- **Mutation:** moves are reducer functions run through Redux + Immer;
|
||
mutate a draft, framework produces immutable next state and appends to an
|
||
action **log** (basis for time travel) and, unless `disableUndo` is set,
|
||
an **undo stack** holding a full state snapshot per move.
|
||
- **Determinism/replay:** seeded RNG via `random` plugin; log + seed give
|
||
replay and time travel. Measured: same seed → identical state hash across
|
||
runs; different seed diverges. ✅
|
||
- **Hidden information:** `playerView` projection (e.g. `STRIP_SECRET`) —
|
||
server strips secret state per player. *Not exercised by our workload*
|
||
(provenance note: commit/reveal shape measured; hidden-info cost not).
|
||
- **Simultaneous actions:** `activePlayers` stages give simultaneous move
|
||
windows; no built-in cryptographic commit/reveal — commitment is plain
|
||
state the server can see.
|
||
- **Maturity:** 12.4k GitHub stars, but **inactive** — last npm release
|
||
0.50.2 ≈ 4 years ago (checkable on npm; Snyk lists maintenance Inactive).
|
||
- **Measured performance** (harness, synthetic 3-player GROUND-shaped
|
||
commit/reveal workload, Node v24, `bnt-lap001`; run-to-run variance on
|
||
this machine is material — ±25% observed on identical configs):
|
||
|
||
| applied moves | default (moves/s) | `disableUndo` (moves/s) | RSS after run (isolated) |
|
||
|---:|---:|---:|---:|
|
||
| 5,000 | 1,439–1,930 | 1,118 | ~100 MB |
|
||
| 10,000 | 1,605 | 1,076 | 132 MB |
|
||
| 20,000 | 870 | 942 | 153 MB |
|
||
| 40,000 | — | 733 | 232 MB |
|
||
| 100,000 | DNF @300 s | DNF @240 s | — |
|
||
|
||
**Two degradation mechanisms, separately attributed:** (a) the undo
|
||
stack — an O(n) per-move array spread with full state snapshots — is the
|
||
dominant superlinear term at small N and is disable-able via the
|
||
documented `disableUndo` flag; (b) with undo disabled, throughput still
|
||
falls 34% from 5k→40k and 100k still does not finish, consistent with
|
||
unbounded client log/deltalog accumulation, which has no client-side off
|
||
switch in 0.50.2. Even the best configuration degrades with history.
|
||
(`updatePlayerID` in the timed loop measured separately: ~2 µs/call,
|
||
<0.5% contamination.)
|
||
- **Weight:** 120 transitive npm packages, 37 MB `node_modules`, core
|
||
package 3.9 MB (independently re-verified in review).
|
||
|
||
### 2. Tabletop Simulator scripting model (Lua) — cited
|
||
|
||
The dominant commercial virtual tabletop; reference for *tabletop
|
||
semantics*, not a rules kernel.
|
||
|
||
- **Data model:** none authoritative — game state *is* the physical scene;
|
||
Lua state serialized as strings into the save JSON (`onSave`/`onLoad`).
|
||
- **Mutation:** imperative Lua in a Global script + per-object scripts with
|
||
event hooks; **no move validation or rules engine** — rules are social,
|
||
physics is primary (sandbox mode in Clay-Borg terms).
|
||
- **Determinism/replay:** none. Hidden info via hand zones (engine feature,
|
||
not a projection model).
|
||
- Pattern source for object-attached behavior and hand zones;
|
||
architecturally the anti-model for an authoritative kernel.
|
||
|
||
### 3. Rune SDK (JS) — cited
|
||
|
||
Modern (active) deterministic multiplayer engine for casual web games.
|
||
|
||
- **Data model/mutation:** pure `logic.js` — game state + action functions,
|
||
statically checked for nondeterminism (mutation escape, `Math.random`
|
||
patched deterministic).
|
||
- **Sync:** predict-rollback: all clients + server simulate the same
|
||
deterministic logic. Strongest determinism *discipline* of the
|
||
candidates — enforced by tooling, not convention.
|
||
- **Limits:** platform-bound (Rune's hosted ecosystem), not an embeddable
|
||
open kernel; no phase framework, hidden-information projection, or
|
||
event-sourced replay surface.
|
||
|
||
### 4. OpenSpiel (C++/Python, DeepMind) — cited *(added after review)*
|
||
|
||
Research games kernel explicitly built for **simultaneous-move and
|
||
imperfect-information games**.
|
||
|
||
- Deterministic state; serializable; replay via action histories;
|
||
information-state abstractions for imperfect information; large game
|
||
library. Active. As an in-process C++ kernel it is also a credible D3
|
||
comparator (unmeasured here — open follow-up).
|
||
- **Limits:** research-oriented — no client visibility *projection* layer,
|
||
no networking/session model, no snapshot format, monolithic C++/Python
|
||
build rather than an embeddable capability boundary. The semantics
|
||
overlap with our kernel is real; the production layer is absent.
|
||
|
||
### 5. Ludii / GGP-GDL lineage (JVM) — cited *(added after review)*
|
||
|
||
General game systems whose core value is **ease of rule specification**
|
||
(the D1 dimension): games written as ludemes (Ludii, 1,000+ games) or GDL
|
||
rules, executed by a general engine.
|
||
|
||
- Proof that rule description languages can cover enormous game spaces
|
||
compactly; the D1 literature our kernel spec should be checked against.
|
||
- **Limits:** research/archival focus; no production multiplayer stack,
|
||
no hidden-info projection for clients, JVM-bound; performance oriented
|
||
to AI playouts, not authoritative session serving.
|
||
|
||
### 6. Event-sourcing kernels (Rust `cqrs-es` pattern / EventStoreDB) — cited
|
||
|
||
Aggregates validate commands and emit events; state is a fold over the
|
||
append-only log; snapshots bound replay cost. Replay/audit native.
|
||
**Performance: estimated** — order 10⁵–10⁶ small events/s per core for
|
||
in-process fold-style application; dedicated stores sustain tens of
|
||
thousands of appends/s over the network. *No reproducible citation held;
|
||
treated as directional only.* No game semantics.
|
||
|
||
### 7. bevy_ecs (Rust) — cited
|
||
|
||
Archetypal ECS; the world/spatial layer in our architecture. Millions of
|
||
entity-component accesses per frame (Bevy's published benches). No
|
||
authoritative command/event pipeline, replay, or hidden-info projection —
|
||
confirms the split: ECS for world representation, typed aggregates for the
|
||
semantic kernel.
|
||
|
||
### Secondary references (not fully surveyed)
|
||
|
||
- **Board Game Arena Studio** — dominant commercial turn-based rules
|
||
framework (PHP): server-authoritative state machine, hidden info, full
|
||
replay. Closed platform; stronger commercial reference than TTS.
|
||
- **Colyseus** — active JS authoritative-state multiplayer server; room
|
||
state sync without game-rules semantics (no phases/legality/replay
|
||
framework). The "active JS" counterweight to inactive boardgame.io.
|
||
- **Vassal** — module player for humans; no programmable rules kernel.
|
||
|
||
---
|
||
|
||
## Baselines (benchmark-to-beat)
|
||
|
||
| Dimension | Baseline holder | Metric | Value | Provenance |
|
||
|---|---|---|---|---|
|
||
| D1 ease of specification | boardgame.io | LOC to express the synthetic 3p commit/reveal game (declarative object) | ~36 LOC | measured (bench.js); gameable — secondary indicator |
|
||
| D1 (qualitative bar) | Ludii/GDL | rule-description-language coverage of large game spaces | qualitative | cited |
|
||
| D2 implementation weight | boardgame.io | transitive deps / install size | 120 pkgs / 37 MB | measured |
|
||
| D3 throughput | boardgame.io (`disableUndo`, best config) | applied moves/s, 3p workload @5k moves | ~1,100 (default config 1,400–1,900; ±25% machine variance) | measured, bnt-lap001 |
|
||
| D3 scaling | boardgame.io (`disableUndo`, best config) | throughput @40k vs @5k moves | 0.66× (default config: 0.45× @20k; both DNF @100k) | measured, bnt-lap001 |
|
||
| D3 memory | boardgame.io | RSS after run, isolated process | ~100 MB @5k → 232 MB @40k | indicative (raw RSS incl. Node baseline) |
|
||
| D4 optionality | Rune | determinism enforced by tooling | static nondeterminism checks | cited |
|
||
| D4 | boardgame.io | replaceability of subsystems | plugin API, but JS-ecosystem-locked; no null/reference impl pattern | observed |
|
||
|
||
Observation (not a baseline row): no surveyed candidate links numbered
|
||
rules to test scenarios (M-D1-COV-style traceability); any nonzero
|
||
coverage exceeds the field, so this is a direction, not a bar.
|
||
|
||
**Headline target for the Clay-Borg kernel (stipulated engineering target,
|
||
not competitor-derived):** ≥ 100,000 applied events/s sustained with **flat
|
||
scaling** (throughput @100k events within 10% of @5k), deterministic replay
|
||
bit-identical, same machine and workload shape as the harness. The
|
||
event-sourcing ceiling row is an estimate; until a Rust comparator is
|
||
measured locally (open follow-up), evidence rows leaning on it cap at
|
||
parity per MetricsAndScenarios §3.
|
||
|
||
## Verdict
|
||
|
||
- **D1:** boardgame.io's declarative game object is the bar to match for
|
||
compactness; Ludii/GDL is the literature bar for rule-language
|
||
expressiveness. Rule-to-scenario traceability is an open lane no one
|
||
occupies.
|
||
- **D2:** boardgame.io's 120-dep/37 MB footprint is beatable by an order
|
||
of magnitude.
|
||
- **D3:** boardgame.io degrades with history even in its best measured
|
||
configuration; our advantage over it is partly language choice, so the
|
||
meaningful commitment is the stipulated flat-scaling + 100k/s target,
|
||
not the multiplier over boardgame.io.
|
||
- **D4:** Rune's tooling-enforced determinism is the discipline to
|
||
assimilate; no candidate offers a null/reference/optimized port pattern
|
||
or a WIT-style embeddable boundary.
|
||
- **The surpass opportunity, stated precisely:** OpenSpiel covers
|
||
simultaneous-move + imperfect-information *semantics* with deterministic
|
||
replay; boardgame.io covers declarative rules + client projection +
|
||
networking; Rune covers enforced determinism; event sourcing covers
|
||
flat-cost replay with snapshots. **No candidate combines** the semantic
|
||
coverage with a production projection/networking layer, snapshot-bounded
|
||
flat-cost replay, and an embeddable, language-portable capability
|
||
boundary. That combination — not raw speed over an inactive JS library —
|
||
is what the Clay-Borg kernel should be built to demonstrate.
|
||
- **Risks that remain:** event-sourcing D3 row unsourced (estimate;
|
||
parity-cap active); OpenSpiel unmeasured locally; harness measures the
|
||
headless client pipeline (canonical usage — a bare server Master could
|
||
differ); ±25% run-to-run variance on this machine; hidden-info cost
|
||
unmeasured.
|