code wiki / _hdl_build / nx_vnsprite_gate.nx
nx_vnsprite_gate.nx source
↩ module page · 409 lines · 18001 B
1// nx_vnsprite_gate.nx -- liar-kill gate for the VN sprite compositor (gamebench bit 13).
2// T1 Z-ORDER is real: at an overlap the higher-z layer's pixel wins; swapping z swaps the winner.
3// T2 ALPHA + KEY are exact: a 50% blend lands on the exact integer mix per channel, and a key-color
4// pixel is fully transparent (background shows through untouched).
5// T3 CLIPPING cannot write out of bounds: blits hanging off every edge leave 8 canary cells intact.
6// T4 EXPRESSION SWAP IS VISIBLE: base+smile vs base+frown differ in >=20 pixels; recomposing the
7// same expression twice differs in 0 (visible AND deterministic -- the VN beat that matters).
8// T5 SCENE STATE ROUND-TRIPS: the layer table through gs_save -> ZEROED -> gs_load -> recompose is
9// frame-checksum identical (parts doctrine: everything persists through the save part).
10// T6 DETERMINISM: the full scene composed twice is checksum-identical.
11// T7 ANTI-VACUITY: the composed scene differs from the bare background in >=500 px with >=5
12// distinct sprite colors (a compositor that draws nothing cannot pass).
13// T8 SCENE ARTIFACT: a full VN frame (gradient bg + two characters + alpha textbox + name tag) is
14// written as a sovereign PNG to knowledge/nx_vnsprite_scene.png -- the board's evidence.
15// T9 GROUP EXCLUSIVITY (the Ren'Py layeredimage bar, researched 2026-07-29): smile and frown are
16// two attributes of ONE group; a single vsp_setg call swaps them with no manual bookkeeping,
17// and exactly one is ever composed (probed at pixel level both ways).
18// T10 DISSOLVE EXACT: the core VN transition -- t=0 equals frame A, t=256 equals frame B (full-frame
19// checksums), and the t=128 midpoint is the exact integer channel mix at a probed pixel.
20// license_tier: ORIGINAL expect_exit: 0
21import "nx_syscalls.nx"
22import "nx_estate_path.nx" // ep_anchor: the CWD must not decide this organ's verdict
23import "nx_vnsprite.nx"
24import "nx_gamesave.nx"
25import "nx_png.nx"
26const VG_B: i64 = 65536
27const VG_CANARY: i64 = 12345678
28const VG_STAMP: i64 = 20260729
29const VG_SCHEMA: i64 = 78
30const VG_W: i64 = 640
31const VG_H: i64 = 360
32
33func hw3(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
34func hn3(v: i64) -> i64 {
35 let t: *u8 = sys_mmap(32)
36 var m: i64 = v
37 if m == 0 { sys_write(1, "0" as *u8, 1); return 0 }
38 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
39 var k: i64 = 0
40 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m/10; k = k + 1 }
41 while k > 0 { k = k - 1; sys_write(1, (t as i64 + k) as *u8, 1) }
42 return 0
43}
44// procedural character sprite: key-filled, head circle + torso rect (art quality is NOT this axis)
45func mk_body(spr: *i64, sw: i64, sh: i64, key: i64, skin: i64, shirt: i64) -> i64 {
46 var i: i64 = 0
47 while i < sw*sh { spr[i] = key; i = i + 1 }
48 let cx: i64 = sw/2
49 let r: i64 = sw/3
50 var y: i64 = 0
51 while y < sh {
52 var x: i64 = 0
53 while x < sw {
54 let dy: i64 = y - r
55 let dx: i64 = x - cx
56 if dx*dx + dy*dy <= r*r { spr[y*sw + x] = skin }
57 if y > 2*r { if x > sw/5 { if x < sw - sw/5 { spr[y*sw + x] = shirt } } }
58 x = x + 1
59 }
60 y = y + 1
61 }
62 return 0
63}
64// expression sprite: mode 0 = smile (corners up), mode 1 = frown (corners down)
65func mk_expr(spr: *i64, sw: i64, sh: i64, key: i64, ink: i64, mode: i64) -> i64 {
66 var i: i64 = 0
67 while i < sw*sh { spr[i] = key; i = i + 1 }
68 var x: i64 = 1
69 while x < sw - 1 {
70 var y: i64 = sh/2
71 let edge: i64 = sw/4
72 var off: i64 = 0
73 if x < edge { off = 1 }
74 if x >= sw - edge { off = 1 }
75 if mode == 0 { y = y - off + 1 }
76 if mode == 1 { y = y + off - 1 }
77 if y >= 0 { if y < sh { spr[y*sw + x] = ink } }
78 x = x + 1
79 }
80 return 0
81}
82func fill_rect(fb: *i64, W: i64, H: i64, x0: i64, y0: i64, x1: i64, y1: i64, c: i64) -> i64 {
83 var y: i64 = y0
84 if y < 0 { y = 0 }
85 while y < y1 {
86 if y >= H { y = y1 } else {
87 var x: i64 = x0
88 if x < 0 { x = 0 }
89 while x < x1 {
90 if x >= W { x = x1 } else { fb[y*W + x] = c; x = x + 1 }
91 }
92 y = y + 1
93 }
94 }
95 return 0
96}
97// scene layer table, Ren'Py-shaped: 5 rows x 7 fields [spr,x,y,alpha,z,vis,group].
98// rows 2+3 = smile+frown, BOTH declared, in exclusive group 1 (attributes, not separate scenes);
99// selection happens via vsp_setg -- never by editing the table by hand.
100func fs_layers(lay: *i64) -> i64 {
101 lay[0]=0; lay[1]=140; lay[2]=140; lay[3]=256; lay[4]=10; lay[5]=1; lay[6]=0
102 lay[7]=1; lay[8]=430; lay[9]=150; lay[10]=256; lay[11]=11; lay[12]=1; lay[13]=0
103 lay[14]=2; lay[15]=160; lay[16]=175; lay[17]=256; lay[18]=20; lay[19]=1; lay[20]=1
104 lay[21]=3; lay[22]=160; lay[23]=175; lay[24]=256; lay[25]=20; lay[26]=0; lay[27]=1
105 lay[28]=6; lay[29]=20; lay[30]=282; lay[31]=210; lay[32]=30; lay[33]=1; lay[34]=0
106 return 0
107}
108// bare background: vertical dusk gradient (shared by the scene and the T7 baseline)
109func fs_bg(fb: *i64) -> i64 {
110 var y: i64 = 0
111 while y < VG_H {
112 var x: i64 = 0
113 let rr: i64 = 24 + 40*(VG_H - y)/VG_H
114 let gg: i64 = 26 + 52*(VG_H - y)/VG_H
115 let bb: i64 = 40 + 90*(VG_H - y)/VG_H
116 let c: i64 = rr + gg*256 + bb*VG_B
117 while x < VG_W { fb[y*VG_W + x] = c; x = x + 1 }
118 y = y + 1
119 }
120 return 0
121}
122// full scene: gradient bg, the composed layer table, then a name tag block
123func fs_scene(fb: *i64, sprites: *i64, sprw: *i64, sprh: *i64, lay: *i64, key: i64) -> i64 {
124 fs_bg(fb)
125 vsp_compose(fb, VG_W, VG_H, sprites, sprw, sprh, lay, 5, key)
126 fill_rect(fb, VG_W, VG_H, 24, 262, 140, 284, 70 + 50*256 + 110*VG_B)
127 return 0
128}
129
130func main() -> i64 {
131 // ANCHOR FIRST (2026-08-04, nx_cwdguard finding): this organ reads a RELATIVE
132 // knowledge/ path, so its answer depended on where it was launched. No-op when
133 // already at the estate root, so the cron/MCP context is unchanged.
134 ep_anchor()
135 hw3("=== nx_vnsprite_gate: VN sprite compositing -- real, or a rubber stamp? ===\n\n" as *u8)
136 var pass: i64 = 0
137 var checks: i64 = 0
138 let key: i64 = 255 + 0*256 + 255*VG_B // magenta key
139 let npx: i64 = VG_W*VG_H
140 let fb: *i64 = sys_mmap((npx + 8)*8) as *i64
141 let fb2: *i64 = sys_mmap((npx + 8)*8) as *i64
142
143 // sprite tables: 0 bodyA 1 bodyB 2 smile 3 frown 4 red patch 5 blue patch 6 textbox
144 let sprites: *i64 = sys_mmap(8*8) as *i64
145 let sprw: *i64 = sys_mmap(8*8) as *i64
146 let sprh: *i64 = sys_mmap(8*8) as *i64
147 let bodyA: *i64 = sys_mmap(60*140*8) as *i64
148 mk_body(bodyA, 60, 140, key, 244 + 214*256 + 196*VG_B, 60 + 90*256 + 170*VG_B)
149 sprites[0] = bodyA as i64; sprw[0] = 60; sprh[0] = 140
150 let bodyB: *i64 = sys_mmap(60*140*8) as *i64
151 mk_body(bodyB, 60, 140, key, 232 + 190*256 + 170*VG_B, 160 + 60*256 + 80*VG_B)
152 sprites[1] = bodyB as i64; sprw[1] = 60; sprh[1] = 140
153 let sm: *i64 = sys_mmap(20*10*8) as *i64
154 mk_expr(sm, 20, 10, key, 120 + 30*256 + 30*VG_B, 0)
155 sprites[2] = sm as i64; sprw[2] = 20; sprh[2] = 10
156 let fr: *i64 = sys_mmap(20*10*8) as *i64
157 mk_expr(fr, 20, 10, key, 120 + 30*256 + 30*VG_B, 1)
158 sprites[3] = fr as i64; sprw[3] = 20; sprh[3] = 10
159 let redp: *i64 = sys_mmap(30*30*8) as *i64
160 var i: i64 = 0
161 while i < 900 { redp[i] = 200 + 60*256 + 60*VG_B; i = i + 1 }
162 sprites[4] = redp as i64; sprw[4] = 30; sprh[4] = 30
163 let blup: *i64 = sys_mmap(30*30*8) as *i64
164 i = 0
165 while i < 900 { blup[i] = 20 + 40*256 + 80*VG_B; i = i + 1 }
166 sprites[5] = blup as i64; sprw[5] = 30; sprh[5] = 30
167 let tbox: *i64 = sys_mmap(600*70*8) as *i64
168 i = 0
169 while i < 600*70 { tbox[i] = 18 + 22*256 + 40*VG_B; i = i + 1 }
170 sprites[6] = tbox as i64; sprw[6] = 600; sprh[6] = 70
171
172 // ---------- T1 z-order ----------
173 checks = checks + 1
174 let bgc: i64 = 30 + 34*256 + 44*VG_B
175 let lay: *i64 = sys_mmap(16*VSP_LROW*8) as *i64
176 vsp_clear(fb, VG_W, VG_H, bgc)
177 lay[0]=4; lay[1]=100; lay[2]=100; lay[3]=256; lay[4]=1; lay[5]=1; lay[6]=0
178 lay[7]=5; lay[8]=110; lay[9]=100; lay[10]=256; lay[11]=2; lay[12]=1; lay[13]=0
179 vsp_compose(fb, VG_W, VG_H, sprites, sprw, sprh, lay, 2, key)
180 let pA: i64 = fb[105*VG_W + 115]
181 lay[4]=2; lay[11]=1
182 vsp_clear(fb, VG_W, VG_H, bgc)
183 vsp_compose(fb, VG_W, VG_H, sprites, sprw, sprh, lay, 2, key)
184 let pB: i64 = fb[105*VG_W + 115]
185 var t1ok: i64 = 0
186 if pA == 20 + 40*256 + 80*VG_B { if pB == 200 + 60*256 + 60*VG_B { t1ok = 1 } }
187 if t1ok == 1 {
188 hw3("T1 GREEN z-order real: overlap pixel = top layer both ways (blue on top, then red after the z swap)\n" as *u8)
189 pass = pass + 1
190 } else {
191 hw3("T1 RED z-order broken: got " as *u8); hn3(pA); hw3(" then " as *u8); hn3(pB); hw3("\n" as *u8)
192 }
193
194 // ---------- T2 alpha exact + key transparent ----------
195 checks = checks + 1
196 vsp_clear(fb, VG_W, VG_H, 20 + 40*256 + 80*VG_B)
197 vsp_blit(fb, VG_W, VG_H, redp, 30, 30, 50, 50, key, 128)
198 let pm: i64 = fb[65*VG_W + 65]
199 let expct: i64 = 110 + 50*256 + 70*VG_B
200 vsp_blit(fb, VG_W, VG_H, sm, 20, 10, 300, 50, key, 256)
201 let pk: i64 = fb[51*VG_W + 305]
202 var t2ok: i64 = 0
203 if pm == expct { if pk == 20 + 40*256 + 80*VG_B { t2ok = 1 } }
204 if t2ok == 1 {
205 hw3("T2 GREEN alpha exact ((200,60,60) at 128 over (20,40,80) = (110,50,70)) and key pixels fully transparent\n" as *u8)
206 pass = pass + 1
207 } else {
208 hw3("T2 RED blend/key broken: mix=" as *u8); hn3(pm); hw3(" want " as *u8); hn3(expct)
209 hw3(" keyprobe=" as *u8); hn3(pk); hw3("\n" as *u8)
210 }
211
212 // ---------- T3 clip canaries ----------
213 checks = checks + 1
214 i = 0
215 while i < 8 { fb[npx + i] = VG_CANARY; i = i + 1 }
216 vsp_blit(fb, VG_W, VG_H, tbox, 600, 70, VG_W - 20, VG_H - 20, key, 256)
217 vsp_blit(fb, VG_W, VG_H, tbox, 600, 70, 0 - 500, 0 - 30, key, 256)
218 vsp_blit(fb, VG_W, VG_H, redp, 30, 30, VG_W + 5, 10, key, 256)
219 var canok: i64 = 1
220 i = 0
221 while i < 8 { if fb[npx + i] != VG_CANARY { canok = 0 } i = i + 1 }
222 if canok == 1 {
223 hw3("T3 GREEN clipped blits: off-edge sprites on all sides, 8 canary cells intact\n" as *u8)
224 pass = pass + 1
225 } else {
226 hw3("T3 RED OUT-OF-BOUNDS WRITE past the framebuffer\n" as *u8)
227 }
228
229 // ---------- T4 expression swap visible (via the GROUP call, never hand-editing the table) ----------
230 checks = checks + 1
231 fs_layers(lay)
232 vsp_setg(lay, 5, 1, 2)
233 fs_scene(fb, sprites, sprw, sprh, lay, key)
234 vsp_setg(lay, 5, 1, 3)
235 fs_scene(fb2, sprites, sprw, sprh, lay, key)
236 var diff: i64 = 0
237 i = 0
238 while i < npx { if fb[i] != fb2[i] { diff = diff + 1 } i = i + 1 }
239 vsp_setg(lay, 5, 1, 3)
240 fs_scene(fb, sprites, sprw, sprh, lay, key)
241 var same: i64 = 0
242 i = 0
243 while i < npx { if fb[i] != fb2[i] { same = same + 1 } i = i + 1 }
244 var t4ok: i64 = 0
245 if diff >= 20 { if same == 0 { t4ok = 1 } }
246 if t4ok == 1 {
247 hw3("T4 GREEN expression swap visible: smile vs frown differ in " as *u8); hn3(diff)
248 hw3(" px; same expression twice differs in 0\n" as *u8)
249 pass = pass + 1
250 } else {
251 hw3("T4 RED expression change invisible or nondeterministic: diff=" as *u8); hn3(diff)
252 hw3(" same=" as *u8); hn3(same); hw3("\n" as *u8)
253 }
254
255 // ---------- T5 scene state round-trip through gs_save ----------
256 checks = checks + 1
257 fs_layers(lay)
258 vsp_setg(lay, 5, 1, 2)
259 fs_scene(fb, sprites, sprw, sprh, lay, key)
260 let ck0: i64 = vsp_fbck(fb, VG_W, VG_H)
261 let sret: i64 = gs_save("knowledge/nx_vnsprite_scene.sav" as *u8, VG_SCHEMA, lay, 35, VG_STAMP)
262 i = 0
263 while i < 35 { lay[i] = 0; i = i + 1 }
264 let atp: *i64 = sys_mmap(32) as *i64
265 let lret: i64 = gs_load("knowledge/nx_vnsprite_scene.sav" as *u8, lay, 35, atp)
266 fs_scene(fb2, sprites, sprw, sprh, lay, key)
267 let ck1: i64 = vsp_fbck(fb2, VG_W, VG_H)
268 var t5ok: i64 = 0
269 if sret > 0 { if lret >= 35 { if ck0 == ck1 { t5ok = 1 } } }
270 if t5ok == 1 {
271 hw3("T5 GREEN scene state round-trips: gs_save -> ZEROED -> gs_load -> recompose, frame ck " as *u8)
272 hn3(ck1); hw3(" identical\n" as *u8)
273 pass = pass + 1
274 } else {
275 hw3("T5 RED save round-trip broke the scene: ck " as *u8); hn3(ck0); hw3(" vs " as *u8); hn3(ck1)
276 hw3(" (save rc=" as *u8); hn3(sret); hw3(" load rc=" as *u8); hn3(lret); hw3(")\n" as *u8)
277 }
278
279 // ---------- T6 determinism ----------
280 checks = checks + 1
281 fs_scene(fb, sprites, sprw, sprh, lay, key)
282 fs_scene(fb2, sprites, sprw, sprh, lay, key)
283 let ckd1: i64 = vsp_fbck(fb, VG_W, VG_H)
284 let ckd2: i64 = vsp_fbck(fb2, VG_W, VG_H)
285 if ckd1 == ckd2 {
286 hw3("T6 GREEN deterministic: full scene composed twice, identical ck " as *u8); hn3(ckd1); hw3("\n" as *u8)
287 pass = pass + 1
288 } else {
289 hw3("T6 RED nondeterministic composition\n" as *u8)
290 }
291
292 // ---------- T7 anti-vacuity: scene vs bare background ----------
293 checks = checks + 1
294 fs_bg(fb2)
295 var ink: i64 = 0
296 var c1: i64 = 0 - 1
297 var c2: i64 = 0 - 1
298 var c3: i64 = 0 - 1
299 var c4: i64 = 0 - 1
300 var c5: i64 = 0 - 1
301 var ncol: i64 = 0
302 i = 0
303 while i < npx {
304 if fb[i] != fb2[i] {
305 ink = ink + 1
306 let v: i64 = fb[i]
307 var seen: i64 = 0
308 if v == c1 { seen = 1 }
309 if v == c2 { seen = 1 }
310 if v == c3 { seen = 1 }
311 if v == c4 { seen = 1 }
312 if v == c5 { seen = 1 }
313 if seen == 0 { if ncol < 5 {
314 if ncol == 0 { c1 = v }
315 if ncol == 1 { c2 = v }
316 if ncol == 2 { c3 = v }
317 if ncol == 3 { c4 = v }
318 if ncol == 4 { c5 = v }
319 ncol = ncol + 1
320 } }
321 }
322 i = i + 1
323 }
324 var t7ok: i64 = 0
325 if ink >= 500 { if ncol >= 5 { t7ok = 1 } }
326 if t7ok == 1 {
327 hw3("T7 GREEN anti-vacuity: scene differs from bare bg in " as *u8); hn3(ink)
328 hw3(" px with >=5 distinct sprite colors\n" as *u8)
329 pass = pass + 1
330 } else {
331 hw3("T7 RED vacuous scene: ink=" as *u8); hn3(ink); hw3(" colors=" as *u8); hn3(ncol); hw3("\n" as *u8)
332 }
333
334 // ---------- T8 scene artifact ----------
335 checks = checks + 1
336 write_png(fb, VG_W, VG_H, "knowledge/nx_vnsprite_scene.png" as *u8)
337 let lenp: *i64 = sys_mmap(8) as *i64
338 lenp[0] = 0
339 let pb: *u8 = sys_read_file("knowledge/nx_vnsprite_scene.png" as *u8, lenp)
340 var t8ok: i64 = 0
341 if (pb as i64) != 0 { if lenp[0] > 2000 { t8ok = 1 } }
342 if t8ok == 1 {
343 hw3("T8 GREEN scene artifact written: knowledge/nx_vnsprite_scene.png " as *u8); hn3(lenp[0])
344 hw3(" bytes (gradient bg + 2 characters + alpha textbox + name tag)\n" as *u8)
345 pass = pass + 1
346 } else {
347 hw3("T8 RED scene artifact missing or trivial: bytes=" as *u8); hn3(lenp[0]); hw3("\n" as *u8)
348 }
349
350 // ---------- T9 group exclusivity (the layeredimage bar) ----------
351 checks = checks + 1
352 // smile's middle-row ink sits at scene y=175+6, frown's at y=175+4 (mk_expr geometry), x=170
353 let inkc: i64 = 120 + 30*256 + 30*VG_B
354 fs_layers(lay)
355 let sh1: i64 = vsp_setg(lay, 5, 1, 2)
356 fs_scene(fb, sprites, sprw, sprh, lay, key)
357 let sA: i64 = fb[181*VG_W + 170]
358 let sB: i64 = fb[179*VG_W + 170]
359 let sh2: i64 = vsp_setg(lay, 5, 1, 3)
360 fs_scene(fb2, sprites, sprw, sprh, lay, key)
361 let fA: i64 = fb2[181*VG_W + 170]
362 let fB: i64 = fb2[179*VG_W + 170]
363 var t9ok: i64 = 0
364 if sh1 == 1 { if sh2 == 1 { if sA == inkc { if sB != inkc { if fB == inkc { if fA != inkc { t9ok = 1 } } } } } }
365 if t9ok == 1 {
366 hw3("T9 GREEN group-exclusive attributes: ONE vsp_setg call swaps smile<->frown; exactly one\n" as *u8)
367 hw3(" attribute of the group composes each time (pixel-probed both ways)\n" as *u8)
368 pass = pass + 1
369 } else {
370 hw3("T9 RED attribute group broken: shown=" as *u8); hn3(sh1); hn3(sh2)
371 hw3(" probes " as *u8); hn3(sA); hw3(" " as *u8); hn3(sB); hw3(" " as *u8); hn3(fA)
372 hw3(" " as *u8); hn3(fB); hw3("\n" as *u8)
373 }
374
375 // ---------- T10 dissolve exact ----------
376 checks = checks + 1
377 let fbd: *i64 = sys_mmap(npx*8) as *i64
378 vsp_dissolve(fb, fb2, fbd, npx, 0)
379 let ckda: i64 = vsp_fbck(fbd, VG_W, VG_H)
380 let ckfa: i64 = vsp_fbck(fb, VG_W, VG_H)
381 vsp_dissolve(fb, fb2, fbd, npx, 256)
382 let ckdb: i64 = vsp_fbck(fbd, VG_W, VG_H)
383 let ckfb: i64 = vsp_fbck(fb2, VG_W, VG_H)
384 vsp_dissolve(fb, fb2, fbd, npx, 128)
385 // probe the smile-ink pixel: A=ink(120,30,30) B=underlying skin/shirt -- expect the exact 50/50 mix
386 let a1: i64 = fb[181*VG_W + 170]
387 let b1: i64 = fb2[181*VG_W + 170]
388 let mr: i64 = ((a1 % 256) + (b1 % 256))/2
389 let mg: i64 = (((a1/256) % 256) + ((b1/256) % 256))/2
390 let mb2: i64 = (((a1/VG_B) % 256) + ((b1/VG_B) % 256))/2
391 let want: i64 = mr + mg*256 + mb2*VG_B
392 let got: i64 = fbd[181*VG_W + 170]
393 var t10ok: i64 = 0
394 if ckda == ckfa { if ckdb == ckfb { if got == want { t10ok = 1 } } }
395 if t10ok == 1 {
396 hw3("T10 GREEN dissolve exact: t=0 == frame A, t=256 == frame B (full-frame cks), t=128 = exact\n" as *u8)
397 hw3(" integer channel mix at the probed pixel\n" as *u8)
398 pass = pass + 1
399 } else {
400 hw3("T10 RED dissolve broken: endpoints " as *u8); hn3(ckda); hw3("/" as *u8); hn3(ckfa)
401 hw3(" " as *u8); hn3(ckdb); hw3("/" as *u8); hn3(ckfb)
402 hw3(" mid got " as *u8); hn3(got); hw3(" want " as *u8); hn3(want); hw3("\n" as *u8)
403 }
404
405 hw3("\n=== nx_vnsprite_gate " as *u8); hn3(pass); hw3("/" as *u8); hn3(checks)
406 if pass == checks { hw3(" verdict=GREEN ===\n" as *u8); return 0 }
407 hw3(" RED ===\n" as *u8)
408 return 1
409}