code wiki / _hdl_build / nx_vn_living.nx
nx_vn_living.nx source
↩ module page · 55 lines · 3608 B
1// nx_vn_living.nx -- the "LIVING but it still TRACKS" engine for the VN (operator: interactions can be LLM-run IN
2// LIMITS so they feel living, but the story stays on its rails). The hard part is the LIMITS, and they must hold
3// for ANY interaction provider -- a deterministic template now, a sovereign no-float LLM later. Design:
4// - a scene carries CANON: a required FACT (must appear) + a FORBIDDEN string (must never appear) + an authored
5// fallback line that is itself on-canon.
6// - a PROVIDER generates a living interaction line (varied). Could be an LLM.
7// - the VALIDATOR (vl_ok) is THE LIMIT: it accepts the line ONLY if it keeps canon (has fact, lacks forbidden).
8// - vl_interact runs the provider, validates, and FALLS BACK to the authored canon line if it derails -> so the
9// final line is ALWAYS on-canon. An unbounded/hallucinating provider can vary the flavour but CANNOT break
10// the plot. That is "living interactions, story still tracks", made mechanical + provider-agnostic. license_tier: ORIGINAL
11import "nx_syscalls.nx"
12
13func vl_has(hay: *u8, needle: *u8) -> i64 {
14 if needle[0] == (0 as u8) { return 1 }
15 var i: i64 = 0
16 while hay[i] != (0 as u8) {
17 var j: i64 = 0; var ok: i64 = 1
18 while needle[j] != (0 as u8) { if hay[i+j] != needle[j] { ok = 0; j = 0; while needle[j] != (0 as u8) { j = j + 1 } } else { j = j + 1 } }
19 if ok == 1 { return 1 }
20 i = i + 1
21 }
22 return 0
23}
24func vl_cpy(out: *u8, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { out[i] = s[i]; i = i + 1 } out[i] = 0 as u8; return i }
25
26// THE LIMIT: a line is acceptable iff it carries the canon fact and never the forbidden string.
27func vl_ok(line: *u8, fact: *u8, forbidden: *u8) -> i64 {
28 if vl_has(line, fact) == 0 { return 0 }
29 if forbidden[0] != (0 as u8) { if vl_has(line, forbidden) == 1 { return 0 } }
30 return 1
31}
32
33// GOOD provider: living variation that stays on canon (every template embeds the fact "Mara", none derail).
34func vl_gen_good(seed: i64, out: *u8) -> i64 {
35 let k: i64 = seed % 4
36 if k == 0 { return vl_cpy(out, "Mara sets the cold lamp down. 'You came a long way,' she says." as *u8) }
37 if k == 1 { return vl_cpy(out, "'I'm Mara,' the keeper murmurs, eyes heavy with sleepless tides." as *u8) }
38 if k == 2 { return vl_cpy(out, "Mara studies you a moment. 'I knew someone would answer the letter.'" as *u8) }
39 return vl_cpy(out, "The keeper -- Mara -- manages a tired smile. 'Welcome to the light.'" as *u8)
40}
41// ROGUE provider: simulates an UNBOUNDED/hallucinating LLM that derails -- omits the fact or injects the forbidden.
42func vl_gen_rogue(seed: i64, out: *u8) -> i64 {
43 if (seed % 2) == 0 { return vl_cpy(out, "The keeper says nothing; a nameless stranger in the dark." as *u8) } // omits "Mara"
44 return vl_cpy(out, "Mara cackles -- she was a ghost all along, and the lighthouse a lie!" as *u8) // injects forbidden "ghost"
45}
46
47// run a provider for one interaction, ENFORCE the limit, fall back to the authored canon line on derail.
48// kind: 1=good 2=rogue. writes the FINAL (always on-canon) line to out. accbox[0]=1 if provider line accepted, 0 if fell back.
49func vl_interact(seed: i64, kind: i64, fact: *u8, forbidden: *u8, authored: *u8, out: *u8, accbox: *i64) -> i64 {
50 let gen: *u8 = sys_mmap(512)
51 if kind == 2 { vl_gen_rogue(seed, gen) } else { vl_gen_good(seed, gen) }
52 if vl_ok(gen, fact, forbidden) == 1 { accbox[0] = 1; return vl_cpy(out, gen) }
53 accbox[0] = 0
54 return vl_cpy(out, authored) // story tracks: the authored canon line
55}