From the Hoshe/Tyre review: - drainage: assert flow_accumulation/max_accumulation determinism + clamp ≥ 1 (the D-209 strength denominator); isolated-basin merge path (no panic). - subbiome: each derivable variant reachable + Volcanic never emitted. - planet_simulation: new test_sim_determinism.py — same body simulates to a bit-identical elevation array at 1024×512 (the 271-body bake can't be cheaply re-run, so a silent drift = full re-bake). Verified PASS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
72 lines
2.4 KiB
Python
72 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
||
"""Determinism guard for planet_simulation.simulate() (#963).
|
||
|
||
The 1024×512 elevation bump (D-202 amended) claims to be deterministic — the
|
||
271-body heightmap bake cannot be re-run cheaply, so a silent non-determinism
|
||
means a full regeneration. This asserts that simulating the same body twice
|
||
yields a bit-identical elevation array.
|
||
|
||
Run: uv run python tooling/planet-gen/test_sim_determinism.py
|
||
Exit: 0 = deterministic, 1 = drift detected, 2 = could not find a test body.
|
||
"""
|
||
import hashlib
|
||
import sqlite3
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
REPO = Path(__file__).resolve().parent.parent.parent
|
||
sys.path.insert(0, str(REPO / "tooling" / "planet-gen"))
|
||
|
||
import numpy as np # noqa: E402
|
||
|
||
from body_definition_parser import parse_system # noqa: E402
|
||
from planet_simulation import GRID_H, GRID_W, simulate # noqa: E402
|
||
|
||
|
||
def _elev_hash(terrain) -> str:
|
||
arr = np.ascontiguousarray(terrain["elevation"], dtype=np.float32)
|
||
return hashlib.sha256(arr.tobytes()).hexdigest()
|
||
|
||
|
||
def main() -> int:
|
||
conn = sqlite3.connect(str(REPO / "server" / "data" / "systems.db"))
|
||
rows = conn.execute(
|
||
"SELECT body_id, terrain_reference FROM bodies "
|
||
"WHERE inhabited=1 AND terrain_reference IS NOT NULL AND population>0 "
|
||
"AND system_id != 'GJ 0' ORDER BY body_id LIMIT 30"
|
||
).fetchall()
|
||
|
||
checked = 0
|
||
for body_id, tref in rows:
|
||
if checked >= 3:
|
||
break
|
||
sys_index = tref.split("/bodies/")[0] + "/index.md"
|
||
try:
|
||
defs = parse_system(sys_index)
|
||
except Exception:
|
||
continue
|
||
bd = next((d for d in defs if d.get("id") == body_id), None)
|
||
if bd is None:
|
||
continue
|
||
t1 = simulate(bd)
|
||
t2 = simulate(bd)
|
||
if not t1 or "elevation" not in t1:
|
||
continue
|
||
h1, h2 = _elev_hash(t1), _elev_hash(t2)
|
||
ok = h1 == h2 and t1["elevation"].shape == (GRID_H, GRID_W)
|
||
print(f" {body_id}: {'OK' if ok else 'DRIFT'} {h1[:16]} shape={t1['elevation'].shape}")
|
||
if not ok:
|
||
print(f"FAIL: {body_id} elevation is non-deterministic ({h1[:16]} != {h2[:16]})")
|
||
return 1
|
||
checked += 1
|
||
|
||
if checked == 0:
|
||
print("SKIP: no testable body found")
|
||
return 2
|
||
print(f"PASS: {checked} bodies simulate deterministically at {GRID_W}×{GRID_H}")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|