Run 10: organic recovery achieved - minerals are not a commons

Recovery blocker was material, not motivational. Two fixes: (1) harvest
floors and commons debris apply only to living, regenerating resources -
stone does not regrow, so "leave half" merely stranded the world supply
and kept the homeless homeless forever; minerals now quarry to depletion
for every soul equally. (2) Matter conservation: idle variety-gathering
touches only renewables, and packs may only shed what the land replaces
- lossy make-room had milled 900+ stone to dust during demand flapping.

Verified over 260 headless days: year-1 winter still wrecks the naive
town, it rebuilds in 20 days, famine survivors found the first granaries
from hunger memory, and year-2 winter passes with zero ruins on stored
reserves. The town has a history now: naive youth, catastrophe,
learning, resilient maturity.

Remaining tuning in FINDINGS.md: granary sprawl, post-famine nourishment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 15:23:18 -04:00
parent 676a9f3f31
commit e661a3f739
7 changed files with 7011 additions and 7030 deletions
+19 -3
View File
@@ -78,6 +78,11 @@ public class Npc
/// who they are becoming. Indexed by (int)NpcState.</summary>
public int[] StateTicksToday = new int[5];
// --- Diagnostics (read-only views for the stats logger) ---------------
public MaterialKind CurrentDemand(GameManager gm) => PickGatherTarget(gm);
public int RoughNights => _unshelteredRests;
public bool HungerMemory => _hungerMemory;
public NpcState DominantStateToday()
{
int best = 0;
@@ -116,6 +121,17 @@ public class Npc
private static readonly MaterialKind[] GatherKinds =
{ MaterialKind.Wood, MaterialKind.Stone, MaterialKind.Clay };
// Idle variety only touches what regrows. Minerals are mined on purpose
// (construction, a roof of your own) — never as a hobby. Run 10 taught
// us that idle mining + lossy packs can mill a world's stone to dust.
private static readonly MaterialKind[] VarietyKinds =
{ MaterialKind.Wood, MaterialKind.Food };
// What a pack may shed to make room: only matter the land replaces.
// Set-down stone is stone destroyed — and the world doesn't make more.
private static readonly MaterialKind[] DroppableKinds =
{ MaterialKind.Wood, MaterialKind.Food, MaterialKind.Clay };
/// <summary>
/// One simulation tick = one in-game minute (SPEC §9).
/// Order per SPEC §10: vitals check → day/night bias → state behavior → fatigue.
@@ -253,9 +269,9 @@ public class Npc
return MaterialKind.Wood;
}
MaterialKind best = GatherKinds[(int)(Mathf.Abs(PersonalityModifier) * 100f) % GatherKinds.Length];
MaterialKind best = VarietyKinds[(int)(Mathf.Abs(PersonalityModifier) * 100f) % VarietyKinds.Length];
float least = float.MaxValue;
foreach (var kind in GatherKinds)
foreach (var kind in VarietyKinds)
{
Inventory.TryGetValue(kind, out float have);
if (have < least) { least = have; best = kind; }
@@ -292,7 +308,7 @@ public class Npc
Inventory.TryGetValue(demanded, out float haveDemanded);
if (haveDemanded >= EffectiveCapacity * 0.25f) return;
foreach (var kind in GatherKinds)
foreach (var kind in DroppableKinds)
{
if (kind == demanded) continue;
if (TotalCarried() <= EffectiveCapacity * 0.5f) break;