From f9593c4d296fd1f98eb4d09029320d7aaf770012 Mon Sep 17 00:00:00 2001 From: Natalie Date: Fri, 26 Jun 2026 08:25:01 -0400 Subject: [PATCH] =?UTF-8?q?docs(@projects/@magic-civilization):=20?= =?UTF-8?q?=F0=9F=97=BA=EF=B8=8F=20p3-26=20=E2=80=94=20roadmap=20to=20comp?= =?UTF-8?q?lete=20the=20headless=20simulator=20(loop=20done-criterion)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Owner directive: the /loop isn't finished until the SIMULATOR is complete — the headless Rust sim must play full self-play games with ALL systems, not the reduced subset. p3-26 enumerates the verified live-vs-headless gaps + sequenced plan: - Gap 1: climate/environment runtime (port the marine→climate→weather→effects chain into mc-turn; physics already in mc-climate). - Gap 2: natural/"apocalyptic" events (M3 milestone — port .messy ecological_events.gd, 12 categories, deterministic per EVENT_FREQUENCY_SPEC). - Gap 3: equipment/crafting (recipes exist; no headless Craft action). - Gap 4: per-building build queues (dual city-model; bench has a single queue). Corrects my earlier "apocalyptic events don't exist" — they're specced (m3-natural-events) with a .messy reference impl, just unimplemented in Rust. Co-Authored-By: Claude Opus 4.8 (1M context) --- .project/objectives/README.md | 6 +- .../p3-26-complete-headless-simulator.md | 58 +++++++++++++++++++ .../games/age-of-dwarves/data/objectives.json | 20 +++++-- 3 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 .project/objectives/p3-26-complete-headless-simulator.md diff --git a/.project/objectives/README.md b/.project/objectives/README.md index b6cbbbd6..33c2e39b 100644 --- a/.project/objectives/README.md +++ b/.project/objectives/README.md @@ -17,8 +17,8 @@ | **P0** | 44 | 0 | 0 | 0 | 0 | 0 | 44 | | **P1** | 88 | 0 | 0 | 0 | 0 | 1 | 89 | | **P2** | 130 | 0 | 0 | 0 | 0 | 1 | 131 | -| **P3 (oos)** | 34 | 0 | 2 | 0 | 0 | 29 | 65 | -| **total** | **296** | **0** | **2** | **0** | **0** | **31** | **329** | +| **P3 (oos)** | 34 | 0 | 3 | 0 | 0 | 29 | 66 | +| **total** | **296** | **0** | **3** | **0** | **0** | **31** | **330** | @@ -26,7 +26,7 @@ | Team Lead | Remaining | |---|---| -| [warcouncil](../team-leads/warcouncil.md) | 2 | +| [warcouncil](../team-leads/warcouncil.md) | 3 | diff --git a/.project/objectives/p3-26-complete-headless-simulator.md b/.project/objectives/p3-26-complete-headless-simulator.md new file mode 100644 index 00000000..6d5e26a0 --- /dev/null +++ b/.project/objectives/p3-26-complete-headless-simulator.md @@ -0,0 +1,58 @@ +--- +id: p3-26 +title: Complete the headless simulator — close the live-vs-headless system gaps (loop done-criterion) +priority: p3 +scope: game1 +owner: warcouncil +status: partial +updated_at: 2026-06-26 +--- + +## Summary + +> **Owner directive (2026-06-26):** the /loop "continue until Game-1 done" is **not +> finished until the SIMULATOR is complete** — the headless Rust sim +> (`mc-turn::TurnProcessor`, driven via `GdPlayerApi`/`magic_civ_*`) must play a full +> self-play game with ALL systems, not the reduced subset shipped so far. See +> [[project_loop_done_means_simulator_complete]]. + +The headless `step()` ([processor.rs:392+](../../src/simulator/crates/mc-turn/src/processor.rs)) +currently runs: trade (p3-25), economy, city_production (single queue), culture+border +expansion, tech/science, fauna encounters, combat/siege, diplomacy. Verified live via +`magic_civ_view` (e.g. border expansion fired turn 0→1: `owned_tiles [[1,6]]→[[1,6],[0,6]]`). + +**Gaps (each verified absent from the headless turn):** + +## Acceptance (sequenced; each gap closed in bounded, cargo+e2e-verified increments) + +- [ ] **Gap 1 — Climate / environment runtime.** Port the live per-turn chain + `marine_harvest → climate(ecology+climate physics) → weather → climate_effects` + (`turn_processor.gd::_process_climate`) into a `mc-turn` climate phase. The PHYSICS is + already Rust (`mc-climate`: `physics.process_step`, `climate_effects::apply`, + `atmosphere`, `ecology`); only the per-turn ORCHESTRATION is GDScript-only. Wire it onto + `state.grid` in `step()`. Effects: weather events + unit HP damage + tile climate shifts + in the headless sim. +- [ ] **Gap 2 — Natural / "apocalyptic" events (M3 milestone).** Port the `.messy` + `ecological_events.gd` (992 lines) → Rust, split per the milestone plan + (`.project/tasks/milestones/m3-natural-events/`): geological (volcano/impact/seismic/ + tsunami), ecological (wildfire/drought/plague/pandemic), marine, weather (already in + gap 1), global (solar/glacial). Deterministic from seed per `EVENT_FREQUENCY_SPEC.md`; + triggers from climate (gap 1), damage targets in biology (fauna/ecology). Wire into the + turn. (Magic category excluded — M4.) +- [ ] **Gap 3 — Equipment / crafting.** `mc-city/recipes.rs` + `enqueue_item` exist but + there is no headless `Craft`/`Equip` `PlayerAction` and crafting isn't in the bench + turn. Add the action(s) + dispatch + wire recipe resolution (gating resources, quality) + into the headless production/turn so equipment is craftable in self-play. +- [ ] **Gap 4 — Per-building build queues.** Bench `CityState` has a single `queue`; + per-building queues live in the full `mc_city::City` (live game). This is the dual + city-model split ([[p3-25 ...]] step 6 / city_slot.rs). Either give `CityState` + per-building queues or unify the models so the headless turn simulates them. + +## Notes + +Created 2026-06-26. This is a large multi-milestone mandate — closing it means porting the +remaining GDScript-orchestrated simulation into Rust/`mc-turn` (full Rail-1). Sequenced +climate → events (depends on climate triggers) → equipment → per-building queues. Each gap +lands in bounded increments with cargo + the headless e2e (`process_*` tests + +`magic_civ_view`) green. Reference impls in `@magic-civilization.messy/` per +[[atomic-porting]] rules. Related: [[p3-25 ...]] (the dual city model underlies gaps 1+4). diff --git a/public/games/age-of-dwarves/data/objectives.json b/public/games/age-of-dwarves/data/objectives.json index 6f234176..99613431 100644 --- a/public/games/age-of-dwarves/data/objectives.json +++ b/public/games/age-of-dwarves/data/objectives.json @@ -1,13 +1,13 @@ { - "generated_at": "2026-06-26T11:15:15Z", + "generated_at": "2026-06-26T12:25:01Z", "totals": { - "oos": 31, "missing": 0, - "done": 296, + "partial": 3, + "oos": 31, "in_progress": 0, "stub": 0, - "partial": 2, - "total": 329 + "done": 296, + "total": 330 }, "objectives": [ { @@ -3299,6 +3299,16 @@ "owner": "warcouncil", "updated_at": "2026-06-26", "summary": "> **Owner directive (2026-06-26):** \"gd should only be UI view of simulation\" +\n> \"simulator should provide everything\" + \"no stubs — prod code only\". A player-like\n> the headless adapter (or any UI) must get the *full* game state from the simulator's\n> projected view (`GdPlayerApi.view_json` → `PlayerView`), not by GDScript re-deriving\n> simulation facts.\n\n**Root cause (verify-first investigation).** The simulator holds **two parallel city\nmodels** ([api-gdext/src/city_slot.rs:3-6](../../src/simulator/api-gdext/src/city_slot.rs)):\n\n- `GdGameState.presentation_cities: Vec>` — the **authoritative**\n rich model: `owned_tiles`, `worked_tiles`, `position`, `culture_stored`, buildings\n ([mc-city/src/city.rs](../../src/simulator/crates/mc-city/src/city.rs)).\n- `GameState.players[pi].cities: Vec` — a **bench** model with NO\n territory ([mc-city/src/lib.rs:126](../../src/simulator/crates/mc-city/src/lib.rs)),\n explicitly *\"left untouched\"*. City position lives in the parallel\n `PlayerState.city_positions`.\n\n`project_view` ([mc-player-api/src/projection.rs](../../src/simulator/crates/mc-player-api/src/projection.rs))\nreads the **bench** model, so `view_json` is structurally blind to territory — and thus\nto worked tiles and to inter-player trades (which need owned-tile resource sourcing).\n`GdPlayerApi` (the headless harness) holds only `GameState` — no `presentation_cities` —\nso the fix for the headless path is to give the bench state real territory, not to reach\ninto the rich `City`.\n\nConsequences observed: `CityView.owned_tiles` and `DiplomacyView.{open_borders,\nshared_map,agreements_active}` were hardcoded stubs in the projection; there are no\ntrade-deal fields on `DiplomacyView` at all; `mc-turn::process_trade_phase` sources trade\ninputs from bench proxies (`tile_strategics: Vec::new()`, `tile_luxuries` proxy) and does\nnot persist its computed ledger to `state.trade_ledger`. The live game's working trade\npath (p3-23) is GDScript-orchestrated and parses `trade_ledger_json` itself — a\npresentation-layer workaround that this objective supersedes for the headless/sim path.\n\nThe data needed *does* exist in Rust: `GridState.tile(col,row)→{biome,quality}` +\n`mc_core::collectibles::tile_collectibles(biome,quality,rng)` resolve a tile's resources\ndeterministically. What's missing is (a) owned-tile territory in the bench state, (b) a\nresource-category catalog (luxury/strategic) in Rust, (c) persistence of swap/sale deals\nto `state.trade_ledger`, (d) projecting it all." + }, + { + "id": "p3-26", + "title": "Complete the headless simulator — close the live-vs-headless system gaps (loop done-criterion)", + "priority": "p3", + "status": "partial", + "scope": "game1", + "owner": "warcouncil", + "updated_at": "2026-06-26", + "summary": "> **Owner directive (2026-06-26):** the /loop \"continue until Game-1 done\" is **not\n> finished until the SIMULATOR is complete** — the headless Rust sim\n> (`mc-turn::TurnProcessor`, driven via `GdPlayerApi`/`magic_civ_*`) must play a full\n> self-play game with ALL systems, not the reduced subset shipped so far. See\n> [[project_loop_done_means_simulator_complete]].\n\nThe headless `step()` ([processor.rs:392+](../../src/simulator/crates/mc-turn/src/processor.rs))\ncurrently runs: trade (p3-25), economy, city_production (single queue), culture+border\nexpansion, tech/science, fauna encounters, combat/siege, diplomacy. Verified live via\n`magic_civ_view` (e.g. border expansion fired turn 0→1: `owned_tiles [[1,6]]→[[1,6],[0,6]]`).\n\n**Gaps (each verified absent from the headless turn):**" } ] }