nx_procgen_biome_test.nx source
↩ module page · 129 lines · 4901 B
1// nx_procgen_biome_test.nx -- KATs for the P2 causal biome map.
2// Exit 0 = all pass; first failing KAT's number is the exit code.
3//
4// KAT 1 determinism: same (heightmap, seed) twice -> identical maps.
5// KAT 2 validity: every cell holds a sealed biome ID.
6// KAT 3 lawfulness: grader BIOME_COHERENCE >= 12000 on a generated world
7// (the R1 ladder target; lawful-by-construction, grader-proven).
8// KAT 4 moisture works: a different moisture seed changes the map, and
9// one map's MID band holds >= 2 distinct biomes.
10// KAT 5 tamper RED: an all-ICE map scores LOW coherence (< 8000) -- the
11// axis discriminates, it doesn't rubber-stamp.
12// KAT 6 flat-map refusal path: range 0 builds valid biomes, no crash.
13// KAT 7 every preset x 2 seeds: coherence >= 12000 (width, worst-case).
14// KAT 8 elevation band function mirrors the grader's quartile cuts.
15// license_tier: ORIGINAL
16
17import "nx_syscalls.nx"
18import "nx_tier.nx"
19import "nx_procgen_preset.nx"
20import "nx_procgen_biome.nx"
21import "nx_world_quality_grader.nx"
22
23const BW: nx_int = 64
24const BH: nx_int = 64
25
26func main() -> i64 {
27 let n: nx_int = BW * BH
28 let bm: *i64 = sys_mmap(n * 8) as *i64
29 let bm2: *i64 = sys_mmap(n * 8) as *i64
30 let v: *i64 = sys_mmap(8 * 12) as *i64
31 let null_ptr: *i64 = 0 as *i64
32
33 // one generated world (mountain preset = the banked-best family)
34 let land: *Landscape = nx_procgen_landscape(4242, NX_PROCGEN_PRESET_MOUNTAIN, BW, BH, 1024)
35
36 // ---- KAT 1: determinism ----
37 nx_biome_map_build(land.heightmap, BW, BH, 4242, bm)
38 nx_biome_map_build(land.heightmap, BW, BH, 4242, bm2)
39 var i: nx_int = 0
40 while i < n {
41 if bm[i] != bm2[i] { return 1 }
42 i = i + 1
43 }
44
45 // ---- KAT 2: validity ----
46 i = 0
47 while i < n {
48 if nx_biome_is_valid(bm[i]) != 1 { return 2 }
49 i = i + 1
50 }
51
52 // ---- KAT 3: lawful coherence on the generated world ----
53 nx_world_quality_grade(land.heightmap, BW, BH, 1024, bm, null_ptr, 0, v)
54 if v[NX_WQG_OFF_BIOME_COHERENCE] < 12000 { return 3 }
55
56 // ---- KAT 4: moisture differentiates ----
57 nx_biome_map_build(land.heightmap, BW, BH, 7777, bm2)
58 var n_diff: nx_int = 0
59 i = 0
60 while i < n {
61 if bm[i] != bm2[i] { n_diff = n_diff + 1 }
62 i = i + 1
63 }
64 if n_diff == 0 { return 4 }
65 // MID band of map 1 holds >= 2 distinct biomes
66 let mid_seen: *i64 = sys_mmap(NX_BIOME_ID_SPAN * 8) as *i64
67 var k: nx_int = 0
68 while k < NX_BIOME_ID_SPAN { mid_seen[k] = 0; k = k + 1 }
69 i = 0
70 while i < n {
71 let b: nx_int = bm[i]
72 if b == NX_BIOME_GRASSLAND { mid_seen[b] = 1 }
73 if b == NX_BIOME_BOREAL { mid_seen[b] = 1 }
74 if b == NX_BIOME_TEMPERATE { mid_seen[b] = 1 }
75 if b == NX_BIOME_TROPICAL { mid_seen[b] = 1 }
76 i = i + 1
77 }
78 var n_mid_kinds: nx_int = 0
79 n_mid_kinds = n_mid_kinds + mid_seen[NX_BIOME_GRASSLAND]
80 n_mid_kinds = n_mid_kinds + mid_seen[NX_BIOME_BOREAL]
81 n_mid_kinds = n_mid_kinds + mid_seen[NX_BIOME_TEMPERATE]
82 n_mid_kinds = n_mid_kinds + mid_seen[NX_BIOME_TROPICAL]
83 if n_mid_kinds < 2 { return 4 }
84
85 // ---- KAT 5: tamper RED ----
86 i = 0
87 while i < n { bm2[i] = NX_BIOME_ICE; i = i + 1 }
88 nx_world_quality_grade(land.heightmap, BW, BH, 1024, bm2, null_ptr, 0, v)
89 if v[NX_WQG_OFF_BIOME_COHERENCE] > 8000 { return 5 }
90
91 // ---- KAT 6: flat map stays valid ----
92 let flat: *i64 = sys_mmap(n * 8) as *i64
93 i = 0
94 while i < n { flat[i] = 100; i = i + 1 }
95 if nx_biome_map_build(flat, BW, BH, 42, bm2) != 0 { return 6 }
96 i = 0
97 while i < n {
98 if nx_biome_is_valid(bm2[i]) != 1 { return 6 }
99 i = i + 1
100 }
101
102 // ---- KAT 7: width -- every preset x 2 seeds ----
103 var preset: nx_int = 0
104 while preset < NX_PROCGEN_N_PRESETS {
105 var s: nx_int = 0
106 while s < 2 {
107 let seed: nx_int = 1000 + s * 777 + preset * 31
108 let wl: *Landscape = nx_procgen_landscape(seed, preset, BW, BH, 1024)
109 nx_biome_map_build(wl.heightmap, BW, BH, seed, bm2)
110 nx_world_quality_grade(wl.heightmap, BW, BH, 1024, bm2, null_ptr, 0, v)
111 if v[NX_WQG_OFF_BIOME_COHERENCE] < 12000 { return 7 }
112 s = s + 1
113 }
114 preset = preset + 1
115 }
116
117 // ---- KAT 8: band function mirrors the grader's quartile law ----
118 // range 100..200: cuts at 125 / 150 / 175 (strict-greater semantics)
119 if nx_biome_band(100, 100, 100) != 0 { return 8 }
120 if nx_biome_band(125, 100, 100) != 0 { return 8 }
121 if nx_biome_band(126, 100, 100) != 1 { return 8 }
122 if nx_biome_band(150, 100, 100) != 1 { return 8 }
123 if nx_biome_band(151, 100, 100) != 2 { return 8 }
124 if nx_biome_band(175, 100, 100) != 2 { return 8 }
125 if nx_biome_band(176, 100, 100) != 3 { return 8 }
126 if nx_biome_band(200, 100, 100) != 3 { return 8 }
127
128 return 0
129}