code wiki / _hdl_build / nx_vn_critic.nx
nx_vn_critic.nx source
↩ module page · 103 lines · 6276 B
1// nx_vn_critic.nx -- the GAME-QUALITY CRITIC bot (the operator's vision: not a bot that BEATS the game, a bot that
2// OPTIMIZES the game). It reads a VN as DATA and judges it like a reviewer of highly-rated visual novels,
3// emitting ACTIONABLE design feedback + a score + an HONEST verdict that can say NEEDS-WORK:
4// FUNCTIONALITY (must-pass): every scene reachable from the start, no broken choice links, >=2 endings.
5// FUN / PACING (graded, grounded in VN norms): scene length ("~N words ~= Ns to read before you can act --
6// too long for a human to enjoy" past a threshold), ILLUSORY choices (options that all go to the same scene
7// = the choice doesn't matter), branching/replay value (#endings), interactivity.
8// It gates EVERY engine-owned VN (vn_sample + vn_sample2) so the whole published catalog is quality-verified,
9// and optionally EMITS a VN's playable HTML. Build->CRITIQUE->fix->re-critique. license_tier: ORIGINAL expect_exit: 0
10import "nx_vn.nx"
11import "nx_syscalls.nx"
12const MAX_MAGIC_131072: i64 = 131072
13
14const MAX_SCENE_WORDS: i64 = 120 // past this, the human is reading too long before the next beat/choice
15const SHIP_SCORE: i64 = 80
16
17func w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
18func wn(v: i64) -> i64 { var m: i64=v; if m<0{w("-" as *u8);m=0-m} let t:*u8=sys_mmap(24); var k:i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i:i64=0; let o:*u8=sys_mmap(24); while i<k{o[i]=t[k-1-i];i=i+1} sys_write(1,o,k); return 0 }
19
20// critique ONE populated VN. emit_path (0 = skip emit) writes the playable zero-JS HTML. Returns 1 if SHIP-READY.
21func critique(title: *u8, txt: *i64, nc: *i64, ct: *i64, cl: *i64, n: i64, emit_path: *u8) -> i64 {
22 w("\n--- critiquing: " as *u8); w(title); w(" ("); wn(n); w(" scenes) ---\n" as *u8)
23 if (emit_path as i64) != 0 {
24 let buf: *u8 = sys_mmap(MAX_MAGIC_131072)
25 let blen: i64 = vn_emit_html(txt, nc, ct, cl, n, title, buf)
26 sys_mkdir("knowledge/staging/game" as *u8, 0x1ed)
27 let fd: i64 = sys_openat_wr(emit_path, 420); if fd >= 0 { sys_write(fd, buf, blen); sys_close(fd) }
28 w(" emitted playable VN -> " as *u8); w(emit_path); w(" ("); wn(blen); w(" bytes, zero-JS)\n" as *u8)
29 }
30 // --- reachability BFS from scene 0 ---
31 let seen: *i64 = sys_mmap(8*VN_NS) as *i64; var i: i64 = 0; while i < n { seen[i] = 0; i = i + 1 }
32 let q: *i64 = sys_mmap(8*VN_NS) as *i64; var qh: i64 = 0; var qt: i64 = 0
33 seen[0] = 1; q[qt] = 0; qt = qt + 1
34 var broken: i64 = 0
35 while qh < qt {
36 let c: i64 = q[qh]; qh = qh + 1
37 var j: i64 = 0
38 while j < nc[c] {
39 let tg: i64 = ct[c*VN_MC + j]
40 if tg < 0 { broken = broken + 1 } else { if tg >= n { broken = broken + 1 } else { if seen[tg] == 0 { seen[tg] = 1; q[qt] = tg; qt = qt + 1 } } }
41 j = j + 1
42 }
43 }
44 var reach: i64 = 0; var endings: i64 = 0
45 i = 0; while i < n { if seen[i] == 1 { reach = reach + 1 } if nc[i] == 0 { endings = endings + 1 } i = i + 1 }
46
47 var score: i64 = 100
48 w(" FUNCTIONALITY:\n" as *u8)
49 if broken > 0 { w(" [FAIL] "); wn(broken); w(" broken choice link(s)\n" as *u8); score = score - 100 } else { w(" [ok] no broken choice links\n" as *u8) }
50 let orphan: i64 = n - reach
51 if orphan > 0 { w(" [FAIL] "); wn(orphan); w(" unreachable scene(s)\n" as *u8); score = score - 30*orphan } else { w(" [ok] all "); wn(n); w(" scenes reachable\n" as *u8) }
52 if endings < 2 { w(" [FAIL] only "); wn(endings); w(" ending(s)\n" as *u8); score = score - 20 } else { w(" [ok] "); wn(endings); w(" distinct endings\n" as *u8) }
53
54 w(" FUN / PACING (vs highly-rated VN norms):\n" as *u8)
55 var illus: i64 = 0
56 i = 0
57 while i < n {
58 if nc[i] >= 2 {
59 var allsame: i64 = 1; var j: i64 = 1
60 while j < nc[i] { if ct[i*VN_MC + j] != ct[i*VN_MC] { allsame = 0 } j = j + 1 }
61 if allsame == 1 { illus = illus + 1; w(" [FIX] scene "); wn(i); w(": ILLUSORY choice -- all options go to the same scene\n" as *u8); score = score - 25 }
62 }
63 i = i + 1
64 }
65 if illus == 0 { w(" [ok] no illusory choices\n" as *u8) }
66 var toolong: i64 = 0
67 i = 0
68 while i < n {
69 let wc: i64 = vn_words(txt[i] as *u8)
70 if wc > MAX_SCENE_WORDS { toolong = toolong + 1; w(" [FIX] scene "); wn(i); w(": ~"); wn(wc); w(" words -- too long before the next beat (norm < "); wn(MAX_SCENE_WORDS); w(")\n" as *u8); score = score - 15 }
71 i = i + 1
72 }
73 if toolong == 0 { w(" [ok] no over-long scenes\n" as *u8) }
74
75 if score < 0 { score = 0 }
76 w(" QUALITY SCORE = "); wn(score); w("/100 -> " as *u8)
77 var ship: i64 = 1
78 if broken > 0 { ship = 0 } if orphan > 0 { ship = 0 } if endings < 2 { ship = 0 } if score < SHIP_SCORE { ship = 0 }
79 if ship == 1 { w("SHIP-READY\n" as *u8) } else { w("NEEDS-WORK (HONEST: fix the [FIX]/[FAIL] notes, re-critique)\n" as *u8) }
80 return ship
81}
82
83// populate + critique vn_sample (id 0) or vn_sample2 (id 1). Returns ship.
84func gate_vn(id: i64, title: *u8, emit_path: *u8) -> i64 {
85 let txt: *i64 = sys_mmap(8*VN_NS) as *i64; let nc: *i64 = sys_mmap(8*VN_NS) as *i64
86 let ct: *i64 = sys_mmap(8*VN_NS*VN_MC) as *i64; let cl: *i64 = sys_mmap(8*VN_NS*VN_MC) as *i64
87 let nbox: *i64 = sys_mmap(16) as *i64; nbox[0] = 0
88 if id == 0 { vn_sample(txt, nc, ct, cl, nbox) } else { vn_sample2(txt, nc, ct, cl, nbox) }
89 return critique(title, txt, nc, ct, cl, nbox[0], emit_path)
90}
91
92func main(argc: i64, argv: *i64) -> i64 {
93 w("=== nx_vn_critic -- GAME-QUALITY critic, gating the WHOLE engine VN catalog ===\n" as *u8)
94 let ship1: i64 = gate_vn(0, "The Lighthouse Keeper" as *u8, "knowledge/staging/game/vn.html" as *u8)
95 let ship2: i64 = gate_vn(1, "The Cartographer's Vow" as *u8, 0 as *u8)
96 w("\n=== CATALOG VERDICT ===\n" as *u8)
97 if ship1 == 1 { if ship2 == 1 {
98 w("ALL VNs SHIP-READY (both functional + paced like highly-rated VNs; the published catalog is quality-gated)\n" as *u8)
99 sys_exit(0); return 0
100 } }
101 w("CATALOG NOT SHIPPABLE -- at least one VN NEEDS-WORK above. HONEST.\n" as *u8)
102 sys_exit(1); return 1
103}