code wiki / _hdl_build / nx_procgen.nx

nx_procgen.nx source

↩ module page · 77 lines · 3686 B

1// nx_procgen.nx -- procedural DUNGEON/maze generation, pure-Nishi NO-FLOAT (seeded integer LCG, iterative DFS). 2// Closes the RESEARCHED->PRESENT gap the research census (GR-S-R5) named for P5 (procedural-generation src=13, 3// the highest-evidence unbuilt cell). Randomized-DFS "recursive backtracker" carves a PERFECT maze on a grid 4// (0=open 1=wall): fully connected by construction, exactly one path between any two open cells. Deterministic 5// (same seed -> same maze). Reusable: roguelike/dungeon levels, RTS maps, tower-defense paths. Composes with 6// nx_pathfind (A*) -- procgen generates, pathfinding verifies reachability. license_tier: ORIGINAL 7import "nx_syscalls.nx" 8const K_MAGIC_1103515245: i64 = 1103515245 9const K_MAGIC_12345: i64 = 12345 10 11func pg_rng(seed: *i64) -> i64 { seed[0] = (seed[0]*K_MAGIC_1103515245 + K_MAGIC_12345) & 0x7fffffff; return seed[0] } 12 13// carve a perfect maze into grid (w*h, ODD dims). all-walls -> iterative DFS from (1,1). `stack` = caller scratch 14// (w*h i64). Deterministic in `seedv`. After: open=0, wall=1, fully connected. Internal scratch via sys_mmap. 15func pg_maze(grid: *i64, w: i64, h: i64, seedv: i64, stack: *i64) -> i64 { 16 let n: i64 = w*h 17 var i: i64 = 0; while i < n { grid[i]=1; i=i+1 } 18 let seed: *i64 = sys_mmap(8) as *i64; seed[0] = seedv 19 let cnx: *i64 = sys_mmap(4*8) as *i64 // up to 4 candidate neighbour cells 20 let cwx: *i64 = sys_mmap(4*8) as *i64 // the wall cell between cur and each candidate 21 var sp: i64 = 0 22 grid[1*w + 1] = 0 23 stack[sp] = 1*w + 1; sp = sp + 1 24 while sp > 0 { 25 let cur: i64 = stack[sp-1] 26 let cx: i64 = cur % w 27 let cy: i64 = cur / w 28 var cc: i64 = 0 29 if cx+2 <= w-2 { if grid[cy*w + (cx+2)]==1 { cnx[cc]=cy*w+(cx+2); cwx[cc]=cy*w+(cx+1); cc=cc+1 } } // right 30 if cx-2 >= 1 { if grid[cy*w + (cx-2)]==1 { cnx[cc]=cy*w+(cx-2); cwx[cc]=cy*w+(cx-1); cc=cc+1 } } // left 31 if cy+2 <= h-2 { if grid[(cy+2)*w + cx]==1 { cnx[cc]=(cy+2)*w+cx; cwx[cc]=(cy+1)*w+cx; cc=cc+1 } } // down 32 if cy-2 >= 1 { if grid[(cy-2)*w + cx]==1 { cnx[cc]=(cy-2)*w+cx; cwx[cc]=(cy-1)*w+cx; cc=cc+1 } } // up 33 if cc > 0 { 34 let pick: i64 = pg_rng(seed) % cc 35 grid[cwx[pick]] = 0 36 grid[cnx[pick]] = 0 37 stack[sp] = cnx[pick]; sp = sp + 1 38 } else { 39 sp = sp - 1 40 } 41 } 42 return 0 43} 44 45func pg_count_open(grid: *i64, n: i64) -> i64 { var c: i64=0; var i: i64=0; while i<n { if grid[i]==0 { c=c+1 } i=i+1 } return c } 46 47// BFS flood from (sx,sy); returns # of open cells reachable. visited+queue = caller scratch (w*h i64 each). 48func pg_flood_count(grid: *i64, w: i64, h: i64, sx: i64, sy: i64, visited: *i64, queue: *i64) -> i64 { 49 let n: i64 = w*h 50 var i: i64 = 0; while i < n { visited[i]=0; i=i+1 } 51 let s: i64 = sy*w + sx 52 if grid[s] != 0 { return 0 } 53 var head: i64 = 0 54 var tail: i64 = 0 55 queue[tail]=s; tail=tail+1; visited[s]=1 56 var reached: i64 = 0 57 while head < tail { 58 let cur: i64 = queue[head]; head=head+1 59 reached = reached + 1 60 let cx: i64 = cur % w 61 let cy: i64 = cur / w 62 var d: i64 = 0 63 while d < 4 { 64 var nx: i64 = cx; var ny: i64 = cy 65 if d==0 { nx=cx+1 } 66 if d==1 { nx=cx-1 } 67 if d==2 { ny=cy+1 } 68 if d==3 { ny=cy-1 } 69 if nx>=0 { if nx<w { if ny>=0 { if ny<h { 70 let nb: i64 = ny*w+nx 71 if grid[nb]==0 { if visited[nb]==0 { visited[nb]=1; queue[tail]=nb; tail=tail+1 } } 72 } } } } 73 d = d + 1 74 } 75 } 76 return reached 77}