code wiki / (root) / nx_canon_solver.nx

nx_canon_solver.nx source

↩ module page · 198 lines · 8377 B

1// nx_canon_solver.nx -- ★B1a of the published plan (/world/procgen): the CANON → CONSTRAINT bridge, i.e. 2// the canon's BEAUTY LINTER. Reads a body/face canon (P part headers + R control rings -- field semantics 3// read FROM nx_body_gen.nx bg_parse_canon, the emitter that owns the format: P <mirror> <rotX> <ox> <oy> 4// <oz> [mat] [rotZ] [rotY]; R <y> <x0> <z0> <a> <b>. This is the SECOND reader of that format -- extract a 5// shared canon lib on the THIRD, per the rule of three), emits `part p<i> <ox> <oy> <oz> <r>` rows, appends 6// a RULES file (an nx_part_solver conf referencing p<i> names: station/inside/clear/band/ratio -- the 7// anatomical + beauty bands as DATA), solves, and reports per-part TYPED vs SOLVED deltas. 8// nx_canon_solver <canon.dat> <rules.conf> 9// Outcomes: exit 0 CLEAN (every delta 0 -- the typed canon already satisfies the rules) | exit 0 ADJUSTED 10// (SOLVED with deltas>0: each delta is a NAMED lint finding and a writeback candidate) | exit 4 11// INCONSISTENT (rules unsatisfiable -- worst constraint named, never fabricated) | 3 parse | 1 io | 2 usage. 12// Bilateral symmetry is BY CONSTRUCTION in the emitter (mirror=1 emits both sides), so the rules carry the 13// OTHER relations: stations, containment, clearance, φ-band ratios between parts. 14// license_tier: ORIGINAL expect_exit: 0 15import "nx_syscalls.nx" 16import "nx_part_solver_lib.nx" 17const CS_MAGIC_4096: i64 = 4096 18 19const CS_MAXP: i64 = 256 20 21// signed-int token reader -- the bg_rdint shape (nx_body_gen.nx), kept call-compatible 22func cs_rdint(b: *u8, pos: *i64, end: i64) -> i64 { 23 var i: i64 = pos[0] 24 var go: i64 = 1 25 while go == 1 { 26 if i >= end { go = 0 } else { 27 let c: i64 = b[i] as i64 28 if c == 45 { go = 0 } else { 29 if c >= 48 { if c <= 57 { go = 0 } else { i = i+1 } } else { i = i+1 } 30 } 31 } 32 } 33 var sg: i64 = 1 34 if i < end { if (b[i] as i64) == 45 { sg = 0-1; i = i+1 } } 35 var v: i64 = 0 36 var g2: i64 = 1 37 while g2 == 1 { 38 if i >= end { g2 = 0 } else { 39 let c: i64 = b[i] as i64 40 if c >= 48 { if c <= 57 { v = v*10 + (c-48); i = i+1 } else { g2 = 0 } } else { g2 = 0 } 41 } 42 } 43 pos[0] = i 44 return v*sg 45} 46// parse P/R rows: typed part positions (ox,oy,oz) + per-part max ring semi-axis as the radius. 47func cs_parse(b: *u8, len: i64, tx: *i64, ty: *i64, tz: *i64, tr: *i64, tm: *i64, nring: *i64) -> i64 { 48 let pos: *i64 = sys_mmap(16) as *i64 49 var np: i64 = 0 50 var nr: i64 = 0 51 var i: i64 = 0 52 while i < len { 53 var j: i64 = i 54 var sk: i64 = 1 55 while sk == 1 { 56 if j >= len { sk = 0 } else { 57 let c: i64 = b[j] as i64 58 if c == 32 { j = j+1 } else { if c == 9 { j = j+1 } else { sk = 0 } } 59 } 60 } 61 var tag: i64 = 0 62 if j < len { tag = b[j] as i64 } 63 var e: i64 = i 64 var fe: i64 = 1 65 while fe == 1 { if e >= len { fe = 0 } else { if (b[e] as i64) == 10 { fe = 0 } else { e = e+1 } } } 66 if tag == 80 { 67 if np >= CS_MAXP { ps_err("CANON-SOLVER-REFUSE part count exceeds solver capacity\n" as *u8); return 0 - 3 } 68 pos[0] = j+1 69 tm[np] = cs_rdint(b, pos, e) 70 cs_rdint(b, pos, e) 71 tx[np] = cs_rdint(b, pos, e) 72 ty[np] = cs_rdint(b, pos, e) 73 tz[np] = cs_rdint(b, pos, e) 74 tr[np] = 1 75 np = np + 1 76 } 77 if tag == 82 { 78 if np > 0 { 79 pos[0] = j+1 80 cs_rdint(b, pos, e) 81 cs_rdint(b, pos, e) 82 cs_rdint(b, pos, e) 83 let ra: i64 = cs_rdint(b, pos, e) 84 if ra > tr[np-1] { tr[np-1] = ra } 85 nr = nr + 1 86 } 87 } 88 i = e + 1 89 } 90 nring[0] = nr 91 return np 92} 93func cs_cats(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; var p: i64 = o; while s[i] != (0 as u8) { d[p] = s[i]; p = p + 1; i = i + 1 } return p } 94func cs_catn(d: *u8, o: i64, v: i64) -> i64 { 95 var p: i64 = o 96 var m: i64 = v 97 if m < 0 { d[p] = 45 as u8; p = p + 1; m = 0 - m } 98 if m == 0 { d[p] = 48 as u8; return p + 1 } 99 let t: *u8 = sys_mmap(32) 100 var k: i64 = 0 101 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 102 var i: i64 = 0 103 while i < k { d[p] = t[k-1-i]; p = p + 1; i = i + 1 } 104 return p 105} 106 107func main(argc: i64, argv: *i64) -> i64 { 108 if argc < 3 { 109 ps_err("usage: nx_canon_solver <canon.dat> <rules.conf>\n" as *u8) 110 ps_err(" lints a P/R canon against anatomical+beauty constraint rules (part names p0..pN in the rules).\n" as *u8) 111 ps_err(" exit 0 = CLEAN or ADJUSTED (deltas printed = lint findings); 4 = INCONSISTENT (worst named).\n" as *u8) 112 sys_exit(2) 113 return 2 114 } 115 let lp: *i64 = sys_mmap(16) as *i64 116 let cb: *u8 = sys_read_file(argv[1] as *u8, lp) 117 if (cb as i64) == 0 { ps_err("CANON-SOLVER-REFUSE cannot read canon\n" as *u8); sys_exit(1); return 1 } 118 let clen: i64 = lp[0] 119 let lp2: *i64 = sys_mmap(16) as *i64 120 let rb: *u8 = sys_read_file(argv[2] as *u8, lp2) 121 if (rb as i64) == 0 { ps_err("CANON-SOLVER-REFUSE cannot read rules\n" as *u8); sys_exit(1); return 1 } 122 let rlen: i64 = lp2[0] 123 let tx: *i64 = sys_mmap(CS_MAXP*8) as *i64 124 let ty: *i64 = sys_mmap(CS_MAXP*8) as *i64 125 let tz: *i64 = sys_mmap(CS_MAXP*8) as *i64 126 let tr: *i64 = sys_mmap(CS_MAXP*8) as *i64 127 let tm: *i64 = sys_mmap(CS_MAXP*8) as *i64 128 let nring: *i64 = sys_mmap(16) as *i64 129 let np: i64 = cs_parse(cb, clen, tx, ty, tz, tr, tm, nring) 130 if np < 0 { sys_exit(3); return 3 } 131 if np == 0 { ps_err("CANON-SOLVER-REFUSE no P rows in canon\n" as *u8); sys_exit(3); return 3 } 132 ps_out("CANON-SOLVER parts=" as *u8); ps_outn(np) 133 ps_out(" rings=" as *u8); ps_outn(nring[0]) 134 ps_out("\n" as *u8) 135 // conf = generated part rows + the rules file verbatim 136 let conf: *u8 = sys_mmap(np*64 + rlen + CS_MAGIC_4096) 137 var o: i64 = 0 138 var i: i64 = 0 139 while i < np { 140 o = cs_cats(conf, o, "part p" as *u8) 141 o = cs_catn(conf, o, i) 142 o = cs_cats(conf, o, " " as *u8) 143 o = cs_catn(conf, o, tx[i]) 144 o = cs_cats(conf, o, " " as *u8) 145 o = cs_catn(conf, o, ty[i]) 146 o = cs_cats(conf, o, " " as *u8) 147 o = cs_catn(conf, o, tz[i]) 148 o = cs_cats(conf, o, " " as *u8) 149 o = cs_catn(conf, o, tr[i]) 150 o = cs_cats(conf, o, "\n" as *u8) 151 i = i + 1 152 } 153 var q: i64 = 0 154 while q < rlen { conf[o] = rb[q]; o = o + 1; q = q + 1 } 155 let rc: i64 = ps_load(conf, o) 156 if rc != 0 { sys_exit(0 - rc); return 0 - rc } 157 let ok: i64 = ps_solve() 158 // per-part deltas: typed vs solved (the lint findings) 159 var adjusted: i64 = 0 160 i = 0 161 while i < np { 162 var dx: i64 = ps_x(i) - tx[i] 163 var dy: i64 = ps_y(i) - ty[i] 164 var dz: i64 = ps_z(i) - tz[i] 165 if dx < 0 { dx = 0 - dx } 166 if dy < 0 { dy = 0 - dy } 167 if dz < 0 { dz = 0 - dz } 168 let dd: i64 = dx + dy + dz 169 if dd > 0 { 170 adjusted = adjusted + 1 171 ps_out(" LINT p" as *u8); ps_outn(i) 172 ps_out(" typed " as *u8); ps_outn(tx[i]); ps_out("," as *u8); ps_outn(ty[i]); ps_out("," as *u8); ps_outn(tz[i]) 173 ps_out(" solved " as *u8); ps_outn(ps_x(i)); ps_out("," as *u8); ps_outn(ps_y(i)); ps_out("," as *u8); ps_outn(ps_z(i)) 174 ps_out(" delta=" as *u8); ps_outn(dd) 175 ps_out("\n" as *u8) 176 } 177 i = i + 1 178 } 179 if ok == 1 { 180 if adjusted == 0 { 181 ps_out("CANON-SOLVER CLEAN parts=" as *u8); ps_outn(np) 182 ps_out(" -- the typed canon already satisfies every rule\n" as *u8) 183 } else { 184 ps_out("CANON-SOLVER ADJUSTED parts=" as *u8); ps_outn(adjusted) 185 ps_out("/" as *u8); ps_outn(np) 186 ps_out(" -- each LINT row is a canon defect and a writeback candidate\n" as *u8) 187 } 188 sys_exit(0) 189 return 0 190 } 191 ps_out("CANON-SOLVER INCONSISTENT worst=" as *u8) 192 ps_out(ps_kindname(ps_ckind(ps_worst()))) 193 ps_out(" idx=" as *u8); ps_outn(ps_worst()) 194 ps_out(" violation=" as *u8); ps_outn(ps_worstv()) 195 ps_out(" -- rules unsatisfiable against this canon; refusing to fabricate\n" as *u8) 196 sys_exit(4) 197 return 4 198}