Deterministic engine internals
This page is derived from the canonical developer-reference; its content has one source of truth.
Engine ownership and deterministic loop
Source: src/game/world.ts, src/game/decisions.ts, src/game/runtime.ts.
createSimulation({seed, config, initialPopulation?, initialResources?}) clones and validates the rule graph. The default world is a 50×50 torus (2,500 cells), uses an unsigned 32-bit seed, has five byte-range traits per organism, energy capped at 255, and runs at most 180 generations. Initial resources and organisms can be injected for deterministic tests. ENGINE_VERSION is currently lifepot-ca-8; recorded ca7 runs select the explicit legacy engine.
The simulation's seeded PRNG drives resource initialization, placement, movement and reproduction choices, and mutation. Each generation calculates resource growth/hazards and organism actions from the previous state, then derives new aggregates/history and outcome. Organisms feed on basal resources only when their rule role allows it. Predation requires a directional graph relation (or an allowed cannibalistic self-relation); population counts do not create food-web edges. Reproduction transfers 38% of basal-parent energy (34% for hunter parents) to the child, rather than creating energy. In lifepot-ca-8, a selected mutation target chooses its corresponding heritable trait; without a target, selection remains seeded-random. Mutation is bounded, may fail at trait bounds, and may change variant identity; strategy and traits remain finite. Extinction-conditioned policies require a configured species that was actually populated and then disappeared; never-populated slots do not trigger them. New runs use lifepot-ca-8. Recorded lifepot-ca-7 runs retain their original mechanics, mutation selection and activation semantics, including through replay restart and sharing. Applying old proposals under ca8 is a separately labeled counterfactual, not an exact replay.
The engine owns movement, feeding, resource growth, hazards, reproduction, mutation, death, scheduled rule activation, and outcome. These are deliberately toy-model mechanisms, not calibrated biological quantities. Basal feeders and consumers pay positive minimum upkeep after bounded social cost reductions; cooperation/mutualism reduces expenditure, never creates energy. Predation yields a fraction of the captured prey's available energy: efficient_kill 0.80, ordinary hunting 0.69, and pack_hunting 0.61, so strategy effects remain distinct while feeding-event energy equals the credited gain and cannot exceed prey energy. Competition remains non-feeding pressure. fitness influences mechanics; it does not specify a guaranteed result. The final extinct outcome is zero population; after the generation limit, a surviving world is thriving only when prey count is at least 50 and predators remain, otherwise surviving.
Bounded decisions, frozen observations, and ledger
Decision schemas and allowed triggers/strategies live in src/game/decisions.ts. src/game/runtime.ts advances deterministically until a trigger, freezes and hashes the observation, and binds the accepted decision to that exact observation, generation, trigger and graph version. A decision must cover the declared probability choices, have valid probabilities and select a maximum-probability option. Species directives are checked against configured species. The model cannot send state mutations, arbitrary code, or undeclared enum values.
A trigger cannot occur before generation 12, within 12 generations of the previous accepted decision, or after eight ledger entries. Accepted entries remain ordered. Missing/invalid model output takes the labeled deterministic fallback path; no invalid proposal is treated as an accepted model decision. Cooldown, trigger, observation, generation, and graph-version checks are enforced in the runtime, not trusted from prose.