nx_procgen_preset_test.nx source
↩ module page · 165 lines · 9414 B
1// nx_procgen_preset_test.nx -- smoke for bidirectional progressive disclosure.
2//
3// What this validates -- the STRUCTURAL invariants of the cardinal:
4// 1. Tier 0 just-works (single call, no knobs, returns useful struct)
5// 2. Tier 0 == Tier 1 with defaults (no hidden code path under Tier 0)
6// 3. Determinism: same (seed, preset, dims) -> bit-identical heightmap
7// 4. Presets actually affect output (different preset -> different heightmap)
8// 5. Tier-2 raw composition reproduces Tier-1 result (visible recipe)
9// 6. Defensive at boundaries: invalid preset / zero dims fall back cleanly
10// 7. Sealed-enum preset validity gate
11// 8. Height-band classifier composes with Perlin sealed-enum
12// 9. Poisson density honors preset radius differences (after the
13// r>=10 saturation fix landed -- previously this assertion blocked
14// on a substrate compiler-bug masquerading as algorithm bug; see
15// project-poisson-disk-bug-large-r-2026-05-15.md).
16
17import "nx_syscalls.nx"
18import "nx_tier.nx"
19import "nx_perlin.nx"
20import "nx_procgen_preset.nx"
21
22func main() -> nx_int {
23 // === Test 1: Tier-0 just-works produces valid Landscape ==========
24 let l0: *Landscape = nx_procgen_landscape_default(42)
25 if l0.width != NX_PROCGEN_DEFAULT_WIDTH { return 1 }
26 if l0.height != NX_PROCGEN_DEFAULT_HEIGHT { return 2 }
27 if l0.preset != NX_PROCGEN_PRESET_FOREST { return 3 }
28 if l0.seed != 42 { return 4 }
29 // Heightmap pointer must be non-null.
30 if (l0.heightmap as i64) == 0 { return 5 }
31
32 // === Test 2: heightmap is filled (not all zero) ==================
33 var nonzero: nx_int = 0
34 var iy: nx_int = 0
35 while iy < l0.height {
36 var ix: nx_int = 0
37 while ix < l0.width {
38 if nx_landscape_height_at(l0, ix, iy) != 0 {
39 nonzero = nonzero + 1
40 }
41 ix = ix + 1
42 }
43 iy = iy + 1
44 }
45 if nonzero < 100 { return 10 }
46
47 // === Test 3: BIDIRECTIONAL INVARIANT -- Tier 0 == Tier 1(defaults) ====
48 //
49 // Calling Tier-1 with the SAME defaults as Tier-0 yields identical
50 // shape AND identical heightmap. This is the load-bearing assertion
51 // for the cardinal: Tier-0 is literally a one-line Tier-1 call, no
52 // hidden code path, no opaque defaults outside the named consts.
53 let l1: *Landscape = nx_procgen_landscape(42,
54 NX_PROCGEN_PRESET_FOREST,
55 NX_PROCGEN_DEFAULT_WIDTH,
56 NX_PROCGEN_DEFAULT_HEIGHT,
57 NX_PROCGEN_DEFAULT_DENSITY_Q10)
58 if l1.width != l0.width { return 20 }
59 if l1.height != l0.height { return 21 }
60 if l1.preset != l0.preset { return 22 }
61 if l1.seed != l0.seed { return 23 }
62 // Heightmap content must match -- determinism + same recipe.
63 if nx_landscape_height_at(l1, 0, 0) != nx_landscape_height_at(l0, 0, 0) { return 24 }
64 if nx_landscape_height_at(l1, 17, 23) != nx_landscape_height_at(l0, 17, 23) { return 25 }
65 if nx_landscape_height_at(l1, 31, 31) != nx_landscape_height_at(l0, 31, 31) { return 26 }
66 if nx_landscape_height_at(l1, 63, 63) != nx_landscape_height_at(l0, 63, 63) { return 27 }
67 if nx_landscape_height_at(l1, 40, 7) != nx_landscape_height_at(l0, 40, 7) { return 28 }
68
69 // === Test 4: determinism -- same seed/preset/dims produce same heightmap ====
70 let l_same: *Landscape = nx_procgen_landscape(42,
71 NX_PROCGEN_PRESET_FOREST,
72 NX_PROCGEN_DEFAULT_WIDTH,
73 NX_PROCGEN_DEFAULT_HEIGHT,
74 NX_PROCGEN_DEFAULT_DENSITY_Q10)
75 // Sample five cells -- determinism must hold pixel-perfect.
76 if nx_landscape_height_at(l_same, 5, 5) != nx_landscape_height_at(l0, 5, 5) { return 30 }
77 if nx_landscape_height_at(l_same, 32, 0) != nx_landscape_height_at(l0, 32, 0) { return 31 }
78 if nx_landscape_height_at(l_same, 0, 32) != nx_landscape_height_at(l0, 0, 32) { return 32 }
79
80 // === Test 5: different seed -> different heightmap ===============
81 //
82 // The Perlin gradient table is seed-permuted; different seeds yield
83 // different output. At least one of three sampled cells must differ.
84 let l_seed: *Landscape = nx_procgen_landscape(99,
85 NX_PROCGEN_PRESET_FOREST,
86 NX_PROCGEN_DEFAULT_WIDTH,
87 NX_PROCGEN_DEFAULT_HEIGHT,
88 NX_PROCGEN_DEFAULT_DENSITY_Q10)
89 var seed_diff: nx_int = 0
90 if nx_landscape_height_at(l_seed, 10, 10) != nx_landscape_height_at(l0, 10, 10) { seed_diff = 1 }
91 if nx_landscape_height_at(l_seed, 20, 30) != nx_landscape_height_at(l0, 20, 30) { seed_diff = 1 }
92 if nx_landscape_height_at(l_seed, 50, 50) != nx_landscape_height_at(l0, 50, 50) { seed_diff = 1 }
93 if seed_diff != 1 { return 40 }
94
95 // === Test 6: different preset -> different heightmap =============
96 //
97 // FOREST and MOUNTAIN use different octaves+persistence, so the
98 // fbm output at the same (seed, x, y) must differ for some cell.
99 let l_mtn: *Landscape = nx_procgen_landscape(42,
100 NX_PROCGEN_PRESET_MOUNTAIN,
101 NX_PROCGEN_DEFAULT_WIDTH,
102 NX_PROCGEN_DEFAULT_HEIGHT,
103 NX_PROCGEN_DEFAULT_DENSITY_Q10)
104 var preset_diff: nx_int = 0
105 if nx_landscape_height_at(l_mtn, 10, 10) != nx_landscape_height_at(l0, 10, 10) { preset_diff = 1 }
106 if nx_landscape_height_at(l_mtn, 20, 30) != nx_landscape_height_at(l0, 20, 30) { preset_diff = 1 }
107 if nx_landscape_height_at(l_mtn, 50, 50) != nx_landscape_height_at(l0, 50, 50) { preset_diff = 1 }
108 if preset_diff != 1 { return 50 }
109
110 // === Test 6b: presets produce DIFFERENT poisson density ==========
111 //
112 // FOREST has base_radius=4, DESERT has base_radius=10, MOUNTAIN has
113 // base_radius=12. On same canvas + same seed, density-ordering
114 // must be FOREST > DESERT > MOUNTAIN (more features at smaller r).
115 // This assertion would have failed before the Poisson stack-arg
116 // codegen fix landed (xs/ys read garbage, all candidates accepted).
117 let lf: *Landscape = nx_procgen_landscape(7,
118 NX_PROCGEN_PRESET_FOREST,
119 64, 64, 1024)
120 let ld: *Landscape = nx_procgen_landscape(7,
121 NX_PROCGEN_PRESET_DESERT,
122 64, 64, 1024)
123 let lm: *Landscape = nx_procgen_landscape(7,
124 NX_PROCGEN_PRESET_MOUNTAIN,
125 64, 64, 1024)
126 if lf.n_features <= ld.n_features { return 55 }
127 if ld.n_features < lm.n_features { return 56 }
128
129 // === Test 7: invalid preset falls back to FOREST =================
130 let l_fallback: *Landscape = nx_procgen_landscape(7, 99,
131 64, 64, 1024)
132 if l_fallback.preset != NX_PROCGEN_PRESET_FOREST { return 60 }
133
134 // === Test 8: zero/negative dims fall back to defaults ============
135 let l_zero: *Landscape = nx_procgen_landscape(7,
136 NX_PROCGEN_PRESET_FOREST,
137 0, 0, 0)
138 if l_zero.width != NX_PROCGEN_DEFAULT_WIDTH { return 70 }
139 if l_zero.height != NX_PROCGEN_DEFAULT_HEIGHT { return 71 }
140
141 // === Test 9: sealed-enum validity ================================
142 if nx_procgen_preset_is_valid(NX_PROCGEN_PRESET_FOREST) != 1 { return 80 }
143 if nx_procgen_preset_is_valid(NX_PROCGEN_PRESET_DESERT) != 1 { return 81 }
144 if nx_procgen_preset_is_valid(NX_PROCGEN_PRESET_MEADOW) != 1 { return 82 }
145 if nx_procgen_preset_is_valid(NX_PROCGEN_PRESET_COAST) != 1 { return 83 }
146 if nx_procgen_preset_is_valid(NX_PROCGEN_PRESET_MOUNTAIN) != 1 { return 84 }
147 if nx_procgen_preset_is_valid(NX_PROCGEN_N_PRESETS) != 0 { return 85 }
148 if nx_procgen_preset_is_valid(99) != 0 { return 86 }
149 if nx_procgen_preset_is_valid(0 - 1) != 0 { return 87 }
150
151 // === Test 10: height-band classifier composes with Perlin sealed-enum ===
152 //
153 // The accessor returns a band value that must be valid per Perlin's
154 // own sealed-enum gate. Composition across modules.
155 let band: nx_int = nx_landscape_height_band_at(l0, 32, 32)
156 if nx_perlin_band_is_valid(band) != 1 { return 90 }
157
158 // === Test 11: out-of-bounds height read returns 0 (no SEGV) ======
159 if nx_landscape_height_at(l0, 0 - 1, 0) != 0 { return 100 }
160 if nx_landscape_height_at(l0, l0.width, 0) != 0 { return 101 }
161 if nx_landscape_height_at(l0, 0, 0 - 1) != 0 { return 102 }
162 if nx_landscape_height_at(l0, 0, l0.height) != 0 { return 103 }
163
164 return 0
165}