fix(simulation): per-body TerrainAnalysis LRU + full-path determinism test (PR #187 C1/C3)
C1 (Tyre): the DeriveWindow branch re-ran the ~45ms run_layer1 for EVERY uncached window — the dominant pan-around-one-body path paid ~52ms per new window while the doc claimed one-time-per-body. TerrainAnalysisCache: private per-body LRU (capacity 8, ~2MB/entry, true access-recency eviction — which body the player keeps panning IS a recency signal) held as Arc<Mutex<>> on GenerationQueue itself, the in_flight field pattern, because run_work_item executes on a Rayon worker where the main-thread caches are unreachable. DeriveWindow now calls get_or_derive; the variant doc states the real model (first window per body pays ~45ms once; subsequent windows any center/n hit the LRU; eviction re-pays). End-to-end test proves two windows on one body via two connections share exactly one cache entry. C3 (Tyre): the determinism test reused one TerrainAnalysis — proving the packer, not the derivation. New test runs run_layer1 twice independently, asserts field-by-field analysis agreement, then byte-identical packed windows end to end (D-227 save-critical proof). Packer-only test kept alongside. atlas:: 564/564; full lib 1778/1778; timing-sensitive suites 3x stable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1283,6 +1283,61 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// FULL-PATH determinism (PR #187 review — Tyre C3, binding, load-bearing
|
||||
/// for save-file lineage under D-227): the test above reuses ONE `ta` for
|
||||
/// both passes, which only proves `build_district_window_layer` (the
|
||||
/// packer) is a pure function of its arguments — it says nothing about
|
||||
/// whether re-running `run_layer1` itself (the D8 drainage pass +
|
||||
/// `TerrainAnalysis::analyze`) is deterministic, which is exactly what the
|
||||
/// production `DeriveWindow` path depends on (T-1137's `TerrainAnalysis`
|
||||
/// re-derive / `TerrainAnalysisCache::get_or_derive` on a cache miss, and
|
||||
/// `aliveness_probe --render`'s workaround before it).
|
||||
///
|
||||
/// This test runs `run_layer1` TWICE, independently, from the SAME
|
||||
/// `(seed, heightmap)` inputs — no shared `ta` — and asserts the two
|
||||
/// COMPLETE `DistrictWindowLayer` outputs (derive AND pack) are
|
||||
/// byte-identical. D-227's save-file guarantee ("same seed/body/position →
|
||||
/// same derived output, always") is only as strong as the weakest link in
|
||||
/// that chain; this closes the gap the packer-only test left open.
|
||||
#[test]
|
||||
fn full_path_two_independent_run_layer1_passes_produce_identical_window() {
|
||||
let hm = window_test_hm();
|
||||
let params = window_test_params();
|
||||
let climate = crate::atlas::district_profile::ClimateConstants::default();
|
||||
let seed = SeedChain::root(11).derive(SeedDomain::Body, 5);
|
||||
let n = 6u32;
|
||||
let center = (4, -1);
|
||||
|
||||
// Two INDEPENDENT calls to run_layer1 — each re-runs D8 drainage +
|
||||
// TerrainAnalysis::analyze from scratch on the SAME heightmap, exactly
|
||||
// mirroring what a cold TerrainAnalysisCache miss does on the real
|
||||
// DeriveWindow path (or a second body eviction re-pay).
|
||||
let (_, ta_pass1) = crate::atlas::layer1::run_layer1(&hm);
|
||||
let (_, ta_pass2) = crate::atlas::layer1::run_layer1(&hm);
|
||||
|
||||
// Confirm the two independent TerrainAnalysis derivations themselves
|
||||
// agree field-by-field — a precise failure signal if drainage/analyze
|
||||
// ever introduces nondeterminism (unordered iteration, uninitialized
|
||||
// memory, etc.) BEFORE the packer even runs.
|
||||
assert_eq!(ta_pass1.ocean_mask, ta_pass2.ocean_mask);
|
||||
assert_eq!(ta_pass1.lake_mask, ta_pass2.lake_mask);
|
||||
assert_eq!(ta_pass1.water_dist, ta_pass2.water_dist);
|
||||
assert_eq!(ta_pass1.slope_deg, ta_pass2.slope_deg);
|
||||
assert_eq!(ta_pass1.elev_pct, ta_pass2.elev_pct);
|
||||
|
||||
// Now the FULL path: pack a DistrictWindowLayer from each independent
|
||||
// TerrainAnalysis and confirm the complete served payload agrees.
|
||||
let window_from_pass1 =
|
||||
build_district_window_layer(seed, "test_body", ¶ms, &ta_pass1, center, n, &climate);
|
||||
let window_from_pass2 =
|
||||
build_district_window_layer(seed, "test_body", ¶ms, &ta_pass2, center, n, &climate);
|
||||
assert_eq!(
|
||||
window_from_pass1, window_from_pass2,
|
||||
"two independent run_layer1 derivations from the same (seed, heightmap) \
|
||||
must pack to a byte-identical DistrictWindowLayer end to end (D-227)"
|
||||
);
|
||||
}
|
||||
|
||||
/// [`DistrictWindowCache`] insert/get round-trips, and a capacity-1 cache
|
||||
/// evicts the oldest entry FIFO — mirroring `BodyWorldStateCache`'s own
|
||||
/// `evicts_lru_on_overflow` precedent, adapted to this cache's
|
||||
|
||||
Reference in New Issue
Block a user