nx_visual_novel_test.nx source
↩ module page · 118 lines · 4674 B
1// nx_visual_novel_test.nx -- VN0 KAT: the engine's ops, branching, vars,
2// determinism, and chaos-user invariants, against the demo story whose pc
3// layout is FIXED (asserted here). Exit = failing assertion number;
4// 0 + marker = green.
5// license_tier: ORIGINAL
6import "nx_syscalls.nx"
7import "nx_visual_novel.nx"
8
9func vt_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
10
11func vt_fresh() -> *i64 { return nx_vn_init() }
12
13func main() -> i64 {
14 // 1: fresh state opens on the narrator SAY, not ended, vars zero
15 let s1: *i64 = vt_fresh()
16 if nx_vn_kind(s1) != VN_OP_SAY { return 1 }
17 if nx_vn_ended(s1) != 0 { return 1 }
18 if nx_vn_var(s1, VN_VAR_TEND) != 0 { return 1 }
19
20 // 2: advance walks SAY->SAY->CHOICE (silent ops invisible to the player)
21 if nx_vn_advance(s1) != 1 { return 2 }
22 if nx_vn_kind(s1) != VN_OP_SAY { return 2 }
23 if nx_vn_advance(s1) != 1 { return 2 }
24 if nx_vn_kind(s1) != VN_OP_CHOICE { return 2 }
25
26 // 3: advance on a CHOICE is a no-op (the player must choose)
27 let h3: i64 = nx_vn_state_hash(s1)
28 if nx_vn_advance(s1) != 0 { return 3 }
29 if nx_vn_state_hash(s1) != h3 { return 3 }
30
31 // 4: out-of-range choice is a no-op
32 if nx_vn_choose(s1, 5) != 0 { return 4 }
33 if nx_vn_choose(s1, 0 - 1) != 0 { return 4 }
34 if nx_vn_state_hash(s1) != h3 { return 4 }
35
36 // 5: choice 0 (tomatoes) lands on the KID say at pc 3 (tend still 0 --
37 // the ADD sits AFTER the say); advancing past it runs ADD+JUMP silently
38 // and lands on the afternoon say with tend=1
39 if nx_vn_choose(s1, 0) != 1 { return 5 }
40 if nx_vn_kind(s1) != VN_OP_SAY { return 5 }
41 if nx_vn_var(s1, VN_VAR_TEND) != 0 { return 5 }
42 if nx_vn_advance(s1) != 1 { return 5 }
43 if nx_vn_kind(s1) != VN_OP_SAY { return 5 }
44 if nx_vn_var(s1, VN_VAR_TEND) != 1 { return 5 }
45
46 // 6: the two branches DIVERGE (different hashes after each choice)
47 let sa: *i64 = vt_fresh()
48 nx_vn_advance(sa)
49 nx_vn_advance(sa)
50 nx_vn_choose(sa, 0)
51 let sb: *i64 = vt_fresh()
52 nx_vn_advance(sb)
53 nx_vn_advance(sb)
54 nx_vn_choose(sb, 1)
55 if nx_vn_state_hash(sa) == nx_vn_state_hash(sb) { return 6 }
56
57 // 7: BLOOM ending -- tend twice (plant + water) -> tend=2 -> happy end
58 let s7: *i64 = vt_fresh()
59 nx_vn_advance(s7)
60 nx_vn_advance(s7)
61 nx_vn_choose(s7, 0) // tomatoes (tend=1)
62 nx_vn_advance(s7) // jump-chain to the afternoon say (pc 9)
63 nx_vn_advance(s7) // -> water choice (pc 10)
64 if nx_vn_kind(s7) != VN_OP_CHOICE { return 7 }
65 nx_vn_choose(s7, 0) // water (tend=2) -> mom say
66 nx_vn_advance(s7) // -> ifgoto -> pc 19 kid say
67 if nx_vn_kind(s7) != VN_OP_SAY { return 7 }
68 nx_vn_advance(s7) // -> END (bloom)
69 if nx_vn_kind(s7) != VN_OP_END { return 7 }
70 if nx_vn_ended(s7) != 1 { return 7 }
71 if nx_vn_var(s7, VN_VAR_TEND) != 2 { return 7 }
72
73 // 8: SPROUT ending -- skip watering -> tend=1 -> modest end at pc 17
74 let s8: *i64 = vt_fresh()
75 nx_vn_advance(s8)
76 nx_vn_advance(s8)
77 nx_vn_choose(s8, 1) // sunflowers (tend=1)
78 nx_vn_advance(s8)
79 nx_vn_advance(s8)
80 nx_vn_choose(s8, 1) // run off and play
81 nx_vn_advance(s8) // narrator dry-ground say -> ifgoto fails -> END at 17
82 if nx_vn_kind(s8) != VN_OP_END { return 8 }
83 if nx_vn_var(s8, VN_VAR_TEND) != 1 { return 8 }
84
85 // 9: the two ENDINGS differ (different epilogue pointers + hashes)
86 if nx_vn_op_field(s7, 1) == nx_vn_op_field(s8, 1) { return 9 }
87 if nx_vn_state_hash(s7) == nx_vn_state_hash(s8) { return 9 }
88
89 // 10: ended story ignores advance + choose (post-game chaos clicks)
90 let h10: i64 = nx_vn_state_hash(s7)
91 if nx_vn_advance(s7) != 0 { return 10 }
92 if nx_vn_choose(s7, 0) != 0 { return 10 }
93 if nx_vn_state_hash(s7) != h10 { return 10 }
94
95 // 11: determinism -- same path twice = identical hash
96 let r1: *i64 = vt_fresh()
97 nx_vn_advance(r1)
98 nx_vn_advance(r1)
99 nx_vn_choose(r1, 0)
100 let r2: *i64 = vt_fresh()
101 nx_vn_advance(r2)
102 nx_vn_advance(r2)
103 nx_vn_choose(r2, 0)
104 if nx_vn_state_hash(r1) != nx_vn_state_hash(r2) { return 11 }
105
106 // 12: action API (the browser entry surface) -- restart resets pristine
107 let s12: *i64 = vt_fresh()
108 let h_fresh: i64 = nx_vn_state_hash(s12)
109 nx_vn_action(s12, 1, 0, 0, 0, 0)
110 nx_vn_action(s12, 1, 0, 0, 0, 0)
111 nx_vn_action(s12, 2, 0, 0, 0, 0)
112 if nx_vn_state_hash(s12) == h_fresh { return 12 }
113 nx_vn_action(s12, 3, 0, 0, 0, 0)
114 if nx_vn_state_hash(s12) != h_fresh { return 12 }
115
116 vt_puts("VN-KAT ALL-PASS\n" as *u8)
117 return 0
118}