world-simulator / GAME_DESIGN.md
DeltaZN
feat: rename god -> world
c58d3eb
|
Raw
History Blame Contribute Delete
19.5 kB

A newer version of the Gradio SDK is available: 6.20.0

Upgrade

GAME_DESIGN.md — WORLD_SIMULATOR: "Overseer vs Chaos"

This document is the single source of truth for all coding agents. If code and this document disagree, this document wins. Do not invent your own balance numbers — use the constants below. All constants should live in one config file (config/game_balance.py or equivalent) so they can be tuned without touching logic.


0. One-liner

A village of tiny LLM citizens trying to survive and multiply, guided by an AI Overseer — while the human player acts as the God of Chaos trying to destroy them.

Architecture principle (unchanged from current codebase):

  • LLM = picks NPC actions only.
  • Engine = the only source of truth. Validates every action, applies all consequences (hp, hunger, inventory, trust, memory, movement, death, birth).
  • Overseer = a larger LLM that reads logs/snapshots and issues directives through the existing directive system. The engine validates Overseer directives exactly like God Console directives.

1. Win / Lose conditions

Condition Value
WIN (village) Population reaches 12 alive NPCs, OR the village survives 600 ticks with population ≥ 3
LOSE (village) Population reaches 0
Game length target A typical session ends in 300–600 ticks (~5–10 min of real time)

world.game_status: "running" | "win_population" | "win_survival" | "lose" — must be present in every snapshot.

When the game ends, the engine emits a game_over event containing summary stats: total births, deaths (by cause), houses built, beasts killed, peak population, final scoreboard.


2. Core NPC stats

Stat Range Notes
hp 0–100 0 = death
hunger 0–100 100 = starving (hp drain)
fear 0–100 affects goal selection
safety 0–100 NEW. 100 required for reproduction
age ticks NEW. Increments +1 every tick
max_age ticks NEW. Randomized at spawn: 400 ± 80 (uniform 320–480)
importance float ≥ 0 NEW. Derived score, see §8
reproduction_cooldown ticks NEW. Counts down to 0

Stat dynamics per tick

Rule Value
Hunger growth +0.8 / tick
Starvation if hunger ≥ 70: hp −0.5 / tick; if hunger ≥ 90: hp −1.5 / tick
Eating (consume, costs 1 food) hunger −40, hp +5
Healing (heal, costs 1 herbs) hp +25 (cap 100)
Fear gain beast within 8 units: fear → at least 60, +20 per attack witnessed
Fear decay −2 / tick when no beast within 12 units
Old age death when age ≥ max_age: NPC dies, cause old_age, witnesses get a memory episode

Safety (NEW)

safety is computed, not LLM-controlled:

  • Inside a completed house AND no living beast within 12 units of the house: safety +12 / tick (cap 100).
  • Inside a house but a beast is within 12 units: safety frozen (no gain, no loss).
  • Outside any house: safety −8 / tick (floor 0).
  • Hostile NPC directive targeting this NPC within 10 units counts as a threat (same as beast).

This means an NPC needs ~9 consecutive calm ticks at home to reach safety 100. That is the intended pacing.


3. Classes (roles)

Every NPC has role: "guard" | "builder" | "gatherer", fixed at spawn.

Action whitelist is enforced by the engine BEFORE the LLM is called. The LLM only ever sees the actions its role allows. Smaller action space = better behavior from small models.

Shared actions (all roles)

move_to_resource, consume, heal, flee, communicate (help_request / warning / trade_request / social), transfer, go_home (NEW: move into nearest house with free capacity), wander.

Gatherer 🌾

  • Extra actions: gather (any resource type), steal.
  • Combat: may attack only in self-defense (beast within 2 units), damage multiplier ×0.3.
  • Intended loop: gather food/herbs/wood → transfer wood to builders, food to hungry NPCs → go home → reproduce.

Guard 🗡

  • Extra actions: attack, defend, patrol (NEW: cycle between houses / village center).
  • Damage multiplier ×1.5; base damage 10 → effective 15 per hit.
  • Spawns with weapon: 1.
  • Cannot gather, cannot steal.
  • Engine-level reflex (not LLM): if a beast is within 10 units of any house or NPC, the guard's goal is forced to engage_threat (LLM then picks attack/defend/communicate within that goal).

Builder 🔨

  • Extra actions: build (see §4), gather restricted to wood only.
  • Cannot attack (flee/defend only), damage multiplier ×0.3 in self-defense.
  • Intended loop: collect/receive wood → build houses → houses enable reproduction.

Role balance rules

  • New NPC (born) inherits a role chosen to fix the biggest scarcity: target ratio gatherer : guard : builder = 3 : 2 : 1. If ratios are balanced, inherit a parent's role.
  • Starting village (tick 0): 6 NPCs = 3 gatherers, 2 guards, 1 builder.

4. Houses & building

New entity:

{
  "id": "house_001",
  "position": {"x": 0, "z": 0},
  "hp": 60,
  "max_hp": 60,
  "state": "under_construction | completed",
  "build_progress": 0,
  "capacity": 3,
  "occupant_ids": []
}
Rule Value
Build cost 5 wood (consumed from builder inventory at start)
Build time 10 ticks of the builder staying within 2 units performing build (progress +1/tick; pauses if builder flees, resumes later)
Capacity 3 NPCs
House radius ("inside") 2.5 units
Min distance between houses 4 units (engine rejects closer build attempts)
Beasts vs houses Beasts cannot damage NPCs inside a completed house. A beast whose target hides indoors attacks the house: 5 dmg/hit. House at 0 hp is destroyed; occupants are exposed and fear → 80
Starting houses 1 completed house at village center (so reproduction is possible from tick 0, but one house caps growth — building matters)

Engine emits events: build_started, build_completed, house_damaged, house_destroyed.


5. Reproduction & lifespan

An NPC reproduces (engine-checked every tick, no LLM involvement in the check) when all are true:

  1. hp ≥ 95
  2. hunger ≤ 5
  3. safety == 100
  4. age ≥ 50
  5. reproduction_cooldown == 0
  6. NPC is inside a completed house with free capacity for the child OR village population < cap
  7. Population < 16 (hard cap)

Effects of birth:

  • New NPC spawns at the house: age 0, hp 70, hunger 20, fear 0, empty inventory (guards still get weapon 1), role per §3 scarcity rule, max_age rerolled.
  • Parent: reproduction_cooldown = 60 ticks, hunger +30 (reproduction is costly — prevents runaway loops).
  • Trust: child ↔ parent initialized at +0.6.
  • Event npc_born + memory episodes for parent (self) and house occupants (witness).
  • Child is a full NPC immediately (no childhood mechanics — out of scope).

Death (any cause: beast, starvation, old age, NPC attack):

  • Event npc_died with cause.
  • Witnesses within 10 units get a memory episode (emotional_weight 0.9).
  • Inventory drops as a resource node at death position (lootable via gather).

6. Beasts

Mostly unchanged; tuned values:

Param Value
Beast hp 60
Beast damage 9 per hit (NPC needs ~2 hits survived to feel danger; not one-shot)
Attack radius 1.5 units
Target priority nearest NPC outside houses; prefers hp < 50 if within 1.3× distance of nearest
Retreat at hp < 20, beast flees to map edge and despawns after 15 ticks
MAX_BEASTS 3 (natural spawns); God Console can exceed up to 6
Natural spawn cooldown 1 beast per 60–90 ticks (random), only while population ≥ 4
Beast vs guard math Guard (15 dmg/hit) kills a beast in 4 hits; beast kills a full-hp NPC in 12 hits → 2 guards handle 1 beast comfortably, 1 guard wins but ends wounded. 3 beasts overwhelm 2 guards — that's the Chaos lever.

7. Resources

Type Nodes at start max_amount Regen Notes
food 4 5 +1 per 8 ticks consumed via consume
herbs 2 4 +1 per 12 ticks consumed via heal
wood 3 6 +1 per 10 ticks consumed via build
weapon 1 2 +1 per 40 ticks picked up via gather, mostly for replacing dead guards
  • gather = 1 unit per tick within 1.5 units of node.
  • Famine (Chaos tool) halves amount and max_amount of all food nodes for 100 ticks.
  • Total food throughput at start: 4 nodes × 1/8 = 0.5 food/tick ≈ supports ~10 NPCs eating every ~50 ticks. Tight but survivable — scarcity is intentional.

8. Importance score

Recomputed by engine every 10 ticks (visible in UI and Overseer context, used by nothing else for now):

importance = 1.0
           + 2.0 * (role_scarcity)            # 1 / count_of_same_role_alive
           + 0.5 * beasts_killed
           + 0.3 * resources_transferred
           + 0.4 * children_count
           + 0.5 * houses_built               # builders

Purpose: gives the Overseer an explicit signal whom to protect ("our last builder!") and gives judges a visible "value of a life" number. No other mechanic depends on it — keep it cheap.


9. Trust (unchanged primitives, fixed deltas)

Event Trust delta
transfer (received a resource) +0.15
Real help: attacked/defended a threat near me while I was in danger +0.20
steal (victim and witnesses) −0.30
NPC attacked NPC −0.40
Beast relationship fixed −1.0
help_request alone 0 (asking isn't helping)
Clamp −1.0 … +1.0
"Trusted ally" threshold for cooperation goals trust > 0.3

10. Overseer (the AI commander)

Param Value
Cycle every 8 ticks
Max directives per cycle 3
Modes off / advisor (thoughts shown, directives NOT applied) / autopilot (directives applied)
Model configurable endpoint, separate from NPC model (larger model)
Fallback on API error/timeout (>10s) or invalid JSON: skip cycle, log overseer_skipped, never block the world tick

Overseer context (compact, ≤ ~1500 tokens): tick, game_status, population by role, win-condition progress, list of NPCs (id, role, hp, hunger, safety, position zone, importance, current goal), houses (state, hp), resource node totals, active beasts (position, hp, current target), last 20 event-log lines, current chaos events.

Overseer output — strict JSON only:

{
  "thoughts": "Two beasts north. Boris (last builder, importance 4.1) must hide. Guards intercept.",
  "directives": [
    {"npc_id": "npc_003", "directive": "go home and stay inside until beasts are gone"},
    {"npc_id": "npc_001", "directive": "attack beast_2 together with npc_005"}
  ],
  "priority_alert": "Protect the last builder"
}

Directives are injected via the existing directive system; the engine validates radius/movement/damage as it does for God Console. A directive cannot grant actions outside the NPC's role whitelist. Invalid npc_id → directive dropped + logged.


11. Scoreboard: Overseer vs Chaos

Event Points
npc_born Overseer +3
build_completed Overseer +2
Beast killed by NPCs Overseer +2
npc_died (any cause) Chaos +3
house_destroyed Chaos +2
steal occurred Chaos +1 (social decay counts as chaos)

Score is cosmetic (does not affect win/lose) but must be in every snapshot and prominent in UI.

Chaos tools (God Console quick-buttons)

Tool Effect Cooldown
Spawn Beast 1 beast at map edge 20 ticks
Beast Pack 3 beasts 80 ticks
Famine food nodes halved for 100 ticks 120 ticks
Maniac hostile directive on a random NPC: attack others (engine-validated; victim can flee/call help) for 60 ticks or until subdued 100 ticks

Cooldowns exist so the player cannot trivially insta-wipe the village; the duel must look winnable for both sides. Free-form God Console directives remain available (power user mode).


12. Goal selection priority (deterministic layer above the LLM)

Engine assigns a goal; the LLM picks the concrete action within it. Priority order (first match wins):

  1. survive_threat — beast/hostile within 8 units → (guard: engage; others: flee / go_home / help_request)
  2. engage_threat — guards only, beast within 10 units of village
  3. recover — hp < 40 → heal if herbs, else seek herbs / go home
  4. eat — hunger ≥ 60 → consume if food, else trade_request to trusted ally with surplus, else gather food; if hunger ≥ 85 and no options → steal becomes available (gatherers)
  5. obey_directive — active Overseer/God directive (note: threats and critical needs above CAN preempt a directive — citizens are not robots; this is intended and makes autopilot imperfect)
  6. settle — hp ≥ 80, hunger ≤ 30, no threats → go_home, rest toward reproduction
  7. work — role routine: gatherer gathers scarcest resource, builder builds/gathers wood, guard patrols
  8. social — communicate / share surplus / wander

13. Event log (the spine of UI and Overseer)

Every engine consequence emits a structured event:

{"tick": 142, "type": "npc_born", "actor_id": "npc_003", "target_id": "npc_009", "summary": "Elena gave birth to Mira", "severity": "good"}

type enum (minimum): beast_spawned, beast_attack, beast_killed, beast_retreat, npc_attack, npc_died, npc_born, build_started, build_completed, house_damaged, house_destroyed, gather, transfer, steal, heal, consume, help_request, directive_issued, directive_completed, chaos_event, overseer_skipped, game_over.

severity: good | neutral | warning | danger. The UI event feed and the Overseer context both read from this log. No mechanic should be invisible — if it happened, it's an event.


14. Balance targets (what playtests must show)

  1. No chaos, no overseer: village survives 300+ ticks, builds ≥ 1 house, ≥ 2 births. Median run does NOT die out.
  2. Scheduled chaos (beast@50, famine@120, pack@200, maniac@280), overseer OFF: village usually loses or ends crippled (population ≤ 3).
  3. Same chaos, overseer ON (autopilot): village survives noticeably more often (target: ≥ 50% of runs reach tick 400).
  4. Max-aggression human chaos: village can be destroyed — Chaos must be able to win.
  5. No runaway growth: population should not hit the 16 cap before tick ~250 in a calm run.

If a target fails, tune numbers in this file only (hunger rate, beast damage, regen, cooldowns) — do not add mechanics.


15. Out of scope (do NOT build)

Factions, economy/money, crafting beyond build, childhood/aging stages, social status & power hierarchy (README roadmap only), multiplayer, persistence/SQLite, long-term story arcs, new LLM tools beyond the action lists above, 3D polish at the expense of gameplay.


16. Acceptance tests (headless, must stay green)

scripts/headless_sim.py (deterministic fallback, no LLM, fixed seed) must assert over a 300-tick run:

  1. ≥ 1 build_completed
  2. ≥ 1 npc_born
  3. ≥ 1 beast engaged by a guard (npc_attack with beast target by a guard)
  4. ≥ 1 death with correct cause attribution
  5. Population never negative; no crashes; every tick ≤ reasonable time budget
  6. Role whitelists never violated (no gather by guards, no attack actions chosen by builders)
  7. Reproduction never fires when any condition in §5 is false (property check)
  8. game_status transitions correctly on win/lose seeds

ADDENDUM A: Observability & Perception

A1. Tick ledger

Every server run creates logs/run_<UTC timestamp>/ containing:

  • ledger.jsonl: one JSON object per record.
  • pretty.log: one tail-friendly, truncated mirror line per record.

Within each tick, records are ordered:

  1. god_command, only when a queued God Console or chaos command is applied at tick start. It includes raw input, parsed interpretation, and applied effects.
  2. overseer_request, with the final prompt/messages, model name, and mode.
  3. overseer_response, with raw model output, parsed JSON, accepted/rejected directives, rejection reasons, and latency.
  4. For each NPC in stable living-NPC order: npc_request, then npc_response; if the LLM is skipped, npc_fallback.
  5. engine_events, containing every event applied during the tick.

No model interaction or deterministic fallback path may be silent.

A2. Queued God and chaos commands

God Console and chaos endpoint calls queue intent for the next tick. They do not mutate the world immediately. At tick start, the engine applies queued commands first, logs god_command, and only then builds Overseer and NPC model prompts.

A3. Clean model perception

WorldState.living_npcs() is the only source for live NPC target enumeration. Dead, corpse, or removed entities must never appear as valid targets for attack, talk, help, follow, flee-from, memory-threat resolution, directives, or Overseer summaries. Dead NPCs may remain only as renderable corpse entities and memory episodes.

NPC prompts are compact and truthful. They contain only:

  • own state and current goal;
  • live nearby threats;
  • live nearby allies with trust;
  • reachable non-empty resources;
  • completed houses with free capacity;
  • active directive with remaining TTL;
  • top 5 relevant memories;
  • allowed action names;
  • 3 to 5 short behavior rules.

A4. Directives

Each directive has ttl_ticks, default 24. Expired directives are cleared and logged. Each NPC has at most one active directive; a new directive replaces the previous one. Threats and urgent survival needs may still preempt directives.

A5. Death consequences

On NPC death:

  • witnesses within 10 world units receive a memory episode with emotional_weight = 0.9;
  • if the killer is a beast, witnesses gain fear +30 and set that beast relationship to -1.0;
  • if the killer is an NPC, witnesses reduce trust toward the killer by -0.5;
  • inventory drops as lootable resource nodes at the death position;
  • the dead NPC is purged from live target lists, house occupancy, beast targets, help/focus targets, active directives, and Overseer/NPC contexts during the same tick.

A6. Observation workflow

A playtest must run at least 100 ticks with live configured endpoints when reachable. The resulting ledger is inspected manually and summarized in OBSERVATIONS.md with:

  • three full NPC request/response pairs and a judgment for each;
  • one full Overseer request/response cycle and judgment;
  • accepted, repaired, rejected, and fallback action percentages;
  • top five concrete problems with ledger-line evidence;
  • a second 100-tick rerun after fixes and a before/after acceptance-rate table.

If live endpoints are unreachable, record the exact error, run deterministic fallback, and mark live validation pending.

scripts/playtest_agent.py runs the scheduled-chaos scenarios from §14 and produces playtest_report.md with: event timeline, population-over-time data, which causal chains occurred (beast→fear→help_request→guard response→trust change; hunger→gather/steal; build→reproduce), and a balance verdict.