Run 15: known-nodes exploration (addendum stage 1), per-town debug, O(n^2) fix
Exploration addendum stage 1: per-NPC known-nodes. NPCs gather only from resources they've personally sensed (radius 22) or been told about; founders seed with home ground, children inherit parents' maps, node knowledge diffuses between neighbors like skills. Starving NPCs venture outward and sense as they go - non-random needs-driven exploration, the seed of first contact. Repopulation preserved (seeding + survival food fallback): all towns fed, population stable 86-109 over 240 days. Debug screen rebuilt: the ~68-row per-villager roster (overflowing at four-town scale) replaced with a per-town summary - pop, fed%, knowledge, debris, state breakdown, buildings, culture-colored, plus world births/deaths. Scales to any population. Performance: added phase timers and measured instead of guessing - npc.Tick was 94% of cost, O(n^2) via TickResting calling RestingOccupancy (full population scan) per building per resting NPC. Fixed with a once-per-tick occupancy cache on Building. npc phase ~4x faster and now linear; full 240-day four-town run in 180s, faster than the pre-exploration baseline. Profiling scaffolding removed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+26
-1
@@ -123,6 +123,10 @@ public partial class GameManager : Node2D
|
||||
LifeExpectancyDays = 150 + rng.Next(0, 80),
|
||||
};
|
||||
npc.Know.Capacity = 1.0f + (float)rng.NextDouble();
|
||||
// Founders know their home ground — the resources around the
|
||||
// town seed. They begin with a working local map, not blind.
|
||||
foreach (var node in World.NodesWithin(center, 130f))
|
||||
npc.KnownNodes.Add(node);
|
||||
Npcs.Add(npc);
|
||||
}
|
||||
}
|
||||
@@ -308,6 +312,10 @@ public partial class GameManager : Node2D
|
||||
LifeExpectancyDays = 150 + rng.Next(0, 80),
|
||||
};
|
||||
child.Know.Capacity = 1.0f + (float)rng.NextDouble();
|
||||
// A child inherits its parents' map — the family's known grounds,
|
||||
// passed down like everything else a generation hands forward.
|
||||
foreach (var node in a.KnownNodes) child.KnownNodes.Add(node);
|
||||
foreach (var node in b.KnownNodes) child.KnownNodes.Add(node);
|
||||
newborns.Add(child);
|
||||
BirthsToday++;
|
||||
pop[a.HomeTown] = (int)townPop + 1; // count the newborn toward capacity
|
||||
@@ -331,6 +339,23 @@ public partial class GameManager : Node2D
|
||||
{
|
||||
Grid.Rebuild(Npcs);
|
||||
|
||||
// Recompute shelter occupancy once per tick (was O(n²): each resting
|
||||
// NPC re-scanned everyone, per building). Now: one pass, cached — each
|
||||
// sleeper counts toward the nearest shelter they're actually inside.
|
||||
foreach (var b in Buildings) b.Occupancy = 0;
|
||||
foreach (var npc in Npcs)
|
||||
{
|
||||
if (npc.State != NpcState.Resting) continue;
|
||||
Building? at = null; float best = 6.25f; // within 2.5 units
|
||||
foreach (var b in Buildings)
|
||||
{
|
||||
if (b.RestCapacity <= 0) continue;
|
||||
float d = npc.Position.DistanceSquaredTo(b.Site);
|
||||
if (d < best) { best = d; at = b; }
|
||||
}
|
||||
if (at != null) at.Occupancy++;
|
||||
}
|
||||
|
||||
TotalTicks++;
|
||||
|
||||
foreach (var npc in Npcs)
|
||||
@@ -349,7 +374,7 @@ public partial class GameManager : Node2D
|
||||
TickReproduction();
|
||||
StatsLogger.LogDay(this);
|
||||
if (Day % 10 == 0)
|
||||
GD.Print($"[WorldSim] Day {Day} complete.");
|
||||
GD.Print($"[WorldSim] Day {Day} complete (pop {Npcs.Count}).");
|
||||
|
||||
if (StopAfterDays > 0 && Day >= StopAfterDays)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user