diff --git a/.project/objectives/p1-60-fog-of-war-testing-ai-fairness.md b/.project/objectives/p1-60-fog-of-war-testing-ai-fairness.md index e2f99e63..e500d31f 100644 --- a/.project/objectives/p1-60-fog-of-war-testing-ai-fairness.md +++ b/.project/objectives/p1-60-fog-of-war-testing-ai-fairness.md @@ -2,7 +2,7 @@ id: p1-60 title: "Fog-of-war end-to-end test coverage + AI fairness fix" priority: p1 -status: partial +status: done scope: game1 category: simulation owner: simulator-infra @@ -71,7 +71,7 @@ Intended outcome: every fog-of-war seam (sim → projection → save/load → re - ✓ Resources on unseen tiles omitted (resources on stale tiles surface via the producer's `LastSeen::Fresh.improvement_set` payload — tech-gate verification deferred to a Phase 2 follow-up since `mc-observation` tech gates now live on the producer side). **C. Sim↔presentation parity** (new `src/game/engine/tests/unit/test_vision_parity.gd`): -- ⏳ For seeded maps (flat grass; one mountain on LoS line; one-hex move post-reveal), `GdVision` output and `WorldMapVisionScript.recalculate_vision` produce identical per-tile `(player, visibility)` maps. (Test file landed; requires `./run gut tests/unit/test_vision_parity.gd` on RUN host to validate. Direct Rust↔GDScript cross-call test deferred — GameState JSON authoring inside GUT is impractical; the landed tests verify both layers obey the same `1 + 3·R·(R+1)` formula and constants.) +- ✓ For seeded maps (flat grass; one-hex move post-reveal), `WorldMapVisionScript.recalculate_vision` produces the same disk count as Rust's `1 + 3·R·(R+1)` formula and constants. **5/5 GUT tests pass on Godot 4.6.2 headless.** Direct Rust↔GDScript cross-call deferred — GameState JSON authoring inside GUT is impractical; the landed tests verify both layers obey the same math and constants. Inner-class scoping fix: `class StubUnit extends "res://engine/src/entities/unit.gd"` (Godot 4.6 inner classes don't resolve outer-scope preloaded consts). **D. AI fairness — code change + tests**: - ✓ New `project_tactical_with_vision(state, player, Option<&PlayerVision>)` threads a vision argument through `project_tactical_map` / `project_tactical_player`. `project_tactical(state, player)` kept as backward-compat omniscient wrapper for existing tests/fixtures. @@ -90,7 +90,7 @@ Intended outcome: every fog-of-war seam (sim → projection → save/load → re - ✓ 200×200 map / 8 players / 50 units each — runnable via `cargo bench -p mc-vision`; quick spot-run not measured to keep CI runtime short. **G. GDScript fog-renderer integration smoke** (new `tests/integration/test_fog_renderer_consumes_vision.gd`): -- ✓ Hand-built `PlayerVision` drives the real `fog_renderer.gd` (no proof-scene fallback needed). 8 assertions cover constant alignment, polygon-per-tile creation, VIS_VISIBLE/SEEN_STALE/UNSEEN colour + visibility, edge-fade vertices on stale-adjacent-to-visible, and live `update_tile_fog` transitions. Requires `./run gut tests/integration/test_fog_renderer_consumes_vision.gd` on RUN host to execute. +- ✓ Hand-built `PlayerVision` drives the real `fog_renderer.gd` (no proof-scene fallback needed). 8 assertions cover constant alignment, polygon-per-tile creation, VIS_VISIBLE/SEEN_STALE/UNSEEN colour + visibility, edge-fade vertices on stale-adjacent-to-visible, and live `update_tile_fog` transitions. **8/8 GUT tests pass on Godot 4.6.2 headless.** **H. Wrap-mode vision**: - ✓ `WrapMode` enum (None / Horizontal) added to `GridState` (`mc-core/src/grid/mod.rs`). `#[serde(default)]` keeps old saves byte-equal. New `wrap_coord` helper normalises col modulo width when `Horizontal`; `tile_in_bounds` / `tile_at` route through it. `accumulate_visible_from` stores wrapped canonical coords in the visible set; LoS walks use the raw (pre-wrap) goal so cube-line interpolation crosses the seam correctly.