Files
settled-reach/tooling/test_planet_biome_rules.py
T
jpmschweitzerandClaude Opus 5.5 3cfec9025d fix(assets): mountain rock could never fire — kelvin compared to 0.35 (T-1295)
compute_biome receives absolute kelvin (the Whittaker table is kelvin; the
0-1 normalisation happens afterwards in simulate(), for the renderer only),
but two terrain rules compared it to 0.35. That was the case from the
generator's first commit (f84275edf):

- mountain rock, `temperature < 0.35`: never true, so the class never
  appeared through this rule on any body;
- sulfuric scrub, `temperature > 0.35`: always true.

0.35 can only mean a fraction of the world's own range, the normalisation
compute_moisture already uses, so both rules now compare against that. A
world with no temperature range reads 0 (the cold end).

tooling/test_planet_biome_rules.py pins the rule both ways on a synthetic
body: cold high ground turns to rock (>90%, since anomaly scatter repaints
~3%), and warm high ground and low ground do not. Mutation-proven: restoring
the kelvin comparison fails it at a rock share of exactly 0.000. Wired into
make test-tooling.

Sulfuric scrub remains unreachable for a different reason: it converts rock
only at elev_norm 0.25-0.65, and rock exists only above 0.65. It is left as
found, since making it reachable is a design call and belongs to the balancing
pass, so it is not pinned.

Found while shaping the D-258 biome bake. The committed reliefmaps and globes
were rendered with the bug, and the coming bake reflects the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 14:03:37 +02:00

83 lines
2.9 KiB
Python

#!/usr/bin/env python3
"""
Pins the mountain-rock rule in `compute_biome`, which compared kelvin
against 0.35 (T-1295).
`compute_biome` receives absolute kelvin, but the rule was written against a
0-1 scale, so from the generator's first commit mountain rock could never
fire. Nothing noticed, because no test asked the rule to fire, or to hold
back. These do both, on a synthetic body whose answer is known.
The sulfuric-scrub rule had the same unit bug and is fixed alongside it, but
it is not pinned here: it converts rock only at elev_norm 0.25-0.65, while
rock exists only above 0.65, so it cannot fire at all. Making it reachable is
a design call, not a units fix.
Run directly or via `make test-tooling`:
.venv/bin/python tooling/test_planet_biome_rules.py
"""
import sys
import unittest
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
import numpy as np # noqa: E402
from tooling.domains.atlas.planet.planet_simulation import ( # noqa: E402
GRID_H,
GRID_W,
compute_biome,
)
MOUNTAIN_ROCK = 18
SEA_LEVEL = 0.2
def _body(substrate: str = "silicate") -> dict:
return {
"seed": 7,
"planet_class": "temperate",
"body_type": "planet",
"physical": {"atmosphere": "standard"},
# No hydrosphere: keeps high ground out of the snow rule, so rock is
# the only rule that can claim it.
"environment": {"hydrosphere": "none", "substrate": substrate},
}
def _world(temp_k: np.ndarray, elev: np.ndarray) -> np.ndarray:
water = np.zeros((GRID_H, GRID_W), dtype=bool)
moisture = np.full((GRID_H, GRID_W), 0.5, dtype=np.float32)
return compute_biome(_body(), elev, SEA_LEVEL, water, temp_k, moisture)
class MountainRockTests(unittest.TestCase):
def setUp(self):
# West half high ground, east half low. Temperature runs 275-305 K
# west to east, the temperate band, so the high west is the cold third.
self.elev = np.full((GRID_H, GRID_W), 0.3, dtype=np.float32)
self.elev[:, : GRID_W // 2] = 0.95 # elev_norm ~0.94, above the 0.65 gate
ramp = np.linspace(275.0, 305.0, GRID_W, dtype=np.float32)
self.temp = np.tile(ramp, (GRID_H, 1))
def test_cold_high_ground_is_rock(self):
biome = _world(self.temp, self.elev)
cold_high = biome[:, : int(GRID_W * 0.30)]
share = float((cold_high == MOUNTAIN_ROCK).mean())
# The anomaly scatter repaints ~3% of land, so "all of it" is too
# strong; the dead rule scored exactly 0 here.
self.assertGreater(share, 0.9, f"cold high ground rock share {share:.3f}")
def test_warm_or_low_ground_is_not_rock(self):
biome = _world(self.temp, self.elev)
warm_high = biome[:, int(GRID_W * 0.40) : GRID_W // 2]
low = biome[:, GRID_W // 2 :]
self.assertEqual(int((warm_high == MOUNTAIN_ROCK).sum()), 0)
self.assertEqual(int((low == MOUNTAIN_ROCK).sum()), 0)
if __name__ == "__main__":
unittest.main()