| # 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: |
|
|
| ```json |
| { |
| "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:** |
| |
| ```json |
| { |
| "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: |
| |
| ```json |
| {"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. |
| |