fix(simulation): synthetic-overflow placements get the ocean-mask guard (T-1206)
synthetic_attractor now takes the terrain analysis and land-corrects its pure-arithmetic position via a bounded nearest-land ring walk (T-1116's pattern: row-major tie-break, column wrap, row clamp, MAX_LAND_SEARCH_RING=128 sized empirically — real polar ocean bands push nearest land up to 125 cells). Land positions pass through UNTOUCHED — verified by direct before/after scan of all 267 real bodies: 63 land-arithmetic placements byte-identical, and every golden/determinism harness passes unchanged. The gap was real and widespread: 46 of 109 synthetic-overflow placements sat in open water at seed 42 (e.g. GJ903c at a genuine polar ocean cell); post-fix zero, with all 109 preserved (confirmed at a second seed). Degradation is defined and pinned: no land within the bound -> the synthetic attractor is skipped and Phase 5's existing not-placed warning reports it — never a panic, never a fabricated water position (the bound never triggers on any scanned real body). 9 new unit tests; road_graph's anchor comment and the D-210 amendment record the gap CLOSED (validated). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -95,18 +95,27 @@ const MIN_CELL_COST: u32 = RIVER_COST;
|
||||
/// edge touching it (the documented T-1116 bug: GJ251c land 0.55 / GJ380c land
|
||||
/// 0.588 — land-majority BODIES with 0 routable edges).
|
||||
///
|
||||
/// **Known gap this relaxation also happens to cover, but does not fix at the
|
||||
/// source:** D-211's Phase-4 **synthetic overflow** path
|
||||
/// (`attractor_matching.rs::synthetic_attractor`, used when a body has more
|
||||
/// cities than real attractors) picks a position by pure grid arithmetic
|
||||
/// (`grid_h/2, grid_w/4` then a fixed spacing-walk) with **no terrain check at
|
||||
/// all** — it takes no heightmap/`TerrainAnalysis` argument and never reads
|
||||
/// `ocean_mask`. A synthetic-overflow settlement CAN land in open ocean. This
|
||||
/// routing relaxation still degrades that case gracefully (anchors to the
|
||||
/// nearest passable cell within `COASTAL_ANCHOR_MAX_RING`, or leaves it
|
||||
/// unrouted beyond that), but the placement itself is not guaranteed land —
|
||||
/// that gap belongs to D-211/Layer 3, not this file, and is reported rather
|
||||
/// than silently patched over here (flagged in PR #215 review, not yet ticketed).
|
||||
/// **Gap this relaxation also happened to cover, now CLOSED at the source
|
||||
/// (T-1206, 2026-07-26):** D-211's Phase-4 **synthetic overflow** path
|
||||
/// (`attractor_matching.rs::synthetic_attractor`) used to pick a position by
|
||||
/// pure grid arithmetic (`grid_h/2, grid_w/4` then a fixed spacing-walk) with
|
||||
/// **no terrain check at all** — a synthetic-overflow settlement could land
|
||||
/// in open ocean (confirmed on 46 real bodies at world seed 42/"yolo" before
|
||||
/// the fix). `synthetic_attractor` now takes an `Option<&TerrainAnalysis>`
|
||||
/// and, when supplied, guards the arithmetic position: already-land stays
|
||||
/// byte-identical (verified unmoved on all 63 real land-arithmetic
|
||||
/// placements), water gets nudged to the nearest land cell via a
|
||||
/// deterministic ring-walk (`nearest_land_cell`, the same tie-break pattern
|
||||
/// as this file's own `nearest_passable_cell` below, at native/working-grid
|
||||
/// resolution rather than the routing grid's downsample), and a city with no
|
||||
/// land within the bounded search radius is SKIPPED entirely (never
|
||||
/// fabricated, never a panic — reported via the existing Phase-5
|
||||
/// name-fulfillment warning). This routing relaxation still degrades
|
||||
/// gracefully for the coastal-cell/downsample case it was built for (anchors
|
||||
/// to the nearest passable cell within `COASTAL_ANCHOR_MAX_RING`, or leaves
|
||||
/// it unrouted beyond that) — the two fixes are independent and both apply
|
||||
/// (T-1206 guarantees the placement pixel is land; this relaxation still
|
||||
/// covers the routing CELL being water-majority at downsample granularity).
|
||||
///
|
||||
/// The fix anchors routing to the NEAREST passable routing cell (ring-expansion
|
||||
/// search, deterministic tie-break) rather than the settlement's own impassable
|
||||
|
||||
Reference in New Issue
Block a user