code wiki / _hdl_build / nx_game_gen.nx
nx_game_gen.nx source
↩ module page · 133 lines · 5827 B
1// nx_game_gen.nx -- the GENERATIVE game front-end ("mythos level"): a high-level INTENT
2// (theme + difficulty) in, a complete game-SPEC out. This is the top of the Nishi game
3// ecosystem: you author at the description level; nx_game_gen writes the data game-spec,
4// nx_game_emit turns the spec into a per-game module, nx_game_engine_lib runs it native.
5// So the whole chain is: describe -> spec -> playable game, hands-off.
6//
7// usage: nx_game_gen <name> <THEME> <difficulty>
8// name module name, e.g. _game_forest
9// THEME one word, becomes the title "THE <THEME>"
10// difficulty easy | normal | hard | nightmare (scales walls/foes/hp/dmg/loot;
11// hard+ use a NO-FLEE combat FSM = scarier)
12//
13// Writes knowledge/specs/<name>.spec. HONEST: this front-end is TUTOR tooling; the GAME
14// it leads to is emitter output. Difficulty->params is a deterministic design table (no
15// magic numbers buried in logic -- the tiers ARE the data). license_tier: ORIGINAL
16import "nx_syscalls.nx"
17import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
18
19func p_w(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 }
20func p_c(fd: i64, c: i64) -> i64 { let b: *u8 = sys_mmap(8); b[0] = c as u8; sys_write(fd, b, 1); return 0 }
21// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
22// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
23// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
24// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
25func p_wn(fd: i64, v: i64) -> i64 { nxi_fd(fd, v); return 0 }
26func streq(a: *u8, b: *u8) -> i64 {
27 var i: i64 = 0
28 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
29 if b[i] != (0 as u8) { return 0 }
30 return 1
31}
32func sp_kv(fd: i64, key: *u8, val: i64) -> i64 { p_w(fd, key); p_c(fd, 32); p_wn(fd, val); p_c(fd, 10); return 0 }
33
34// difficulty word -> tier 0..3 (default normal=1)
35func tier_of(d: *u8) -> i64 {
36 if streq(d, "easy" as *u8) == 1 { return 0 }
37 if streq(d, "normal" as *u8) == 1 { return 1 }
38 if streq(d, "hard" as *u8) == 1 { return 2 }
39 if streq(d, "nightmare" as *u8) == 1 { return 3 }
40 return 1
41}
42
43func build_spec_path(name: *u8, out: *u8) -> i64 {
44 let pre: *u8 = "knowledge/specs/" as *u8
45 var w: i64 = 0
46 var i: i64 = 0
47 while pre[i] != (0 as u8) { out[w] = pre[i]; w = w + 1; i = i + 1 }
48 i = 0
49 while name[i] != (0 as u8) { out[w] = name[i]; w = w + 1; i = i + 1 }
50 let suf: *u8 = ".spec" as *u8
51 i = 0
52 while suf[i] != (0 as u8) { out[w] = suf[i]; w = w + 1; i = i + 1 }
53 out[w] = 0 as u8
54 return w
55}
56
57func write_fsm(fd: i64, noflee: i64) -> i64 {
58 // dungenc convention table; flee transition (state1,event4 -> 4) removed when noflee
59 let t: *i64 = sys_mmap(512) as *i64
60 var i: i64 = 0
61 while i < 36 { t[i] = 0 - 1; i = i + 1 }
62 t[0 * 6 + 0] = 1 // START --enter--> PLAYER_TURN
63 t[1 * 6 + 1] = 2 // PLAYER --attack--> ENEMY_TURN
64 t[1 * 6 + 3] = 3 // PLAYER --defeat--> WIN
65 t[1 * 6 + 4] = 4 // PLAYER --flee--> FLEE
66 t[2 * 6 + 2] = 1 // ENEMY --enemy_act--> PLAYER_TURN
67 t[3 * 6 + 5] = 5 // WIN --resolve--> END
68 t[4 * 6 + 5] = 5 // FLEE --resolve--> END
69 if noflee == 1 { t[1 * 6 + 4] = 0 - 1 }
70 p_w(fd, "fsm 6 6\n" as *u8)
71 var r: i64 = 0
72 while r < 6 {
73 p_w(fd, "row" as *u8)
74 var c: i64 = 0
75 while c < 6 { p_c(fd, 32); p_wn(fd, t[r * 6 + c]); c = c + 1 }
76 p_c(fd, 10)
77 r = r + 1
78 }
79 return 0
80}
81
82func main(argc: i64, argv: *i64) -> i64 {
83 if argc < 4 { p_w(1, "usage: nx_game_gen <name> <THEME> <difficulty>\n" as *u8); sys_exit(2); return 2 }
84 let name: *u8 = argv[1] as *u8
85 let theme: *u8 = argv[2] as *u8
86 let diff: *u8 = argv[3] as *u8
87 let tier: i64 = tier_of(diff)
88
89 // deterministic difficulty design table (the tiers ARE the data)
90 let walls: i64 = 16 + tier * 6
91 let eb: i64 = 2 + tier
92 let epl: i64 = 1
93 let hb: i64 = 14 + tier * 6
94 let hpl: i64 = 3 + tier
95 var loot: i64 = 4 - tier
96 if loot < 1 { loot = 1 }
97 var db: i64 = 10 - tier
98 if db < 6 { db = 6 }
99 let dr: i64 = 8
100 var noflee: i64 = 0
101 if tier >= 2 { noflee = 1 }
102
103 let path: *u8 = sys_mmap(256)
104 build_spec_path(name, path)
105 let fd: i64 = sys_openat_wr(path, 0x1a4)
106 if fd < 0 { p_w(1, "GAMEGEN REFUSED: cannot open spec path\n" as *u8); sys_exit(1); return 1 }
107
108 p_w(fd, "# generated by nx_game_gen (intent -> spec): theme=" as *u8); p_w(fd, theme)
109 p_w(fd, " difficulty=" as *u8); p_w(fd, diff); p_w(fd, " tier=" as *u8); p_wn(fd, tier); p_c(fd, 10)
110 p_w(fd, "name " as *u8); p_w(fd, name); p_c(fd, 10)
111 p_w(fd, "title THE " as *u8); p_w(fd, theme); p_c(fd, 10)
112 sp_kv(fd, "walls" as *u8, walls)
113 sp_kv(fd, "enemy_base" as *u8, eb)
114 sp_kv(fd, "enemy_per_level" as *u8, epl)
115 sp_kv(fd, "ehp_base" as *u8, hb)
116 sp_kv(fd, "ehp_per_level" as *u8, hpl)
117 sp_kv(fd, "loot" as *u8, loot)
118 sp_kv(fd, "dmg_base" as *u8, db)
119 sp_kv(fd, "dmg_rand" as *u8, dr)
120 p_w(fd, "needs save\n" as *u8)
121 write_fsm(fd, noflee)
122 sys_close(fd)
123
124 p_w(1, "GAMEGEN GREEN name=" as *u8); p_w(1, name)
125 p_w(1, " title=THE " as *u8); p_w(1, theme)
126 p_w(1, " difficulty=" as *u8); p_w(1, diff)
127 p_w(1, " (walls=" as *u8); p_wn(1, walls); p_w(1, " foes=" as *u8); p_wn(1, eb)
128 p_w(1, " ehp=" as *u8); p_wn(1, hb); p_w(1, " noflee=" as *u8); p_wn(1, noflee); p_w(1, ")\n" as *u8)
129 p_w(1, " -> " as *u8); p_w(1, path); p_c(1, 10)
130 p_w(1, " next: nx_game_emit " as *u8); p_w(1, path); p_c(1, 10)
131 sys_exit(0)
132 return 0
133}