From ef78374c316587fb585b72e6d427cd6e61203655 Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Tue, 22 Sep 2026 02:28:31 +0800 Subject: feat(mapgen): 参数化确定性生成器与视口裁剪(P20 第一步) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增纯函数 MapGen:尺寸参数化(默认 64x48,可 256x192),splitmix64 随机游走河道 + 2-3 octave value noise + 泊松式布点;核心区整平后以 BFS 校验 5x6=30 个 spawn 全部可达,不可达确定性修整并复验。 - visibleTileRange/visibleTileCount 纯函数,drawWorld 改为调用之(行为不变); drawMapFitted/drawMapViewport 仅证据用。 - 存档沿用 v2/v3 边界字段,补参数化边界 round-trip 测试,未改 Sim 语义。 - 回归:Desktop 109->115、Kernel 89->90;final_digest 与基线一致。 --- .../LivingVillage.Desktop.Tests.fsproj | 1 + src/LivingVillage.Desktop.Tests/MapGenTests.fs | 81 ++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 src/LivingVillage.Desktop.Tests/MapGenTests.fs (limited to 'src/LivingVillage.Desktop.Tests') diff --git a/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj b/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj index 60d9995..87e9e9d 100644 --- a/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj +++ b/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj @@ -11,6 +11,7 @@ + diff --git a/src/LivingVillage.Desktop.Tests/MapGenTests.fs b/src/LivingVillage.Desktop.Tests/MapGenTests.fs new file mode 100644 index 0000000..4454b33 --- /dev/null +++ b/src/LivingVillage.Desktop.Tests/MapGenTests.fs @@ -0,0 +1,81 @@ +namespace LivingVillage.Desktop.Tests + +open Microsoft.VisualStudio.TestTools.UnitTesting +open LivingVillage.Kernel +open LivingVillage.Desktop + +/// P20 第一步回归:确定性生成器 + 尺寸参数化 + 30 spawn 核心可达 + 视口裁剪。 +[] +type MapGenTests () = + + let tilesEqual (a: MapGen.Result) (b: MapGen.Result) (message: string) = + if a.Tiles.Length <> b.Tiles.Length then + Assert.Fail(sprintf "%s: tile count %d <> %d" message a.Tiles.Length b.Tiles.Length) + for i in 0 .. a.Tiles.Length - 1 do + if a.Tiles.[i] <> b.Tiles.[i] then + Assert.Fail(sprintf "%s: tile %d = %d, expected %d" message i a.Tiles.[i] b.Tiles.[i]) + + let checksum (map: MapGen.Result) : int64 = + map.Tiles |> Array.mapi (fun i v -> int64 i * int64 (v + 1)) |> Array.sum + + [] + member _.SameSeedGeneratesByteIdenticalTerrain () = + let a = MapGen.generateWithSize 64 48 (uint64 4242) + let b = MapGen.generateWithSize 64 48 (uint64 4242) + tilesEqual a b "64x48 same seed" + Assert.AreEqual(a.Seed, b.Seed) + Assert.AreEqual(checksum a, checksum b) + + let large1 = MapGen.generateWithSize 256 192 (uint64 7) + let large2 = MapGen.generateWithSize 256 192 (uint64 7) + tilesEqual large1 large2 "256x192 same seed" + Assert.AreEqual(checksum large1, checksum large2) + + [] + member _.DifferentSeedsProduceDifferentTerrain () = + let a = MapGen.generateWithSize 256 192 (uint64 1) + let b = MapGen.generateWithSize 256 192 (uint64 2) + Assert.AreNotEqual(checksum a, checksum b, "different seeds must differ") + + [] + member _.LargeMapThirtySpawnsAllReachCore () = + let map = MapGen.generateWithSize 256 192 (uint64 4242) + Assert.AreEqual(256, map.Width) + Assert.AreEqual(192, map.Height) + Assert.AreEqual(30, map.Spawns.Length) + Assert.IsTrue(map.ReachabilityOk, "all 30 spawns must reach the core") + Assert.IsTrue(map.ReachableTiles > 30, "core region must be walkable") + for (x, y) in map.Spawns do + Assert.AreNotEqual(int MapGen.GroundTile.Water, map.Tiles.[y * map.Width + x], sprintf "spawn (%d,%d) must be walkable" x y) + + [] + member _.DefaultSizeMatchesLegacyBounds () = + let map = MapGen.generate (MapGen.defaultParams (uint64 4242)) + Assert.AreEqual(64, map.Width) + Assert.AreEqual(48, map.Height) + Assert.AreEqual(64 * 48, map.Tiles.Length) + Assert.IsTrue(map.ReachabilityOk) + Assert.AreEqual(30, map.Spawns.Length) + + [] + member _.VisibleRangeCullsLargeMapToViewport () = + let total = 256 * 192 + let x0, y0, x1, y1 = MapGen.visibleTileRange 256 192 1000 1000 1280 720 + let count = MapGen.visibleTileCount 256 192 1000 1000 1280 720 + Assert.AreEqual((x1 - x0 + 1) * (y1 - y0 + 1), count) + Assert.IsTrue(count < total, "viewport culling must draw strictly fewer tiles than the whole map") + + let ox0, oy0, ox1, oy1 = MapGen.visibleTileRange 256 192 0 0 1280 720 + Assert.AreEqual(0, ox0) + Assert.AreEqual(0, oy0) + Assert.AreEqual(1280 / Sim.tilePixels + 1, ox1) + Assert.AreEqual(720 / Sim.tilePixels + 1, oy1) + + [] + member _.VisibleRangeClampsAtLegacyMapEdges () = + let x0, y0, x1, y1 = MapGen.visibleTileRange 64 48 0 0 2048 1536 + Assert.AreEqual(0, x0) + Assert.AreEqual(0, y0) + Assert.AreEqual(63, x1) + Assert.AreEqual(47, y1) + Assert.AreEqual(64 * 48, MapGen.visibleTileCount 64 48 0 0 2048 1536) -- cgit v1.2.3