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}