Skip to content

feat(terrarium): grow the world on real terrain - #18

Open
bluevisor wants to merge 1 commit into
mainfrom
ai/terrarium-terrain
Open

feat(terrarium): grow the world on real terrain#18
bluevisor wants to merge 1 commit into
mainfrom
ai/terrarium-terrain

Conversation

@bluevisor

Copy link
Copy Markdown
Owner

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, main goes 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.

seed main this PR
s1 extinct @ 1392 209 alive, 44 predators
s2 extinct @ 876 146 alive, 58 predators
s3 survives, predators gone @ 2936 135 alive, 62 predators
s4 extinct @ 1035 191 alive, 45 predators
s5 extinct @ 1274 131 alive, 44 predators
s6 extinct @ 1194 507 alive, 1 predator

AI 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

  • Terrain over the other options on the list. Weather and seasons modulate an existing global rate; terrain changes the space the simulation runs in, which is the larger structural change and the one the other ideas can later sit on top of.
  • Quantile cuts, not fixed elevations. A fixed sea level on this noise field gave 21%–54% water depending on seed — one world drowned, the next was a desert. Slicing the elevation field by share of cells keeps every world habitable while leaving its shape entirely up to the seed.
  • Wrapping lattice. The world is a torus; a creature walking off the east edge has to find the shoreline it left on the west.
  • Rock as herbivore-only refuge. The first two drafts collapsed — terrain shrinks the grazing area, so first the herd overgrazed (extinct @ 1280), then predators bloomed in the tighter space (extinct @ 547). Fixing that with a tuning constant (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.
  • Escape rewritten to score all four directions, refuge above distance. The old dominant-axis retreat fell back to a random scramble when blocked, which turned every shoreline into a kill box.
  • Fertility-weighted growth pool so rain never falls in the lake, and loam outgrows plain soil, without a second per-terrain cap to keep in sync.
  • Terrain shares are options (waterShare, rockShare, loamShare), so a caller can flatten the world for testing; 0 is honoured exactly.
  • No new dependencies. No new CLI flags. Terrain is part of the world, not a mode.

Checks Run

  • pnpm lint — clean
  • pnpm typecheck — clean
  • pnpm 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 --snapshot and pnpm world (animated) — inspected visually
  • 3000-tick × 6-seed extinction sweep against main in a scratch worktree (table above)

Notes for Reviewers

  • Determinism is preserved: terrain is generated from the world's Rng before anything else and is included in serializeWorld, so the replay test covers it. The rng draw order changed, so worlds look different from main at the same seed — expected, and there are no golden-frame fixtures.
  • terrainCounts is currently exercised only by tests; it exists for a future status line.
  • Drive-by: dropped the stale -- from the documented pnpm world -- --snapshot invocation in README.md and cli.ts. pnpm 9 forwards it as a literal argument, so the documented command errors with Unknown argument: --.
  • Follow-ups I deliberately left out of scope: predators sensing prey straight across a lake they cannot cross, and terrain-aware pathfinding better than "try the other axis".

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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/terrarium/world.ts
// 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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant