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}