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}