code wiki / (root) / nx_decl_random_environment.nx

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}