summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-22 04:37:51 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-22 04:37:51 +0800
commit8654557d9ba1c98208b67f5909f7dd7917e10d50 (patch)
tree7cc64ad5f533f45b72dd7223b274327cce6493f6 /src
parent9b31590d6ad5f83505dc665e02b8ff477f26cc1a (diff)
downloadliving-village-8654557d9ba1c98208b67f5909f7dd7917e10d50.tar.gz
test(kernel): 512x384 参数化边界存档 round-trip 与 NPC 边界回归(P23)
- Sim.configureBounds 512 384 下 v2 存档 round-trip 且同 world 两次保存逐字节一致, 读回再保存仍与原文一致;v1 的 64x48 兼容由既有用例覆盖。 - DriverHarness 长跑 20000 tick 断言 avatar 与全部 NPC 有限且留在 [0,max] 内。 - Kernel 测试 90 -> 92。
Diffstat (limited to 'src')
-rw-r--r--src/LivingVillage.Kernel.Tests/DeterminismTests.fs24
-rw-r--r--src/LivingVillage.Kernel.Tests/M6aTests.fs26
2 files changed, 50 insertions, 0 deletions
diff --git a/src/LivingVillage.Kernel.Tests/DeterminismTests.fs b/src/LivingVillage.Kernel.Tests/DeterminismTests.fs
index 52cae89..c4b4154 100644
--- a/src/LivingVillage.Kernel.Tests/DeterminismTests.fs
+++ b/src/LivingVillage.Kernel.Tests/DeterminismTests.fs
@@ -113,6 +113,30 @@ type DeterminismTests () =
checkedTicks <- checkedTicks + 1
Assert.IsTrue(120001 = checkedTicks)
+ /// P23:512x384 参数化边界下,avatar 与全部 NPC 在长跑中仍留在 [0, max] 且数值有限。
+ [<TestMethod>]
+ member _.LargeMapBoundsClampAvatarAndNpcs () =
+ Sim.configureBounds 512 384
+ try
+ let trace = DriverHarness.run 4242UL 20000L
+ let maxX = float32 (Sim.mapWidthTiles * Sim.tilePixels - Sim.tilePixels)
+ let maxY = float32 (Sim.mapHeightTiles * Sim.tilePixels - Sim.tilePixels)
+ Assert.IsTrue(maxX > 64.0f * float32 Sim.tilePixels, "512 map must extend past legacy width")
+ for w in trace do
+ let p = w.Avatar.Pos
+ if Single.IsNaN p.X || Single.IsNaN p.Y then Assert.Fail($"NaN avatar at tick {w.Tick}")
+ if p.X < 0.0f || p.X > maxX then Assert.Fail($"avatar X out of bounds at tick {w.Tick}: {p.X}")
+ if p.Y < 0.0f || p.Y > maxY then Assert.Fail($"avatar Y out of bounds at tick {w.Tick}: {p.Y}")
+ for npc in w.Npcs do
+ if Single.IsNaN npc.Pos.X || Single.IsNaN npc.Pos.Y then
+ Assert.Fail($"NaN npc {npc.Id} at tick {w.Tick}")
+ if npc.Pos.X < 0.0f || npc.Pos.X > maxX then
+ Assert.Fail($"npc {npc.Id} X out of bounds at tick {w.Tick}: {npc.Pos.X}")
+ if npc.Pos.Y < 0.0f || npc.Pos.Y > maxY then
+ Assert.Fail($"npc {npc.Id} Y out of bounds at tick {w.Tick}: {npc.Pos.Y}")
+ finally
+ Sim.configureBounds 64 48
+
[<TestMethod>]
member _.SplitMix64MatchesKnownVectors () =
let expected : uint64 list =
diff --git a/src/LivingVillage.Kernel.Tests/M6aTests.fs b/src/LivingVillage.Kernel.Tests/M6aTests.fs
index 9348b0f..94341a1 100644
--- a/src/LivingVillage.Kernel.Tests/M6aTests.fs
+++ b/src/LivingVillage.Kernel.Tests/M6aTests.fs
@@ -314,3 +314,29 @@ type M6aTests () =
finally
Sim.configureBounds 64 48
if File.Exists path then File.Delete path
+
+ /// P23:512x384 参数化边界在存档 v2 中 round-trip;同 world 两次保存逐字节一致,
+ /// 读回后再次保存仍与原文一致(v1 的 64x48 兼容由 SaveV1LegacyStillReadable 覆盖)。
+ [<TestMethod>]
+ member _.SaveRoundTrips512x384BoundsAndIsByteStable () =
+ let path = "p23-bounds-512-v2.save"
+ let path2 = path + ".2"
+ try
+ Sim.configureBounds 512 384
+ let world = Sim.initialWorld (uint64 4242)
+ WorldSave.saveToFile path world
+ WorldSave.saveToFile path2 world
+ let text = File.ReadAllText path
+ Assert.IsTrue(text.StartsWith("LV_WORLD_SAVE_V2"), "bounds save must stay v2")
+ Assert.AreEqual<string>(text, File.ReadAllText path2)
+ Sim.configureBounds 64 48
+ match WorldSave.loadFromFile path with
+ | Error failure -> Assert.Fail(sprintf "512x384 bounds save should load: %s" failure)
+ | Ok restored ->
+ Assert.AreEqual<int>(512, Sim.mapWidthTiles)
+ Assert.AreEqual<int>(384, Sim.mapHeightTiles)
+ Assert.AreEqual<string>(text, WorldSave.save restored)
+ finally
+ Sim.configureBounds 64 48
+ if File.Exists path then File.Delete path
+ if File.Exists path2 then File.Delete path2