summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop.Tests/P40ArtTests.fs
blob: 549640efd81952f4ea35581ed9dc9fc87ef1b268 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
namespace LivingVillage.Desktop.Tests

open Microsoft.VisualStudio.TestTools.UnitTesting
open LivingVillage.Kernel.Sim
open LivingVillage.Desktop
open LivingVillage.Desktop.VillageArt

/// P40 art-deepening slice:
///  * paving slabs follow the traffic network (paths / doorways / bridges) and the
///    isolated decorative scatter is dropped, so cc0 ground reads as coherent bands;
///  * red door lanterns complement the stone road lanterns with a distinct form,
///    both feeding the existing P14 premultiplied-alpha night-glow path;
///  * every placement stays a pure function of the map, so the same seed yields
///    byte-identical positions across day and night frames.
[<TestClass>]
type P40ArtTests () =

    let seed = 4242UL
    let map = MapGen.generateWithSize 256 192 seed

    let indexOf (x: int) (y: int) = y * map.Width + x
    let isStone (x: int) (y: int) =
        x >= 0 && x < map.Width && y >= 0 && y < map.Height
        && map.Tiles.[indexOf x y] = int MapGen.GroundTile.Stone

    [<TestMethod>]
    member _.PavingCoversTrafficNetworkAndDropsIsolatedScatter () =
        let paving = cc0PavingTiles map
        Assert.IsTrue(paving.Count > 0, "seed 4242 should pave something")
        // Every path / bridge anchor tile that is stone must be paved.
        for (x, y) in map.Paths @ map.Bridges do
            if isStone x y then
                Assert.IsTrue(paving.Contains(indexOf x y), sprintf "path/bridge stone (%d,%d) must be paved" x y)
        // Every paved tile must be stone and touch the network (itself or a 4-neighbour).
        let anchors = (map.Paths @ map.Bridges) |> List.map (fun (x, y) -> indexOf x y) |> Set.ofList
        let anchorAt (x: int) (y: int) =
            x >= 0 && x < map.Width && y >= 0 && y < map.Height && Set.contains (indexOf x y) anchors
        for i in paving do
            let x = i % map.Width
            let y = i / map.Width
            Assert.IsTrue(isStone x y, "paving must only replace stone tiles")
            Assert.IsTrue(
                anchorAt x y || anchorAt (x - 1) y || anchorAt (x + 1) y || anchorAt x (y - 1) || anchorAt x (y + 1),
                sprintf "paved tile (%d,%d) is disconnected from the traffic network" x y)
        // The isolated decorative scatter must NOT be paved (that is the whole point).
        Assert.IsTrue(paving.Count < (map.Tiles |> Array.filter (fun c -> c = int MapGen.GroundTile.Stone) |> Array.length),
                      "paving must be a strict subset of all stone (scatter dropped)")

    [<TestMethod>]
    member _.PavingIsDeterministicForTheSameSeed () =
        let map2 = MapGen.generateWithSize 256 192 seed
        let a = cc0PavingTiles map |> Set.toList |> List.sort |> List.map (fun i -> sprintf "%d" i) |> String.concat ";"
        let b = cc0PavingTiles map2 |> Set.toList |> List.sort |> List.map (fun i -> sprintf "%d" i) |> String.concat ";"
        Assert.AreEqual<string>(a, b)

    [<TestMethod>]
    member _.RedLanternsHangAtDoorwaysAndAreDeterministic () =
        let lanterns = cc0RedLanternTiles map
        let doors =
            (map.Buildings @ map.Farmhouses)
            |> List.map (fun b -> (b.DoorX, b.DoorY))
            |> List.distinct
        Assert.IsTrue(lanterns.Length > 0, "seed 4242 should hang red lanterns")
        Assert.AreEqual<int>(doors.Length, lanterns.Length)
        for (x, y) in lanterns do
            Assert.IsTrue(List.contains (x, y) doors, "a red lantern hangs exactly at a doorway")
        Assert.AreEqual<string>(
            (lanterns |> List.sort |> List.map (fun (x, y) -> sprintf "%d:%d" x y) |> String.concat ";"),
            (cc0RedLanternTiles map |> List.sort |> List.map (fun (x, y) -> sprintf "%d:%d" x y) |> String.concat ";"))

    [<TestMethod>]
    member _.RedAndStoneLanternsFeedTheSameNightGlowPath () =
        let cc0Lights = nightLightTilesWith true map |> Set.ofList
        let fallbackLights = nightLightTilesWith false map |> Set.ofList
        // cc0 mode adds both the road stone lanterns and the red door lanterns.
        for t in cc0RedLanternTiles map do
            Assert.IsTrue(Set.contains t cc0Lights, "red lantern must be a cc0 night light")
        for t in cc0LanternTiles map 12 do
            Assert.IsTrue(Set.contains t cc0Lights, "road lantern must be a cc0 night light")
        // Red lanterns hang at doorways, which are already house lights, so the
        // fallback set may legitimately contain that tile; what must NOT leak is
        // the road stone lantern set (a cc0-only placement).
        for t in cc0LanternTiles map 12 do
            Assert.IsFalse(Set.contains t fallbackLights, "road lantern must not leak into the fallback light set")
        Assert.IsTrue(nightLightTiles map = nightLightTilesWith false map, "nightLightTiles keeps the old set")
        Assert.IsTrue(Set.isSubset fallbackLights cc0Lights, "cc0 light set is a superset of the fallback set")

    [<TestMethod>]
    member _.SameSeedPlacementsAreStableAcrossGeneration () =
        // Day and night frames reuse the same map; re-generating it must reproduce
        // every P40 placement byte-for-byte (no clock, no hidden randomness).
        let map2 = MapGen.generateWithSize 256 192 seed
        Assert.AreEqual<string>(
            (cc0PavingTiles map |> Set.toList |> List.sort |> List.map string |> String.concat ";"),
            (cc0PavingTiles map2 |> Set.toList |> List.sort |> List.map string |> String.concat ";"))
        Assert.AreEqual<string>(
            (cc0RedLanternTiles map |> List.sort |> List.map (fun (x, y) -> sprintf "%d:%d" x y) |> String.concat ";"),
            (cc0RedLanternTiles map2 |> List.sort |> List.map (fun (x, y) -> sprintf "%d:%d" x y) |> String.concat ";"))

    [<TestMethod>]
    member _.P40AtlasIndicesAreInRange () =
        for index in [ cc0TileIndexPaving; cc0TileIndexRedLantern ] do
            Assert.IsTrue(index >= 0 && index < cc0TileCount, sprintf "P40 tile index %d out of atlas" index)
        Assert.AreEqual<int>(21, cc0TileCount)