Commit Graph

5 Commits

Author SHA1 Message Date
bd9f550b67 replay: closing tests and overlay polish (Phase 4)
- Round-trip: every command verb serialize->parse->re-serialize is identical;
  malformed input is rejected.
- Equivalence: add a long ~2400-tick run through waves/combat that records,
  reads back, and replays to a byte-identical final state with no desync
  (alongside the existing short scenario).
- Desync detection: corrupting one recorded checksum makes ReplayPlayer report
  the exact desync tick.
- Reset boundary: a Reset drained through CommandManager rolls the recorder to
  a new file named by the new seed.
- Polish: the end-of-replay / desync overlay dims the world behind the message.

Record + playback is now functionally complete and covered by headless tests.
Full suite green (354 cases / 3418 assertions).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DUsFgd2Ga6pmLz8giS8WUn
2026-06-30 21:20:22 +02:00
26e108f3e1 replay: play back recorded runs via --replay (Phase 3)
Adds view-only playback: re-simulate from the recorded seed + commands and
verify the RNG checksums.

- ReplayReader (lib): parses a replay file into header + an ordered stream of
  command/checksum entries. CommandSerializer gains the inverse parseCommand
  (round-trips every verb; rejects malformed input).
- ReplayPlayer (lib): the playback driver. Applies each command at its exact
  recorded tick and verifies checksums in file order (start() handles tick 0;
  advanceTo(tick) handles each tick after sim.tick()). Independent of
  replay-time speed/pause; reports the first desync tick.
- CommandManager replay mode: enqueue() becomes a no-op so live input is
  ignored while the recorded stream drives application.
- main.cpp: --replay <file> reads + validates (warns on version/config-hash
  mismatch), seeds the sim from the header, and threads the replay through
  MainWindow to GameWorldView.
- GameWorldView: drives the player in onFrame (manual speed/pause kept,
  forward-only), gates the schematic-choices and game-over polls, and draws a
  "REPLAY" tag plus a passive "Replay ended" / "Desync at tick N" overlay.
- computeReplayConfigHash factored out of ReplayRecorder for reuse by main.

ReplayPlaybackTest records a scripted run, reads it back, replays it, and
asserts no desync + byte-identical final state -- including the
periodic-checksum-then-command ordering at a shared tick. Full suite green
(350 cases / 3396 assertions); app, tests, and balancing all build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DUsFgd2Ga6pmLz8giS8WUn
2026-06-30 21:06:59 +02:00
97b6f0d8fd replay: record every run to a replay file (Phase 2)
Hooks a recorder into the command chokepoint so each run is written to disk as
it plays, ready for playback in Phase 3.

- ReplayRecorder (lib): line-oriented append-friendly file — keyed header
  (version, build, seed, config_hash, timestamp), then '---', then tick-tagged
  command lines interleaved with "# checksum <tick> <hex>" RNG fingerprints.
  Each line is flushed so a crash leaves a valid partial file. Config hash is a
  64-bit FNV over the config dir's *.toml files; build tag is __DATE__/__TIME__.
- CommandSerializer (lib): per-command text (length-prefixed variable parts;
  ship layouts and splitter filters serialized inline). Reset is a file
  boundary, never a stream entry.
- CommandManager owns an optional ReplayRecorder: drain() records each applied
  command + a post-apply checksum; a drained Reset rolls to a new file;
  recordTickCheckpoint() (called per tick from onFrame) writes a checksum every
  30 ticks.
- Random seed generated in main and on restart (std::random_device); Simulation
  retains it via getSeed() for the header.
- GameWorldView attaches the recorder at construction (first file + tick-0
  checksum); replays land in <data>/replays named <timestamp>_<seed>.replay.

ReplayRecorderTest covers serialization, file well-formedness, file rolling,
and the CommandManager drain->record integration. Full suite green
(346 cases / 3377 assertions); app, tests, and balancing all build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DUsFgd2Ga6pmLz8giS8WUn
2026-06-30 20:08:04 +02:00
82ca9080a5 replay: route player input through a command chokepoint (Phase 1)
Reshapes every UI-driven sim mutation to flow through one path so it can be
recorded and replayed later, with behaviour unchanged.

- Command model (lib): Command base + derived types (PlaceBuilding, Demolish,
  RotateInPlace, SetRecipe, SetShipLayout, Set[Site]SplitterFilters,
  ClearBeltTiles, ApplySchematicChoice, Reset), each with a playerId for the
  future-multiplayer shape. PlaceBuilding is atomic (carries optional
  recipe/layout/filters) because deferred commands never return the new
  BuildingId to the caller.
- CommandManager (lib): FIFO queue holding a Simulation&; enqueue + drain.
- Simulation::apply(const Command&): the single chokepoint, dispatching by
  kind to the existing mutators. Mutators stay public (enforced by convention,
  not compile-time, so the test suite keeps driving the sim directly).
- Timing: GameWorldView owns the CommandManager and drains it once per frame in
  onFrame, before the tick batch (runs at 0x too, so build-while-paused is
  preserved). A drained Reset triggers the view reset.
- UI fan-in: GameWorldView enqueues its own input directly; MainWindow and
  SelectedBuildingPanel emit CommandRequestedEvent, which GameWorldView
  subscribes to and enqueues. No UI site mutates the sim directly anymore.
- CommandTest: asserts apply(...) yields byte-identical state to the direct
  mutator path, and that CommandManager drains FIFO through apply.

Full suite green (338 cases / 3353 assertions); determinism double-run still
passes. Design doc updated with the atomic-PlaceBuilding and convention-
enforcement decisions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DUsFgd2Ga6pmLz8giS8WUn
2026-06-30 19:44:50 +02:00
a97687154e docs: add replay record/playback design and implementation plan
Captures the deterministic command-replay design (seed + config hash +
tick-tagged command log, re-simulated on playback), the command-chokepoint
and timing model, determinism/checksum strategy, file format, and a
sequenced Phase 0-4 implementation plan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DUsFgd2Ga6pmLz8giS8WUn
2026-06-30 15:33:55 +02:00