code wiki / _hdl_build / nx_procgen_terrain_gate.nx
nx_procgen_terrain_gate.nx source
↩ module page · 221 lines · 10138 B
1// nx_procgen_terrain_gate.nx -- Gx/PROCGEN: rule-grounded, seed-diverse procedural terrain (the Infinigen
2// direction: procedural, asset-free, parametric-for-diversity, NOT random noise). FBM value-noise heightfield ->
3// D8 flow-direction -> flow-accumulation (descending-elevation pass = a real WATERSHED) -> rivers that PROVABLY
4// flow downhill (the clay-audit named "rivers that don't flow downhill" as the failure) -> shaded-relief render
5// (hillshade x altitude colormap, rivers blue). Three SEEDS side-by-side = parametric diversity (don't overfit to
6// one environment). All integer, deterministic, sovereign. Honest: this is coherence rung 1, not photoreal yet.
7// D001 MIGRATION 2026-09-02 (procgen PG19): the verdict was hand-rolled (PASS/FAIL prints, a fails counter, sys_exit(1)), so
8// /api/promote refused it and nothing outside could read its outcome. Every tooth is now a gv_check and the exit code IS the
9// verdict (nx_gate_verdict). The measurements, thresholds and the PNG are unchanged.
10// license_tier: ORIGINAL expect_exit: 0
11import "nx_syscalls.nx"
12import "nx_gate_verdict.nx"
13import "nx_png_write.nx"
14
15func pt_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
16func pt_pn(v: i64) -> i64 { let b: *u8=sys_mmap(32) as *u8; var x: i64=v; var ng: i64=0; if x<0{ng=1;x=0-x} var i: i64=31; if x==0{b[i]=48 as u8;i=i-1} while x>0{b[i]=(48+x%10) as u8;x=x/10;i=i-1} if ng==1{b[i]=45 as u8;i=i-1} sys_write(1,(b as i64+i+1) as *u8,31-i); return 0 }
17
18// integer hash -> [0,4095]
19func pt_hash2(x: i64, y: i64, seed: i64) -> i64 {
20 var a: i64=(x+1)*73856093 + (y+1)*19349663 + (seed+1)*83492791
21 a = a % 16777216
22 if a<0 { a = a + 16777216 }
23 a = (a*1103515245 + 12345) % 16777216
24 if a<0 { a = a + 16777216 }
25 return a % 4096
26}
27// smoothstep on Q12 t in [0,4096]
28func pt_smooth(t: i64) -> i64 { let t2: i64=(t*t)/4096; return (t2*(3*4096 - 2*t))/4096 }
29// value noise at (px,py) with `lat` lattice cells across dim `sz`, seed -> [0,4095]
30func pt_vnoise(px: i64, py: i64, lat: i64, sz: i64, seed: i64) -> i64 {
31 let fx: i64=(px*lat*4096)/sz
32 let fy: i64=(py*lat*4096)/sz
33 let gx: i64=fx/4096; let tx: i64=fx-gx*4096
34 let gy: i64=fy/4096; let ty: i64=fy-gy*4096
35 let c00: i64=pt_hash2(gx,gy,seed)
36 let c10: i64=pt_hash2(gx+1,gy,seed)
37 let c01: i64=pt_hash2(gx,gy+1,seed)
38 let c11: i64=pt_hash2(gx+1,gy+1,seed)
39 let sx: i64=pt_smooth(tx); let sy: i64=pt_smooth(ty)
40 let top: i64=c00 + ((c10-c00)*sx)/4096
41 let bot: i64=c01 + ((c11-c01)*sx)/4096
42 return top + ((bot-top)*sy)/4096
43}
44// FBM 5 octaves -> [0,HMAX]
45func pt_fbm(px: i64, py: i64, sz: i64, seed: i64) -> i64 {
46 var h: i64=0; var amp: i64=2048; var lat: i64=5; var norm: i64=0; var o: i64=0
47 while o<5 {
48 let v: i64=pt_vnoise(px,py,lat,sz,seed+o*131)
49 h = h + (v*amp)/4096
50 norm = norm + amp
51 amp = amp/2
52 lat = lat*2
53 o = o+1
54 }
55 if norm<1 { norm=1 }
56 return (h*4000)/norm
57}
58
59func main() -> i64 {
60 pt_puts("=== nx_procgen_terrain_gate -- rule-grounded seed-diverse procedural terrain (Infinigen direction) ===\n" as *u8)
61 let ctr: *i64=gv_ctr()
62 let TW: i64=280; let TH: i64=280; let GAP: i64=20
63 let W: i64=TW*3+GAP*2; let H: i64=TH
64 let N: i64=TW*TH
65 let rgb: *u8=sys_mmap(W*H*3+16)
66 var i: i64=0
67 while i<W*H { let o: i64=i*3; rgb[o]=10 as u8; rgb[o+1]=12 as u8; rgb[o+2]=18 as u8; i=i+1 }
68 let hf: *i64=sys_mmap(N*8) as *i64
69 let dn: *i64=sys_mmap(N*8) as *i64
70 let ac: *i64=sys_mmap(N*8) as *i64
71 let order: *i64=sys_mmap(N*8) as *i64
72 let NB: i64=260
73 let cnt: *i64=sys_mmap(NB*8) as *i64
74 let pos: *i64=sys_mmap(NB*8) as *i64
75 // dx/dy for 8 neighbors
76 let ndx: *i64=sys_mmap(8*8) as *i64
77 let ndy: *i64=sys_mmap(8*8) as *i64
78 ndx[0]=0-1; ndx[1]=0; ndx[2]=1; ndx[3]=0-1; ndx[4]=1; ndx[5]=0-1; ndx[6]=0; ndx[7]=1
79 ndy[0]=0-1; ndy[1]=0-1; ndy[2]=0-1; ndy[3]=0; ndy[4]=0; ndy[5]=1; ndy[6]=1; ndy[7]=1
80 var globaldownhill: i64=0; var globalriver: i64=0; var hrange0: i64=0
81 var s: i64=0
82 while s<3 {
83 let seed: i64=1000 + s*777
84 // 1) heightfield
85 var hmin: i64=999999; var hmax: i64=0
86 var py: i64=0
87 while py<TH {
88 var px: i64=0
89 while px<TW {
90 let hv: i64=pt_fbm(px,py,TW,seed)
91 hf[py*TW+px]=hv
92 if hv<hmin { hmin=hv }
93 if hv>hmax { hmax=hv }
94 px=px+1
95 }
96 py=py+1
97 }
98 if s==0 { hrange0=hmax-hmin }
99 // 2) D8 flow direction (interior; border -> self)
100 var c: i64=0
101 while c<N {
102 dn[c]=c; ac[c]=1
103 c=c+1
104 }
105 py=1
106 while py<TH-1 {
107 var px: i64=1
108 while px<TW-1 {
109 let ci: i64=py*TW+px
110 let hc: i64=hf[ci]
111 var best: i64=hc; var bidx: i64=ci
112 var k: i64=0
113 while k<8 {
114 let ni: i64=(py+ndy[k])*TW + (px+ndx[k])
115 if hf[ni]<best { best=hf[ni]; bidx=ni }
116 k=k+1
117 }
118 dn[ci]=bidx
119 px=px+1
120 }
121 py=py+1
122 }
123 // 3) counting sort by height/16 ascending
124 var b: i64=0
125 while b<NB { cnt[b]=0; b=b+1 }
126 c=0
127 while c<N { var bb: i64=hf[c]/16; if bb<0 {bb=0} if bb>=NB {bb=NB-1} cnt[bb]=cnt[bb]+1; c=c+1 }
128 pos[0]=0; b=1
129 while b<NB { pos[b]=pos[b-1]+cnt[b-1]; b=b+1 }
130 c=0
131 while c<N { var bb: i64=hf[c]/16; if bb<0 {bb=0} if bb>=NB {bb=NB-1} order[pos[bb]]=c; pos[bb]=pos[bb]+1; c=c+1 }
132 // 4) flow accumulation: process descending height
133 var oi: i64=N-1
134 while oi>=0 {
135 let cell: i64=order[oi]
136 let d: i64=dn[cell]
137 if d!=cell { ac[d]=ac[d]+ac[cell] }
138 oi=oi-1
139 }
140 // 5) render into canvas at x-offset
141 let xoff: i64=s*(TW+GAP)
142 var rdownhill: i64=0; var rriver: i64=0
143 py=1
144 while py<TH-1 {
145 var px: i64=1
146 while px<TW-1 {
147 let ci: i64=py*TW+px
148 let hc: i64=hf[ci]
149 // hillshade from gradient
150 let dzdx: i64=hf[ci+1]-hf[ci-1]
151 let dzdy: i64=hf[ci+TW]-hf[ci-TW]
152 var shade: i64=150 + (0-dzdx-dzdy)/6
153 if shade<40 { shade=40 }
154 if shade>255 { shade=255 }
155 // altitude colormap
156 var br: i64=0; var bg: i64=0; var bb2: i64=0
157 if hc<700 { br=40; bg=90; bb2=140 }
158 else { if hc<1600 { br=70; bg=120; bb2=60 }
159 else { if hc<2600 { br=110; bg=95; bb2=60 }
160 else { if hc<3300 { br=140; bg=132; bb2=122 }
161 else { br=222; bg=222; bb2=228 } } } }
162 let isriver: i64=ac[ci]
163 if isriver>90 { br=50; bg=110; bb2=205 }
164 var rr: i64=(br*shade)/255; var gg: i64=(bg*shade)/255; var bl: i64=(bb2*shade)/255
165 if rr>255 {rr=255} if gg>255 {gg=255} if bl>255 {bl=255}
166 let ox: i64=xoff+px
167 if ox<W { let o: i64=(py*W+ox)*3
168 rgb[o]=rr as u8; rgb[o+1]=gg as u8; rgb[o+2]=bl as u8 }
169 // downhill proof: river cells whose downstream is strictly lower
170 if isriver>90 { let d: i64=dn[ci]
171 if d!=ci { rriver=rriver+1; if hf[d]<hc { rdownhill=rdownhill+1 } } }
172 px=px+1
173 }
174 py=py+1
175 }
176 globaldownhill=globaldownhill+rdownhill; globalriver=globalriver+rriver
177 pt_puts(" seed "); pt_pn(seed); pt_puts(": hrange="); pt_pn(hmax-hmin); pt_puts(" rivercells="); pt_pn(rriver); pt_puts(" downhill="); pt_pn(rdownhill); pt_puts("\n" as *u8)
178 s=s+1
179 }
180 nx_png_write_rgb("knowledge/nx_procgen_terrain.png\x00" as *u8, rgb, W, H)
181 pt_puts(" wrote knowledge/nx_procgen_terrain.png\n" as *u8)
182 // T1 varied heightfield
183 var t1: i64=0
184 if hrange0>1500 { t1=1 }
185 pt_puts(" hrange(seed0)="); pt_pn(hrange0); pt_puts("\n" as *u8)
186 gv_check("T1 heightfield is varied, not flat (seed0 height range over 1500)" as *u8, t1, ctr)
187 // T2 water flows downhill (watershed coherence)
188 var pct: i64=0
189 if globalriver>0 { pct=(globaldownhill*100)/globalriver }
190 pt_puts(" river cells="); pt_pn(globalriver); pt_puts(" flowing-downhill="); pt_pn(pct); pt_puts("%\n" as *u8)
191 var t2: i64=0
192 if globalriver>200 { if pct>=95 { t2=1 } }
193 gv_check("T2 rivers provably flow downhill: over 200 river cells and at least 95 percent with a strictly lower downstream cell (a real watershed, not noise)" as *u8, t2, ctr)
194 // T3 seed diversity (parametric = don't overfit)
195 var dsum: i64=0; var samp: i64=0; var sy2: i64=10
196 while sy2<TH-10 {
197 var sx2: i64=10
198 while sx2<TW-10 {
199 let a: i64=pt_fbm(sx2,sy2,TW,1000)
200 let c2: i64=pt_fbm(sx2,sy2,TW,1000+2*777)
201 var dd: i64=a-c2; if dd<0 {dd=0-dd}
202 dsum=dsum+dd; samp=samp+1
203 sx2=sx2+23
204 }
205 sy2=sy2+23
206 }
207 var davg: i64=0
208 if samp>0 { davg=dsum/samp }
209 pt_puts(" seed diversity avg|dh|="); pt_pn(davg); pt_puts("\n" as *u8)
210 var t3: i64=0
211 if davg>200 { t3=1 }
212 gv_check("T3 seeds produce diverse terrain: mean absolute height difference between two seeds over 200 (parametric, no overfit)" as *u8, t3, ctr)
213 // T4 png
214 let szp: *i64=sys_mmap(16) as *i64
215 let rb: *u8=sys_read_file("knowledge/nx_procgen_terrain.png\x00" as *u8, szp)
216 var t4: i64=0
217 if (rb as i64)!=0 { if szp[0]>1000 { t4=1 } }
218 if (rb as i64)!=0 { pt_puts(" png bytes="); pt_pn(szp[0]); pt_puts("\n" as *u8) }
219 gv_check("T4 the shaded-relief PNG was written and reads back over 1000 bytes" as *u8, t4, ctr)
220 return gv_verdict("nx_procgen_terrain_gate" as *u8, ctr, "rule-grounded, seed-diverse, watershed-coherent procedural terrain (Infinigen direction, sovereign integer); coherence rung, not photoreal" as *u8)
221}