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>
83 lines
2.9 KiB
Python
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()
|