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