diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-19 02:37:20 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-19 02:37:20 +0800 |
| commit | 2f46d1789088eb165271bd915844376aa5a84768 (patch) | |
| tree | feeb8acc35333660652d9fe1052d6ea536d43bbf /src/LivingVillage.Kernel.Tests/DeterminismTests.fs | |
| parent | 4627a29302aa780ba8de1e537a869650601debc3 (diff) | |
| download | living-village-2f46d1789088eb165271bd915844376aa5a84768.tar.gz | |
feat: M2b NPC 记忆与行为稳定化 — 环形记忆(≤64: tick/kind/valence) + 行动粘性(最小600tick, urgent<20豁免) + 夜间(22-6)sleep权重x3 + Desktop蓝色NPC与标题行动名 + headless摘要(mem_count/last_valence/行动平均持续/昼夜sleep占比) + 粘性与valence测试
Diffstat (limited to 'src/LivingVillage.Kernel.Tests/DeterminismTests.fs')
| -rw-r--r-- | src/LivingVillage.Kernel.Tests/DeterminismTests.fs | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/src/LivingVillage.Kernel.Tests/DeterminismTests.fs b/src/LivingVillage.Kernel.Tests/DeterminismTests.fs index aba794e..0e1766e 100644 --- a/src/LivingVillage.Kernel.Tests/DeterminismTests.fs +++ b/src/LivingVillage.Kernel.Tests/DeterminismTests.fs @@ -180,3 +180,64 @@ type DeterminismTests () = seqB.Add tb.Npcs.[0].Mind.Action if Seq.compareWith compare seqA seqB = 0 then Assert.Fail("npc action sequence must diverge across seeds") + + [<TestMethod>] + member _.NpcActionPersistsAtLeastMinTicks () = + let zero = { Input = { MoveX = 0.0f; MoveY = 0.0f } } + let mutable w = Sim.initialWorld 42UL + let total = 30000 + let mutable current = w.Npcs.[0].Mind.Action + let mutable prevNeeds = w.Npcs.[0].Mind.Needs + let mutable age = 0 + let mutable minStickyAge = System.Int32.MaxValue + let mutable switches = 0 + let mutable urgentSwitches = 0 + for _ in 1 .. total do + w <- Sim.step zero w + let npc = w.Npcs.[0] + let a = npc.Mind.Action + if a <> current then + let decayed = + { Hunger = prevNeeds.Hunger - Sim.hungerDecayPerTick + Energy = prevNeeds.Energy - Sim.energyDecayPerTick + Social = prevNeeds.Social - Sim.socialDecayPerTick + Money = prevNeeds.Money - Sim.moneyDecayPerTick } + if Sim.needsUrgent decayed then urgentSwitches <- urgentSwitches + 1 + else minStickyAge <- min minStickyAge age + current <- a + age <- 0 + switches <- switches + 1 + else + age <- age + 1 + prevNeeds <- npc.Mind.Needs + Assert.IsTrue(switches > 0, "expected at least one action switch in 30000 ticks") + Assert.IsTrue(minStickyAge >= 600, $"non-urgent action must persist >= 600 ticks, got min {minStickyAge}") + + [<TestMethod>] + member _.NpcRecordsValenceMemoryOnActionEffect () = + let zero = { Input = { MoveX = 0.0f; MoveY = 0.0f } } + let mutable w = Sim.initialWorld 42UL + let mutable sawEmpty = false + let mutable sawEvent = false + for _ in 1 .. 6000 do + w <- Sim.step zero w + let npc = w.Npcs.[0] + if npc.Mind.Memory.IsEmpty then sawEmpty <- true + if npc.Mind.Memory.Length > Sim.memoryCapacity then + Assert.Fail("memory exceeds ring capacity") + match List.tryHead npc.Mind.Memory with + | Some e -> + let expected = + match e.Kind with + | Meal -> 0.5f + | Rest -> 0.4f + | Pay -> 0.2f + | Hungry -> -0.4f + if e.Valence <> expected then + Assert.Fail($"valence/kind mismatch: {e.Kind} -> {e.Valence}") + if e.Valence < -1.0f || e.Valence > 1.0f then + Assert.Fail($"valence out of [-1,1]: {e.Valence}") + if e.Tick >= 1L && e.Tick <= 6000L then sawEvent <- true + | None -> () + Assert.IsTrue(sawEmpty, "memory must start empty") + Assert.IsTrue(sawEvent, "expected a valence memory event within 6000 ticks") |
