v0.4.1 - bug fixes. initial d12 rolls determine what player in which seat.

This commit is contained in:
Jesse
2026-08-13 15:28:53 -04:00
parent 49f8504b05
commit a7221dcf20
22 changed files with 736 additions and 237 deletions
+18 -8
View File
@@ -67,12 +67,18 @@ Three flows, and keeping them distinct is what keeps the implementation tractabl
```
- **Intents** are what a player wants to do. They are proposals; they can be rejected.
- **Events** are what happened. They are facts, ordered, and form the game's history.
- **Events** are what happened. They are facts, ordered, and standalone — they narrate the game.
They do **not** reconstruct it: see `protocol.md` §3, and the note below.
- **Views** are per-player projections of state, with other players' hands redacted.
An event log that fully determines state is worth building even at this scale. It gives
reconnection (replay to catch up), persistence (store the log, not a snapshot), and debugging (replay
a reported bug exactly) for one design decision.
A recorded, ordered history is worth keeping even at this scale. It gives persistence (store the
history, not a snapshot) and debugging (replay a reported bug exactly) for one design decision.
**That history is the INTENTS, not the events.** This paragraph originally said an event log "fully
determines state", and it does not — the phase driver mutates state and then emits a descriptive
event, so fourteen of the forty-six event types are never reduced, including every train movement.
A game is `{ seed, history: Intent[] }` and `fromSave` replays it exactly. Reconnection is therefore a
fresh view rather than a catch-up replay, which is simpler anyway.
### Post-game replay — a desired future capability
@@ -84,9 +90,13 @@ traffic, the wrecks, who was Superintendent when. For a game whose drama is larg
you are playing your own Office, this is worth more than it would be in most games. You spend the
game watching your own station; the replay is where you find out what the railroad was doing.
State is already `fold(events)` over an ordered, append-only log, and all randomness derives from a
stored seed. Replay is therefore a **read-only projection over the existing log** — no new rules-engine
surface, no second code path, nothing the server has to do differently during play.
A game is already reconstructible from `{ seed, history: Intent[] }`, and all randomness derives from
that stored seed. Replay is therefore a **read-only re-run of the stored intents** — no new
rules-engine surface, no second code path, nothing the server has to do differently during play.
(This paragraph said "state is `fold(events)`" until v0.4.0. It is not: the phase driver mutates state
and then emits a descriptive event, so folding the log rebuilds card plays and switching but not one
train movement or clock tick. The intents are what is canonical.)
Three constraints keep it cheap, and all three are free if honoured from the start:
@@ -144,7 +154,7 @@ is a lobby setting layered on top of the rules, never part of them.
Worth stating, because small self-hosted scale makes several standard concerns disappear:
- **No horizontal scaling.** A handful of concurrent games fits in one process. Game state lives in
memory; the event log is persisted for durability, not for coordination.
memory; the intent history is persisted for durability, not for coordination.
- **No matchmaking service.** Players share a game code (see `lobby-and-sessions.md`).
- **No accounts system, initially.** A display name plus a session token is enough to join and
reconnect. Real accounts can be layered on later without touching the game engine.