nx_decl_random_environment.nx source
↩ module page · 180 lines · 7280 B
1// nx_decl_random_environment.nx -- declarative environment generator.
2//
3// license_tier: ORIGINAL
4//
5// First substrate primitive in the "#tag" ergonomic family: one
6// substrate call produces a fully-specified procedural environment
7// from a single MODIFIER + SEED. Equivalent of TypeScript
8// `#random_environment real` -- the substrate fills in everything
9// (size, biome, river count, forest count, settlement, decoration
10// density) with sensible defaults seeded by the modifier.
11//
12// Mission cardinal: declarative > imperative for scene authoring.
13// Game authors write one line, substrate composes from
14// nx_biome_classifier + nx_forest_layout + nx_river_carve + sealed
15// enums. Per the user's S-class roadmap directive 2026-05-16.
16//
17// Usage:
18// let env: NxRandomEnvironment
19// nx_decl_random_environment(&env, NX_RE_MODIFIER_REAL, 0xC0FFEE)
20// // env is now fully populated.
21
22// nx_safety_envelope:
23// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
24// sil_target: SIL1
25// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
26// verdict: NOT_YET_EVALUATED
27
28import "nx_syscalls.nx"
29import "nx_types.nx"
30import "nx_tier.nx"
31import "nx_biome_classifier.nx"
32
33// ---- Sealed enum: modifier ----------------------------------------
34// Determines defaults across the whole environment.
35const NX_RE_MODIFIER_REAL: nx_int = 0 // Earth-like physics + biomes
36const NX_RE_MODIFIER_FANTASY: nx_int = 1 // exaggerated scale, magic biomes
37const NX_RE_MODIFIER_SCI_FI: nx_int = 2 // alien biomes, large scale
38const NX_RE_MODIFIER_N: nx_int = 3
39
40func nx_re_modifier_is_valid(m: nx_int) -> nx_int {
41 if m < 0 { return 0 }
42 if m >= NX_RE_MODIFIER_N { return 0 }
43 return 1
44}
45
46// Elm-style reflection: enum value -> human-readable name.
47// Lets debug output, log lines, and error messages quote the symbolic
48// name instead of the raw integer.
49func nx_re_modifier_name(m: nx_int) -> *u8 {
50 if m == NX_RE_MODIFIER_REAL { return "REAL" as *u8 }
51 if m == NX_RE_MODIFIER_FANTASY { return "FANTASY" as *u8 }
52 if m == NX_RE_MODIFIER_SCI_FI { return "SCI_FI" as *u8 }
53 return "<invalid modifier>" as *u8
54}
55
56// ---- Output struct -----------------------------------------------
57struct NxRandomEnvironment {
58 seed: nx_int,
59 modifier: nx_int,
60 size_tiles: nx_int, // square side in tiles
61 biome: nx_int, // NX_BIOME_*
62 river_count: nx_int,
63 forest_count: nx_int,
64 settlement_present: nx_int, // 0 / 1
65 decoration_density: nx_int, // Q10 (0..1024)
66 elevation_max_m: nx_int, // meters above sea level
67 is_valid: nx_int, // 1 = OK, 0 = inputs out-of-range
68}
69
70// ---- Deterministic hash helper -----------------------------------
71// Knuth multiplicative hash mod a small modulus. Pure function;
72// reproducible from (seed, salt) pair.
73func _re_hash_mod(seed: nx_int, salt: nx_int, modulus: nx_int) -> nx_int {
74 let mixed: nx_int = seed * 2654435761 + salt * 374761393
75 var v: nx_int = mixed % modulus
76 if v < 0 { v = 0 - v }
77 return v
78}
79
80// ---- Default tables per modifier ---------------------------------
81// Small per-modifier tables. Substrate-internal; non-exported.
82func _re_default_size(modifier: nx_int) -> nx_int {
83 if modifier == NX_RE_MODIFIER_REAL { return 256 }
84 if modifier == NX_RE_MODIFIER_FANTASY { return 512 }
85 if modifier == NX_RE_MODIFIER_SCI_FI { return 1024 }
86 return 256
87}
88
89func _re_default_river_count(modifier: nx_int, biome: nx_int) -> nx_int {
90 var base: nx_int = 2
91 if modifier == NX_RE_MODIFIER_FANTASY { base = 4 }
92 if modifier == NX_RE_MODIFIER_SCI_FI { base = 1 }
93 // Desert biomes have fewer rivers
94 if biome == NX_BIOME_DESERT { return 0 }
95 if biome == NX_BIOME_COLD_DESERT { return 0 }
96 if biome == NX_BIOME_ARCTIC_ICE { return 0 }
97 if biome == NX_BIOME_TROPICAL_RAINFOREST { return base + 3 }
98 if biome == NX_BIOME_TEMPERATE_RAINFOREST { return base + 2 }
99 return base
100}
101
102func _re_default_forest_count(modifier: nx_int, biome: nx_int) -> nx_int {
103 var base: nx_int = 1
104 if modifier == NX_RE_MODIFIER_FANTASY { base = 3 }
105 if modifier == NX_RE_MODIFIER_SCI_FI { base = 0 }
106 if biome == NX_BIOME_TAIGA { return base + 2 }
107 if biome == NX_BIOME_TEMPERATE_FOREST { return base + 3 }
108 if biome == NX_BIOME_TEMPERATE_RAINFOREST { return base + 4 }
109 if biome == NX_BIOME_TROPICAL_FOREST { return base + 4 }
110 if biome == NX_BIOME_TROPICAL_RAINFOREST { return base + 5 }
111 if biome == NX_BIOME_DESERT { return 0 }
112 if biome == NX_BIOME_COLD_DESERT { return 0 }
113 if biome == NX_BIOME_ARCTIC_ICE { return 0 }
114 return base
115}
116
117func _re_default_decoration_density(modifier: nx_int, biome: nx_int) -> nx_int {
118 let lush: nx_int = nx_biome_lushness_q14(biome)
119 // lush is Q14; renormalize to Q10
120 var q10: nx_int = lush / 16
121 if modifier == NX_RE_MODIFIER_FANTASY { q10 = q10 + 256 } // +0.25
122 if modifier == NX_RE_MODIFIER_SCI_FI { q10 = q10 - 256 }
123 if q10 < 0 { q10 = 0 }
124 if q10 > 1024 { q10 = 1024 }
125 return q10
126}
127
128func _re_default_elevation_max(modifier: nx_int, biome: nx_int) -> nx_int {
129 if biome == NX_BIOME_ALPINE { return 4500 }
130 if biome == NX_BIOME_ARCTIC_ICE { return 1500 }
131 if modifier == NX_RE_MODIFIER_FANTASY { return 6000 }
132 if modifier == NX_RE_MODIFIER_SCI_FI { return 8000 }
133 return 2500
134}
135
136// ---- Main declarator ---------------------------------------------
137//
138// Populates `out` from (modifier, seed). Pure deterministic; same
139// (modifier, seed) -> same struct. Sets is_valid=0 if modifier is
140// out-of-range.
141
142func nx_decl_random_environment(out: *NxRandomEnvironment, modifier: nx_int, seed: nx_int) -> nx_int {
143 if nx_re_modifier_is_valid(modifier) == 0 {
144 out.is_valid = 0
145 return 0
146 }
147 out.seed = seed
148 out.modifier = modifier
149
150 // Pick biome from seed (Knuth hash mod 13). Different salts so
151 // independent fields don't correlate.
152 out.biome = _re_hash_mod(seed, 1, NX_BIOME_COUNT)
153
154 out.size_tiles = _re_default_size(modifier)
155 out.river_count = _re_default_river_count(modifier, out.biome)
156 out.forest_count = _re_default_forest_count(modifier, out.biome)
157 out.decoration_density = _re_default_decoration_density(modifier, out.biome)
158 out.elevation_max_m = _re_default_elevation_max(modifier, out.biome)
159
160 // Settlement present iff a random roll succeeds and biome is
161 // habitable. Arctic / desert biomes get no settlement by
162 // default. Knuth mod 3: 0/3 = 33% / 33% / 33%.
163 let roll: nx_int = _re_hash_mod(seed, 2, 3)
164 var settle: nx_int = 0
165 if roll > 0 { settle = 1 }
166 if out.biome == NX_BIOME_ARCTIC_ICE { settle = 0 }
167 if out.biome == NX_BIOME_DESERT { settle = 0 }
168 if out.biome == NX_BIOME_COLD_DESERT { settle = 0 }
169 out.settlement_present = settle
170
171 out.is_valid = 1
172 return 1
173}
174
175// ---- Allocator helper --------------------------------------------
176func nx_re_alloc() -> *NxRandomEnvironment {
177 let raw: *u8 = sys_mmap(128)
178 let e: *NxRandomEnvironment = raw as *NxRandomEnvironment
179 return e
180}