v0.7.0 — four game types, a lobby you can read and leave, and a multiplayer game that makes a sound

The multiplayer set-up, the lobby, the start of a game, and four signals a remote client had never
been sent. Reasoning, the preset table and what was verified how: CHANGELOG.md.

- Co-op, Competitive, Cutthroat, Solitaire and Custom, on both screens, from one shared block —
  they had drifted, and each was missing a question the other asked.
- A player reads the whole rule set before taking a seat, may leave a lobby or a running game, and
  keeps a seat across a reload. The host may clear a chair. The browser remembers every game it is
  in, not just the last one.
- The start of a game is drawn: a handoff beat, an announcement, the code and type in the header.
- Sound, the timetable flash, announcements and the just-drawn badge now reach a remote client;
  justDrawn goes to the seat that drew it and nobody else.
- Played on StartOS, which found the rest: an Extra belongs to the player who played it, the board
  never named the Superintendent, bot seats were reported as absent players, and rule section
  numbers are out of every string a player reads.

Also carries the previous session's Heavy Grade documentation work — asked again, answer unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016JczK5i33ZNSf2PtzZqdhS
This commit is contained in:
Jesse.Markowitz
2026-08-23 05:46:42 -04:00
co-authored by Claude Opus 5
parent 42adfda390
commit 06db36e5b5
41 changed files with 4387 additions and 588 deletions
+13 -1
View File
@@ -46,7 +46,7 @@ import {
mainlineDescription,
} from '../engine/content.ts';
import type { Intent } from '../engine/intents.ts';
import type { Facility, GameState, PlayerIndex, SeatIndex, TrackCard, TurnoutOrientation } from '../engine/state.ts';
import type { Facility, GameConfig, GameState, PlayerIndex, SeatIndex, TrackCard, TurnoutOrientation } from '../engine/state.ts';
import { carsOn, playerAtSeat, railFacingOf, seatOf, turnOf } from '../engine/state.ts';
import type { Hand, HouseRules, TrackGeometry } from '../engine/content.ts';
import type { Port } from '../engine/track.ts';
@@ -374,6 +374,16 @@ export type Frame = {
* must be able to answer. Resolved, never partial, so nobody downstream re-applies defaults.
*/
houseRules: HouseRules;
/**
* WHICH GAME THIS IS — the mode it is scored under and Appendix B's three switches.
*
* Added 2026-08-23 with the game types (`web/presets.ts`). Everything else needed to name a game
* "Co-op" or "Cutthroat" was already here; `mode` and `optionalRules` were the two missing pieces,
* so a remote client could see the dials but not what they added up to — and a Cutthroat game
* looked exactly like a Co-op one from the board.
*/
mode: GameConfig['mode'];
optionalRules: GameConfig['optionalRules'];
/**
* The victory-condition dials this game was configured with (`GameConfig`, `state.ts`), plus the
* running collision counts — same reasoning as `houseRules`: a remote client holds no `GameState`
@@ -1313,6 +1323,8 @@ export function snapshot(
wasted,
option: turnOf(s, viewer).option,
houseRules: houseRules(s.config),
mode: s.config.mode,
optionalRules: s.config.optionalRules,
days: s.config.days,
minCombinedRevenue: s.config.minCombinedRevenue,
maxCollisionsPerDay: s.config.maxCollisionsPerDay,