feat(terrarium): grow the world on real terrain - #18
Conversation
The terrarium ran on featureless ground: every cell walkable, every cell equally fertile. Give it a landscape instead. A wrapping value-noise field, drawn from the world's own Rng, is cut into water, fertile loam, plain soil, and bare rock. The cuts are quantiles rather than fixed elevations — raw noise is lumpy enough that a fixed sea level drowns one seed and deserts the next — so every world keeps the same balance of ground while its shape stays entirely the seed's business. The ground then does ecological work: - Nothing swims. Movement, spawning, and birth placement all respect the shoreline, and blocked steps fall through to the other axis instead of stalling. - Only herbivores climb rock. It grows nothing, so cover is paid for in hunger — and threatened prey now break for it, scoring refuge above raw distance when they flee. - Plants root only in soil and loam, and growth events are drawn from a fertility-weighted pool, so rain never falls in the lake and lowland loam outgrows plain soil. The refuges turn out to be what the food web was missing. Over 3000 ticks across six seeds, `main` goes extinct in five of them (herbivores strip the world, predators starve out first); with terrain, none go extinct and predators survive in all six, oscillating against prey instead of crashing. Also drops the stale `--` from the documented `pnpm world` invocations: pnpm 9 forwards it as a literal argument and the CLI rejects it.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d669203235
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // Offspring land beside the parent, or on top of it if that way is barred. | ||
| const nx = wrap(parent.x + dx, world.width); | ||
| const ny = wrap(parent.y + dy, world.height); | ||
| const reachable = canEnter(world, nx, ny, parent.traits.diet); |
There was a problem hiding this comment.
Check the offspring's diet before placing it
When an herbivore reproduces on or beside rock and mutation changes genome byte 2 into the predator range, this check still uses the parent's herbivore diet. spawnCreature then honors the explicit coordinates without validating them against the child's traits, so the new predator can be born on forbidden rock and remain trapped there, violating the terrain invariant. Determine passability using the mutated genome's diet or validate the final coordinates inside spawnCreature.
Useful? React with 👍 / 👎.
Summary
The terrarium ran on featureless ground — every cell walkable, every cell equally fertile. This PR gives it a landscape. A wrapping value-noise field, drawn from the world's own
Rng, is cut into water, fertile loam, plain soil, and bare rock, and the ground is then made to do ecological work: nothing swims, only herbivores climb rock, plants root only in soil and loam, and threatened prey break for cover. Same history still means the same replay, tick for tick.The refuges turn out to be what the food web was missing. Over 3000 ticks across six seeds,
maingoes extinct in five of six (herbivores strip the world bare, predators starve out first); with terrain, none go extinct and predators survive in all six, oscillating against prey instead of crashing.mainAI Model
Claude Opus 5 (
claude-opus-5[1m]), via Claude Code.Human Input Received
"write new pr for today" — no direction on what to build, no design or code supplied. Scope, mechanics, and implementation were chosen from
README.md's standing invitation to future agents ("New species, terrain, weather, seasons, ecology rules…").Key AI Decisions
KILL_CHANCE, regrowth) would have papered over the mechanism. Giving prey ground the hunter cannot climb — paid for in hunger, since rock grows nothing — is a rule rather than a fudge, and it produced the predator-prey oscillation above.waterShare,rockShare,loamShare), so a caller can flatten the world for testing;0is honoured exactly.Checks Run
pnpm lint— cleanpnpm typecheck— cleanpnpm test— 60 passing (11 new: terrain generation, share targets, degenerate worlds, passability, plants-only-on-fertile-ground, creatures-never-on-forbidden-ground over 300 ticks, prey-takes-refuge, terrain rendering)pnpm world --snapshotandpnpm world(animated) — inspected visuallymainin a scratch worktree (table above)Notes for Reviewers
Rngbefore anything else and is included inserializeWorld, so the replay test covers it. The rng draw order changed, so worlds look different frommainat the same seed — expected, and there are no golden-frame fixtures.terrainCountsis currently exercised only by tests; it exists for a future status line.--from the documentedpnpm world -- --snapshotinvocation inREADME.mdandcli.ts. pnpm 9 forwards it as a literal argument, so the documented command errors withUnknown argument: --.