code wiki / _hdl_build / nx_char_ai_gate.nx

nx_char_ai_gate.nx source

↩ module page · 82 lines · 4775 B

1// nx_char_ai_gate.nx -- proves G-R2 char-AI (behavior tree + FSM). NPC BT (priority selector): 2// SEL[ SEQ[COND health_low, ACT flee], SEQ[COND enemy_visible, ACT attack], ACT patrol ] 3// blackboard bits: 0=health_low, 1=enemy_visible. actions: flee=1, attack=2, patrol=3. 4// FSM: IDLE--see-->ATTACK--health_low-->FLEE--healed-->IDLE (no other transitions). GREEN only if every 5// decision + transition is correct, incl. priority and neg-controls. license_tier: ORIGINAL expect_exit: 0 6// 2026-08-28 D001 MIGRATION: the hand-rolled pass/total counter and the sys_exit tail are replaced by 7// gv_ctr/gv_check/gv_verdict, so /api/promote will accept it and /api/gate_run can READ the verdict from 8// the exit code instead of a printed string nothing parses. Every tooth now PRINTS THE VALUE IT JUDGED -- 9// the incumbent printed a value only on failure, so a passing run said nothing about what it saw. 10import "nx_syscalls.nx" 11import "nx_char_ai.nx" 12import "nx_gate_verdict.nx" 13 14func cw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 15func cn(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)} let t: *u8=sys_mmap(28); 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; while i<k{bb[i]=t[k-1-i];i=i+1} sys_write(1,bb,k); return 0 } 16 17func bt_action(ty: *i64, a0: *i64, nk: *i64, kids: *i64, bb: i64) -> i64 { 18 let ao: *i64 = sys_mmap(8) as *i64 19 ao[0] = -1 20 bt_tick(ty, a0, nk, kids, 0, bb, ao) 21 return ao[0] 22} 23 24// one place that turns "the observed value" into a tooth condition, so the value is always PRINTED 25// beside the verdict and a tooth can never assert on a number the reader cannot see. 26func cai_is(label: *u8, got: i64, want: i64) -> i64 { 27 cw(" " as *u8); cw(label); cw(" got=" as *u8); cn(got); cw(" want=" as *u8); cn(want); cw("\n" as *u8) 28 if got == want { return 1 } 29 return 0 30} 31 32func main() -> i64 { 33 // ---- behavior tree (8 nodes) ---- 34 let ty: *i64 = sys_mmap(8 * 8) as *i64 35 let a0: *i64 = sys_mmap(8 * 8) as *i64 36 let nk: *i64 = sys_mmap(8 * 8) as *i64 37 let kids: *i64 = sys_mmap(8 * 32) as *i64 38 var i: i64 = 0 39 while i < 32 { kids[i] = -1; i = i + 1 } 40 // node 0 SEL(flee-seq, attack-seq, patrol); 1 SEQ(cond hp, act flee); 2 COND hp; 3 ACT flee; 41 // 4 SEQ(cond vis, act atk); 5 COND vis; 6 ACT atk; 7 ACT patrol 42 ty[0]=NT_SEL; nk[0]=3; kids[0]=1; kids[1]=4; kids[2]=7 43 ty[1]=NT_SEQ; nk[1]=2; kids[4]=2; kids[5]=3 44 ty[2]=NT_COND; a0[2]=0 45 ty[3]=NT_ACT; a0[3]=1 46 ty[4]=NT_SEQ; nk[4]=2; kids[16]=5; kids[17]=6 47 ty[5]=NT_COND; a0[5]=1 48 ty[6]=NT_ACT; a0[6]=2 49 ty[7]=NT_ACT; a0[7]=3 50 51 let ctr: *i64 = gv_ctr() 52 53 let bt1: i64 = bt_action(ty,a0,nk,kids, 1) 54 gv_check("BT1-health_low-alone-selects-FLEE" as *u8, cai_is("bt[health_low]" as *u8, bt1, 1), ctr) 55 let bt2: i64 = bt_action(ty,a0,nk,kids, 2) 56 gv_check("BT2-enemy_visible-alone-selects-ATTACK" as *u8, cai_is("bt[enemy_visible]" as *u8, bt2, 2), ctr) 57 let bt3: i64 = bt_action(ty,a0,nk,kids, 3) 58 gv_check("BT3-PRIORITY-both-bits-set-still-selects-FLEE-not-ATTACK" as *u8, cai_is("bt[both]" as *u8, bt3, 1), ctr) 59 let bt4: i64 = bt_action(ty,a0,nk,kids, 0) 60 gv_check("BT4-neg-control-no-bit-set-falls-through-to-PATROL" as *u8, cai_is("bt[none]" as *u8, bt4, 3), ctr) 61 62 // ---- FSM (3 states x 4 events) ---- 63 // states IDLE=0 ATTACK=1 FLEE=2 ; events SEE=0 LOSE=1 HPLOW=2 HEALED=3 64 let trans: *i64 = sys_mmap(8 * 12) as *i64 65 var j: i64 = 0 66 while j < 12 { trans[j] = -1; j = j + 1 } 67 trans[0*4 + 0] = 1 // IDLE + SEE -> ATTACK 68 trans[1*4 + 1] = 0 // ATTACK + LOSE -> IDLE 69 trans[1*4 + 2] = 2 // ATTACK + HPLOW -> FLEE 70 trans[2*4 + 3] = 0 // FLEE + HEALED -> IDLE 71 72 let f1: i64 = fsm_step(trans,4, 0,0) 73 gv_check("FSM1-IDLE-plus-SEE-goes-to-ATTACK" as *u8, cai_is("fsm[idle,see]" as *u8, f1, 1), ctr) 74 let f2: i64 = fsm_step(trans,4, 1,2) 75 gv_check("FSM2-ATTACK-plus-HPLOW-goes-to-FLEE" as *u8, cai_is("fsm[attack,hplow]" as *u8, f2, 2), ctr) 76 let f3: i64 = fsm_step(trans,4, 2,3) 77 gv_check("FSM3-FLEE-plus-HEALED-returns-to-IDLE" as *u8, cai_is("fsm[flee,healed]" as *u8, f3, 0), ctr) 78 let f4: i64 = fsm_step(trans,4, 0,1) 79 gv_check("FSM4-neg-control-IDLE-plus-LOSE-is-an-UNDECLARED-transition-and-must-STAY-idle" as *u8, cai_is("fsm[idle,lose]" as *u8, f4, 0), ctr) 80 81 return gv_verdict("CHAR-AI" as *u8, ctr, "behaviour tree AND finite-state machine on one subject: BT3 is the discriminating tooth because a selector that merely returns the first matching branch in declaration order passes BT1/BT2 and fails it, and FSM4 refuses an undeclared transition rather than inventing one" as *u8) 82}