nx_inter_layer_coherence.nx source
↩ module page · 363 lines · 14469 B
1// nx_inter_layer_coherence.nx -- cross-layer constraint checker.
2//
3// Per honest audit 2026-05-16: per-layer graders can each be S-class
4// while the OVERALL composition is nonsense -- river flowing uphill,
5// forest in a desert biome, town built on a cliff edge, castle in a
6// swamp. This primitive grades the INTER-LAYER CONSTRAINTS that
7// must hold for a coherent world.
8//
9// Four constraints scored individually + composed:
10//
11// 1. RIVER_DRAINAGE: rivers should flow downhill. For each polyline
12// segment, sample heightmap at both endpoints; score = fraction
13// of segments where h_end < h_start.
14//
15// 2. FOREST_BIOME_MATCH: forests should sit in biomes that support
16// forests (BOREAL_FOREST / TEMPERATE_FOREST / TROPICAL_RAINFOREST
17// or GRASSLAND). Score = fraction of forest positions in valid
18// biomes.
19//
20// 3. TOWN_FLATNESS: towns should be on relatively flat ground. Sample
21// local heightmap stddev within town radius; score = inverse of
22// normalised stddev.
23//
24// 4. CASTLE_DEFENSIVE: castles benefit from elevation advantage.
25// Score = fraction of castles whose centre elevation exceeds the
26// mean elevation in a surrounding band of 2x radius.
27//
28// Each function returns Q14 [0, Q]; nx_inter_layer_coherence_grade
29// composes them into a LAYER_VERDICT (kind = NX_LAYER_KIND_READABILITY,
30// refine = NX_LAYER_REFINE_FIX_COHERENCE).
31//
32// genealogy_id: world_design_coherence_canon +
33// leopold_wolman_1957_drainage +
34// medieval_settlement_geography_canon
35// lineage_id: nx_inter_layer_coherence_4axis_v1
36
37// nx_safety_envelope:
38// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
39// sil_target: SIL1
40// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
41// verdict: NOT_YET_EVALUATED
42
43import "nx_syscalls.nx"
44import "nx_tier.nx"
45import "nx_layer_verdict.nx"
46
47const NX_ILC_Q: nx_int = 16384
48
49// ===== Axis indices =================================================
50const NX_ILC_AXIS_RIVER: nx_int = 0
51const NX_ILC_AXIS_FOREST_BIOME: nx_int = 1
52const NX_ILC_AXIS_TOWN_FLAT: nx_int = 2
53const NX_ILC_AXIS_CASTLE_HIGH: nx_int = 3
54
55const NX_ILC_AXIS_COUNT: nx_int = 4
56
57// ===== Helper: heightmap sample (clamped) ==========================
58func _ilc_h_at(
59 heightmap: *i64, w: nx_int, h: nx_int, x: nx_int, y: nx_int
60) -> nx_int {
61 var cx: nx_int = x
62 var cy: nx_int = y
63 if cx < 0 { cx = 0 }
64 if cx >= w { cx = w - 1 }
65 if cy < 0 { cy = 0 }
66 if cy >= h { cy = h - 1 }
67 return heightmap[cy * w + cx]
68}
69
70// ===== 1. River drainage: rivers must flow downhill ================
71// rivers: flat array of N polylines, each n_points_per_river control
72// points stored as 2 i64 (x, y) in heightmap-cell coords (NOT Q14).
73// Caller may scale Q14 river coords down before calling.
74func nx_coherence_river_drainage(
75 rivers: *i64, n_rivers: nx_int, points_per: nx_int,
76 heightmap: *i64, w: nx_int, hgt: nx_int
77) -> nx_int {
78 if n_rivers <= 0 { return NX_ILC_Q / 2 } // skip => MARGINAL
79 let q: nx_int = NX_ILC_Q
80 var n_segments: nx_int = 0
81 var n_downhill: nx_int = 0
82 var r: nx_int = 0
83 while r < n_rivers {
84 var pi: nx_int = 0
85 while pi < points_per - 1 {
86 let idx0: nx_int = (r * points_per + pi) * 2
87 let idx1: nx_int = (r * points_per + pi + 1) * 2
88 let x0: nx_int = rivers[idx0 ]
89 let y0: nx_int = rivers[idx0 + 1]
90 let x1: nx_int = rivers[idx1 ]
91 let y1: nx_int = rivers[idx1 + 1]
92 let h0: nx_int = _ilc_h_at(heightmap, w, hgt, x0, y0)
93 let h1: nx_int = _ilc_h_at(heightmap, w, hgt, x1, y1)
94 if h1 <= h0 { n_downhill = n_downhill + 1 }
95 n_segments = n_segments + 1
96 pi = pi + 1
97 }
98 r = r + 1
99 }
100 if n_segments == 0 { return 0 }
101 return (n_downhill * q) / n_segments
102}
103
104// ===== 2. Forest in valid biome ====================================
105// forests: flat array of N positions (x, y) in heightmap-cell coords.
106// biome_map: parallel i64 array of biome ids.
107// Valid forest biomes: 1 (BOREAL_FOREST), 4 (GRASSLAND), 5
108// (TEMPERATE_FOREST), 11 (TROPICAL_RAINFOREST).
109func nx_coherence_forest_in_biome(
110 forests: *i64, n_forests: nx_int,
111 biome_map: *i64, w: nx_int, hgt: nx_int
112) -> nx_int {
113 if n_forests <= 0 { return NX_ILC_Q / 2 }
114 let q: nx_int = NX_ILC_Q
115 var n_valid: nx_int = 0
116 var i: nx_int = 0
117 while i < n_forests {
118 let x: nx_int = forests[i * 2 ]
119 let y: nx_int = forests[i * 2 + 1]
120 let b: nx_int = _ilc_h_at(biome_map, w, hgt, x, y)
121 if b == 1 { n_valid = n_valid + 1 }
122 if b == 4 { n_valid = n_valid + 1 }
123 if b == 5 { n_valid = n_valid + 1 }
124 if b == 11 { n_valid = n_valid + 1 }
125 i = i + 1
126 }
127 return (n_valid * q) / n_forests
128}
129
130// ===== 3. Town on flat ground ======================================
131// towns: flat array of N records (cx, cy, radius) in heightmap-cell
132// coords. For each town, compute local heightmap variation in a
133// circle of given radius. Score = average per-town flatness Q14.
134func nx_coherence_town_on_flatland(
135 towns: *i64, n_towns: nx_int,
136 heightmap: *i64, w: nx_int, hgt: nx_int, max_relief: nx_int
137) -> nx_int {
138 if n_towns <= 0 { return NX_ILC_Q / 2 }
139 if max_relief <= 0 { return 0 }
140 let q: nx_int = NX_ILC_Q
141 var sum_flat: nx_int = 0
142 var i: nx_int = 0
143 while i < n_towns {
144 let cx: nx_int = towns[i * 3 ]
145 let cy: nx_int = towns[i * 3 + 1]
146 let r: nx_int = towns[i * 3 + 2]
147 if r <= 0 { i = i + 1 }
148 if r > 0 {
149 // Sample local stddev via mean abs deviation in a square.
150 let r_sq: nx_int = r * r
151 var sum_h: nx_int = 0
152 var count: nx_int = 0
153 var dy: nx_int = 0 - r
154 while dy <= r {
155 var dx: nx_int = 0 - r
156 while dx <= r {
157 if dx * dx + dy * dy <= r_sq {
158 sum_h = sum_h + _ilc_h_at(heightmap, w, hgt, cx + dx, cy + dy)
159 count = count + 1
160 }
161 dx = dx + 1
162 }
163 dy = dy + 1
164 }
165 if count == 0 { i = i + 1 }
166 if count > 0 {
167 let mean_h: nx_int = sum_h / count
168 var dev_sum: nx_int = 0
169 var dy2: nx_int = 0 - r
170 while dy2 <= r {
171 var dx2: nx_int = 0 - r
172 while dx2 <= r {
173 if dx2 * dx2 + dy2 * dy2 <= r_sq {
174 var d: nx_int = _ilc_h_at(heightmap, w, hgt, cx + dx2, cy + dy2) - mean_h
175 if d < 0 { d = 0 - d }
176 dev_sum = dev_sum + d
177 }
178 dx2 = dx2 + 1
179 }
180 dy2 = dy2 + 1
181 }
182 let mean_dev: nx_int = dev_sum / count
183 // Score = 1 - mean_dev / (max_relief / 10).
184 // mean_dev = 0 -> q (perfectly flat)
185 // mean_dev = max/10 -> 0 (very rugged)
186 let target: nx_int = max_relief / 10
187 if target <= 0 { sum_flat = sum_flat + q }
188 if target > 0 {
189 var dev_score: nx_int = q - (mean_dev * q) / target
190 if dev_score < 0 { dev_score = 0 }
191 if dev_score > q { dev_score = q }
192 sum_flat = sum_flat + dev_score
193 }
194 i = i + 1
195 }
196 }
197 }
198 return sum_flat / n_towns
199}
200
201// ===== 4. Castle on elevated ground ================================
202// castles: flat array of N records (cx, cy, radius) in heightmap-cell
203// coords. For each castle, compare centre elevation to the mean in
204// an annular ring (2r radius).
205func nx_coherence_castle_on_high(
206 castles: *i64, n_castles: nx_int,
207 heightmap: *i64, w: nx_int, hgt: nx_int
208) -> nx_int {
209 if n_castles <= 0 { return NX_ILC_Q / 2 }
210 let q: nx_int = NX_ILC_Q
211 var n_high: nx_int = 0
212 var i: nx_int = 0
213 while i < n_castles {
214 let cx: nx_int = castles[i * 3 ]
215 let cy: nx_int = castles[i * 3 + 1]
216 let r: nx_int = castles[i * 3 + 2]
217 let centre_h: nx_int = _ilc_h_at(heightmap, w, hgt, cx, cy)
218 // Sample ring at 2r distance.
219 var sum_h: nx_int = 0
220 var count: nx_int = 0
221 let r2: nx_int = 2 * r
222 if r2 > 0 {
223 var dy: nx_int = 0 - r2
224 while dy <= r2 {
225 var dx: nx_int = 0 - r2
226 while dx <= r2 {
227 let d_sq: nx_int = dx * dx + dy * dy
228 let r2_sq: nx_int = r2 * r2
229 let r_sq: nx_int = r * r
230 // Annular band: r < sqrt(d_sq) < 2r.
231 if d_sq > r_sq {
232 if d_sq <= r2_sq {
233 sum_h = sum_h + _ilc_h_at(heightmap, w, hgt, cx + dx, cy + dy)
234 count = count + 1
235 }
236 }
237 dx = dx + 1
238 }
239 dy = dy + 1
240 }
241 }
242 if count > 0 {
243 let mean_ring: nx_int = sum_h / count
244 if centre_h >= mean_ring { n_high = n_high + 1 }
245 }
246 i = i + 1
247 }
248 return (n_high * q) / n_castles
249}
250
251// ===== Public: compose all 4 axes into a LAYER_VERDICT ============
252func nx_inter_layer_coherence_grade(
253 river_score: nx_int, forest_score: nx_int,
254 town_score: nx_int, castle_score: nx_int,
255 out_verdict: *i64
256) {
257 nx_layer_verdict_init(out_verdict, NX_LAYER_KIND_READABILITY,
258 NX_ILC_AXIS_COUNT, NX_LAYER_REFINE_FIX_COHERENCE)
259 out_verdict[NX_LV_OFF_AXIS_0 + NX_ILC_AXIS_RIVER] = river_score
260 out_verdict[NX_LV_OFF_AXIS_0 + NX_ILC_AXIS_FOREST_BIOME] = forest_score
261 out_verdict[NX_LV_OFF_AXIS_0 + NX_ILC_AXIS_TOWN_FLAT] = town_score
262 out_verdict[NX_LV_OFF_AXIS_0 + NX_ILC_AXIS_CASTLE_HIGH] = castle_score
263 nx_layer_verdict_finalize(out_verdict)
264}
265
266// ===== Self-test ====================================================
267func main() -> i64 {
268 let q: nx_int = NX_ILC_Q
269
270 let w: nx_int = 16
271 let h: nx_int = 16
272 let n: nx_int = w * h
273
274 // Linear ramp heightmap (h = y * 100); rivers flowing south->north
275 // SHOULD flow downhill (decreasing y -> decreasing h), wait that
276 // logic is wrong. Actually y=0 -> h=0, y=15 -> h=1500. River
277 // flowing "downhill" means from y=15 to y=0 (high to low).
278 let map: *i64 = (sys_mmap(n * NX_SIZEOF_NX_INT)) as *i64
279 var i: nx_int = 0
280 while i < n {
281 let y: nx_int = i / w
282 map[i] = y * 100
283 i = i + 1
284 }
285
286 // T1: River drainage -- 1 river with 4 points going downhill
287 // (y=15 to y=0). All segments should pass.
288 let rivers: *i64 = (sys_mmap(2 * 4 * NX_SIZEOF_NX_INT)) as *i64
289 rivers[0] = 4; rivers[1] = 15
290 rivers[2] = 4; rivers[3] = 10
291 rivers[4] = 4; rivers[5] = 5
292 rivers[6] = 4; rivers[7] = 0
293 let s_r: nx_int = nx_coherence_river_drainage(rivers, 1, 4, map, w, h)
294 if s_r != q { return __syscall(93, 1, 0, 0, 0, 0, 0) }
295
296 // T2: River flowing UPHILL -- all segments fail.
297 rivers[0] = 4; rivers[1] = 0
298 rivers[2] = 4; rivers[3] = 5
299 rivers[4] = 4; rivers[5] = 10
300 rivers[6] = 4; rivers[7] = 15
301 let s_r2: nx_int = nx_coherence_river_drainage(rivers, 1, 4, map, w, h)
302 if s_r2 != 0 { return __syscall(93, 2, 0, 0, 0, 0, 0) }
303
304 // T3: Forest in biome. All-forest biome map (biome 5 everywhere).
305 let biome: *i64 = (sys_mmap(n * NX_SIZEOF_NX_INT)) as *i64
306 var bi: nx_int = 0
307 while bi < n { biome[bi] = 5; bi = bi + 1 }
308 let forests: *i64 = (sys_mmap(2 * 3 * NX_SIZEOF_NX_INT)) as *i64
309 forests[0] = 4; forests[1] = 4
310 forests[2] = 8; forests[3] = 8
311 forests[4] = 12; forests[5] = 12
312 let s_f: nx_int = nx_coherence_forest_in_biome(forests, 3, biome, w, h)
313 if s_f != q { return __syscall(93, 10, 0, 0, 0, 0, 0) }
314
315 // T4: Forest in DESERT biome -- 0 valid.
316 var bi2: nx_int = 0
317 while bi2 < n { biome[bi2] = 8; bi2 = bi2 + 1 }
318 let s_f2: nx_int = nx_coherence_forest_in_biome(forests, 3, biome, w, h)
319 if s_f2 != 0 { return __syscall(93, 11, 0, 0, 0, 0, 0) }
320
321 // T5: Town on flat (constant heightmap) -> score ~ Q.
322 let map_flat: *i64 = (sys_mmap(n * NX_SIZEOF_NX_INT)) as *i64
323 var fi: nx_int = 0
324 while fi < n { map_flat[fi] = 100; fi = fi + 1 }
325 let towns: *i64 = (sys_mmap(3 * 2 * NX_SIZEOF_NX_INT)) as *i64
326 towns[0] = 8; towns[1] = 8; towns[2] = 3
327 towns[3] = 4; towns[4] = 4; towns[5] = 2
328 let s_t: nx_int = nx_coherence_town_on_flatland(towns, 2, map_flat, w, h, 1000)
329 if s_t < q * 9 / 10 { return __syscall(93, 20, 0, 0, 0, 0, 0) }
330
331 // T6: Town on rugged ramp -> score lower than flat.
332 let s_t2: nx_int = nx_coherence_town_on_flatland(towns, 2, map, w, h, 1000)
333 if s_t2 >= s_t { return __syscall(93, 21, 0, 0, 0, 0, 0) }
334
335 // T7: Castle on elevated ground. Place castle at the peak of the
336 // ramp (y = high) -- centre h > mean ring h.
337 let castles: *i64 = (sys_mmap(3 * 2 * NX_SIZEOF_NX_INT)) as *i64
338 castles[0] = 8; castles[1] = 13; castles[2] = 2
339 let s_c: nx_int = nx_coherence_castle_on_high(castles, 1, map, w, h)
340 if s_c < q / 2 { return __syscall(93, 30, 0, 0, 0, 0, 0) }
341
342 // T8: Castle in a valley (y = low) -> score 0 (centre below ring mean).
343 castles[0] = 8; castles[1] = 2; castles[2] = 2
344 let s_c2: nx_int = nx_coherence_castle_on_high(castles, 1, map, w, h)
345 if s_c2 > 0 { return __syscall(93, 31, 0, 0, 0, 0, 0) }
346
347 // T9: Compose into a verdict and finalize.
348 let verdict: *i64 = (sys_mmap(NX_LV_STRIDE * NX_SIZEOF_NX_INT)) as *i64
349 // s_r=Q, s_f=Q (from T3), s_t=Q, s_c=Q -> 4 wins -> S.
350 nx_inter_layer_coherence_grade(s_r, s_f, s_t, s_c, verdict)
351 if verdict[NX_LV_OFF_GRADE] != NX_LV_GRADE_S {
352 return __syscall(93, 40, 0, 0, 0, 0, 0)
353 }
354
355 // T10: Compose with a deliberately failing forest axis -> A grade
356 // (3 wins, 1 loss, wins = n - 1).
357 nx_inter_layer_coherence_grade(s_r, 0, s_t, s_c, verdict)
358 if verdict[NX_LV_OFF_GRADE] != NX_LV_GRADE_A {
359 return __syscall(93, 41, 0, 0, 0, 0, 0)
360 }
361
362 return 0
363}