Run 12: knowledge system, mortality, and the North's generational soul
SPEC §5 built: three skill domains (forage/woodcraft/masonry) that pay off and grow by practice; proximity diffusion soul-calibrated by absorption x teaching-drive; and mortality - the piece that makes knowledge generational. Elders die (~150-230 days), everything unshared dies with them, a youth comes of age near knowing nothing. North soul (absorption 1.6, teaching 0.9) vs South (1.0, 0.3); East teaches no one. Two findings: (1) headless soaks were contaminated by orphaned child processes writing later runs' CSVs - fixed with StopAfterDays clean self-terminate; (2) passive diffusion barely helped - the North's real culture is the young seeking elders on purpose, so added deliberate teacher-seeking and youths now cradle beside the wisest elder. North now leads knowledge at every checkpoint, but a stable town approaches the ceiling regardless, so teaching is a recovery advantage not an equilibrium one. The crisis-recovery experiment is the real test, noted in FINDINGS as next. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+79
-2
@@ -36,6 +36,11 @@ public partial class GameManager : Node2D
|
||||
[Export(PropertyHint.Range, "1,2000,1")]
|
||||
public float TicksPerRealSecond = 20f; // sim speed; ~1500 ≈ one day/sec for soaks
|
||||
|
||||
/// <summary>Auto-quit after this many days (0 = run forever). Set for
|
||||
/// headless soaks so runs self-terminate cleanly — no external kill, no
|
||||
/// orphaned process racing the next run's CSV writes.</summary>
|
||||
[Export] public int StopAfterDays = 0;
|
||||
|
||||
public World World { get; private set; } = null!;
|
||||
public List<Npc> Npcs { get; } = new();
|
||||
public List<Building> Buildings { get; } = new();
|
||||
@@ -91,14 +96,20 @@ public partial class GameManager : Node2D
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Npcs.Add(new Npc
|
||||
var npc = new Npc
|
||||
{
|
||||
Name = $"{prefix}_{(char)('A' + Npcs.Count % 26)}{(Npcs.Count >= 26 ? Npcs.Count.ToString() : "")}",
|
||||
HomeTown = town,
|
||||
Soul = soul,
|
||||
PersonalityModifier = (float)(rng.NextDouble() * 0.4 - 0.2),
|
||||
Position = center + new Vector2(rng.Next(-12, 13), rng.Next(-12, 13)),
|
||||
});
|
||||
// Staggered ages so the founding generation doesn't die
|
||||
// in one terrible week.
|
||||
AgeDays = rng.Next(0, 100),
|
||||
LifeExpectancyDays = 150 + rng.Next(0, 80),
|
||||
};
|
||||
npc.Know.Capacity = 1.0f + (float)rng.NextDouble();
|
||||
Npcs.Add(npc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,6 +209,65 @@ public partial class GameManager : Node2D
|
||||
}
|
||||
}
|
||||
|
||||
// --- Mortality: generations end, and what wasn't passed on ends too --
|
||||
|
||||
public int DeathsToday { get; set; }
|
||||
private int _bornCount;
|
||||
|
||||
private void TickMortality()
|
||||
{
|
||||
var dying = new List<Npc>();
|
||||
foreach (var npc in Npcs)
|
||||
{
|
||||
npc.AgeDays += 1f;
|
||||
if (npc.AgeDays >= npc.LifeExpectancyDays)
|
||||
dying.Add(npc);
|
||||
}
|
||||
|
||||
var rng = new System.Random(Seed + (int)TotalTicks);
|
||||
foreach (var dead in Npcs.ToArray())
|
||||
{
|
||||
if (!dying.Contains(dead)) continue;
|
||||
Npcs.Remove(dead);
|
||||
DeathsToday++;
|
||||
|
||||
// Everything unshared dies with them: skills, memories, echoes.
|
||||
// What survives is what they taught, built, and stored.
|
||||
foreach (var other in Npcs)
|
||||
other.ForgetPerson(dead);
|
||||
|
||||
// A youth comes of age in the same town, same culture, knowing
|
||||
// almost nothing — but raised among the living, not in a void.
|
||||
// They spawn beside the town's wisest elder, so the young begin
|
||||
// life within reach of what the community still remembers. The
|
||||
// town's knowledge is only what its living carry — but the young
|
||||
// start where that knowledge is.
|
||||
_bornCount++;
|
||||
Npc? elder = null;
|
||||
float bestKnow = -1f;
|
||||
foreach (var other in Npcs)
|
||||
if (other.HomeTown == dead.HomeTown && other.Know.Total > bestKnow)
|
||||
{ bestKnow = other.Know.Total; elder = other; }
|
||||
Vector2 cradle = elder?.Position ?? TownCentroid(dead.HomeTown);
|
||||
|
||||
var youth = new Npc
|
||||
{
|
||||
Name = $"{dead.HomeTown}_{(char)('A' + _bornCount % 26)}{_bornCount + 25}",
|
||||
HomeTown = dead.HomeTown,
|
||||
Soul = dead.Soul, // raised in the culture that raised them
|
||||
PersonalityModifier = (float)(rng.NextDouble() * 0.4 - 0.2),
|
||||
Position = cradle + new Vector2(rng.Next(-4, 5), rng.Next(-4, 5)),
|
||||
AgeDays = 0f,
|
||||
LifeExpectancyDays = 150 + rng.Next(0, 80),
|
||||
};
|
||||
youth.Know.Capacity = 1.0f + (float)rng.NextDouble();
|
||||
Npcs.Add(youth);
|
||||
|
||||
GD.Print($"[Passing] day {Day}: {dead.Name} dies at {dead.AgeDays:0} days " +
|
||||
$"(knowledge {dead.Know.Total:0.00} lost); {youth.Name} comes of age.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Where the community's feet actually are — new communal
|
||||
/// buildings are founded here, not at a scripted town center.</summary>
|
||||
public Vector2 CommunityCentroid()
|
||||
@@ -224,9 +294,16 @@ public partial class GameManager : Node2D
|
||||
building.DailyUpkeep();
|
||||
foreach (var npc in Npcs)
|
||||
npc.FadeEchoes(0.005f);
|
||||
TickMortality();
|
||||
StatsLogger.LogDay(this);
|
||||
if (Day % 10 == 0)
|
||||
GD.Print($"[WorldSim] Day {Day} complete.");
|
||||
|
||||
if (StopAfterDays > 0 && Day >= StopAfterDays)
|
||||
{
|
||||
GD.Print($"[WorldSim] Reached day {Day} — stopping.");
|
||||
GetTree().Quit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user