diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-22 11:52:35 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-22 11:52:35 +0800 |
| commit | b2a67011ca7eef02c685794d887804e7e50dadf8 (patch) | |
| tree | fe9b3d02391cd0baf991c144cab4b589b942f7a1 /src/LivingVillage.Desktop.Tests | |
| parent | 38496525403cefcbfefedcf8377100e662d9e6d4 (diff) | |
| parent | ecb96a9626b3c5583a995f78b99d6db43ab2dd5b (diff) | |
| download | living-village-b2a67011ca7eef02c685794d887804e7e50dadf8.tar.gz | |
merge: P28 民居第二变体强化 + 512 沿河加密(青灰挑檐B/竖长格栅窗/绿院门,2/3/4 宽分层 16 座外环)— Hermes 复验通过
Diffstat (limited to 'src/LivingVillage.Desktop.Tests')
| -rw-r--r-- | src/LivingVillage.Desktop.Tests/MapGenTests.fs | 15 | ||||
| -rw-r--r-- | src/LivingVillage.Desktop.Tests/PrototypeTests.fs | 53 |
2 files changed, 68 insertions, 0 deletions
diff --git a/src/LivingVillage.Desktop.Tests/MapGenTests.fs b/src/LivingVillage.Desktop.Tests/MapGenTests.fs index 6486dd7..b737707 100644 --- a/src/LivingVillage.Desktop.Tests/MapGenTests.fs +++ b/src/LivingVillage.Desktop.Tests/MapGenTests.fs @@ -256,6 +256,21 @@ type MapGenTests () = Assert.IsTrue(a.Buildings = b.Buildings) Assert.AreEqual<string>(MapGen.serialize a, MapGen.serialize b) + /// P28:512 沿河民居由 10 -> 16 增密(+6),并出现 2/3/4 三档宽度(附属小筑/民居/大宅), + /// 形成聚落层次;默认图仍为 0,不消耗 RNG,故 64/256 checksum 锁定不变。 + [<TestMethod>] + member _.LargeMapHouseDensityHierarchyOnlyGrowsLargeMaps () = + Assert.AreEqual<int>(16, (MapGen.paramsForSize 512 384 (uint64 1)).ExtraRiversideHouses) + Assert.AreEqual<int>(0, (MapGen.paramsForSize 64 48 (uint64 1)).ExtraRiversideHouses) + Assert.AreEqual<int>(0, (MapGen.paramsForSize 256 192 (uint64 1)).ExtraRiversideHouses) + let map = MapGen.generateWithSize 512 384 (uint64 4242) + Assert.IsTrue(map.Buildings.Length >= 22, sprintf "P28 512 must place >=22 houses, saw %d" map.Buildings.Length) + let widths = map.Buildings |> List.map (fun b -> b.Width) |> Set.ofList + Assert.IsTrue(widths.Contains 2, "outbuilding width 2 must appear") + Assert.IsTrue(widths.Contains 3, "house width 3 must appear") + Assert.IsTrue(widths.Contains 4, "hall width 4 must appear") + Assert.AreEqual<int>(24, map.Buildings.Length) + [<TestMethod>] member _.DefaultSizeHouseCountUnchanged () = Assert.AreEqual<int>(4, (MapGen.generateWithSize 64 48 (uint64 4242)).Buildings.Length) diff --git a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs index bc3425a..7f67280 100644 --- a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs +++ b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs @@ -1171,6 +1171,59 @@ type PrototypeTests () = Assert.IsTrue((atlasWidth = VillageArt.worldAtlasWidth), "world atlas width must match 32 x 32px slots") Assert.IsTrue((atlasHeight = VillageArt.worldAtlasHeight), "world atlas height must stay 32px") + /// P28:防复发。第二变体(P26)在 512 整图 ~2px/tile 下不可辨,根因是挑檐与第一变体同为 + /// 近黑黛瓦。此回归直接解码 world 图集,断言变体 B 的挑檐(slot 28)平均亮度显著高于 + /// 变体 A 的屋脊(slot 8),保证「配色差异」不会在后续编辑中被悄悄抹平。 + [<TestMethod>] + member _.VariantBRoofPaletteIsClearlyLighterThanVariantA () = + let worldPath = Path.Combine(AppContext.BaseDirectory, "Assets/jiangnan-world.png") + let bytes = File.ReadAllBytes worldPath + let be32 offset = + (int bytes.[offset] <<< 24) + ||| (int bytes.[offset + 1] <<< 16) + ||| (int bytes.[offset + 2] <<< 8) + ||| int bytes.[offset + 3] + Assert.AreEqual<int>(0x89504E47, be32 0) + let width = be32 16 + let height = be32 20 + Assert.AreEqual<int>(VillageArt.worldAtlasWidth, width) + Assert.AreEqual<int>(VillageArt.worldAtlasHeight, height) + // Collect IDAT and inflate; the baked atlas is 8-bit RGBA, non-interlaced, filter 0. + let mutable offset = 8 + use idat = new MemoryStream() + while offset < bytes.Length do + let length = be32 offset + let tag = System.Text.Encoding.ASCII.GetString(bytes, offset + 4, 4) + if tag = "IDAT" then idat.Write(bytes, offset + 8, length) + offset <- offset + 12 + length + idat.Position <- 0L + use inflater = new System.IO.Compression.ZLibStream(idat, System.IO.Compression.CompressionMode.Decompress) + use raw = new MemoryStream() + inflater.CopyTo raw + let data = raw.ToArray() + let stride = width * 4 + 1 + let pixel (x: int) (y: int) = + let rowStart = y * stride + Assert.AreEqual<byte>(0uy, data.[rowStart]) + let p = rowStart + 1 + x * 4 + (int data.[p], int data.[p + 1], int data.[p + 2], int data.[p + 3]) + let slotMeanBrightness (slot: int) = + let mutable sum = 0.0 + let mutable count = 0 + for y in 0 .. height - 1 do + for x in slot * 32 .. slot * 32 + 31 do + let (r, g, b, a) = pixel x y + if a > 0 then + sum <- sum + float (r + g + b) / 3.0 + count <- count + 1 + Assert.IsTrue(count > 0, sprintf "slot %d must have opaque pixels" slot) + sum / float count + let roofA = slotMeanBrightness 8 // RoofRidgeSprite: variant A near-black 黛瓦 + let roofB = slotMeanBrightness 28 // RoofEaveOverhangSprite: variant B light blue-grey 瓦 + Assert.IsTrue( + roofB > roofA + 15.0, + sprintf "variant B roof must be clearly lighter than variant A (A=%.1f B=%.1f)" roofA roofB) + [<TestMethod>] member _.CjkGlyphManifestIsUniqueAndMatchesAtlasGrid () = // 防复发:图集用字增长时,清单必须非空、每行恰 1 个字符、字符唯一, |
