code wiki / _hdl_build / nx_breeding.nx
nx_breeding.nx source
↩ module page · 162 lines · 6762 B
1// nx_breeding.nx -- certified composable game part: GENETICS / BREEDING (RENAMED from nx_genome.nx 2026-07-26:
2// the basename collided with runtime/nx_genome.nx, an unrelated CREATURE body-plan genome. Two different
3// capabilities, one filename -- whichever a build resolved depended on the include path. Both names were
4// right in their own domain, so the collision was the defect, not either name.) (gamebench capability 30, the
5// Breeders-of-the-Nephelym keystone; program plan Lane B1, board row F1066).
6//
7// A GENOME is a bounded integer trait vector. BREEDING is seeded, deterministic, per-trait
8// inheritance (pick a parent, apply a bounded mutation, clamp to the trait's legal range).
9// This is the axis where a GENERATIVE emitter beats a hand-authored roster BY CONSTRUCTION:
10// a roster is finite; a genome space this size is not (vsbench carries that as HYPOTHESIS until a
11// head-to-head cell measures it -- this part is what makes the hypothesis testable).
12//
13// THE CONTRACT (each clause is a gate tooth):
14// - DETERMINISTIC: same (parents, seed) -> bit-identical child. Replay/lockstep-safe.
15// - BOUNDED HERITABILITY: every continuous trait of a child lies within the parents' span
16// widened by at most the trait's mutation step. Inheritance, not reroll.
17// - UNIQUE OFFSPRING: different seeds -> distinct children (the loottable 0-dup law applied to
18// creatures -- every bred creature is genuinely YOURS, the ownership pillar).
19// - ADULT INVARIANT FAIL-CLOSED: breeding involving a parent below the adult age REFUSES
20// (GN_E_MINOR) with the output untouched, and every emitted child carries age = GN_ADULT_AGE
21// by construction (the nx_card_compile / nx_wardrobe_state law, inherited into the bloodline).
22// - MORPH = the CoC transformation mechanism: a bounded delta to ONE trait, every other word
23// bit-identical -- transformation is a genome edit re-emitted, not a second asset set.
24// - Serializable span rides nx_gamesave like every certified part.
25// LIB, no main (ecosystem convention). license_tier: ORIGINAL No hw writes (Rule 26).
26import "nx_syscalls.nx"
27
28// ---- trait ids ----
29const GN_T_SPECIES: i64 = 0 // 0..7 humanoid morph archetypes (matches WS_NMORPH)
30const GN_T_SEX: i64 = 1 // 0/1
31const GN_T_BUILD: i64 = 2 // 0..1000 body-proc knob space
32const GN_T_MUSC: i64 = 3
33const GN_T_FAT: i64 = 4
34const GN_T_STYLIZE: i64 = 5
35const GN_T_HUE: i64 = 6
36const GN_T_VIGOR: i64 = 7 // 0..31 IVs (companion lineage)
37const GN_T_INSTINCT: i64 = 8
38const GN_T_TEMPER: i64 = 9
39const GN_T_FERT: i64 = 10
40const GN_T_RARITY: i64 = 11
41const GN_NT: i64 = 12
42// ---- vector layout (i64 words, ALL serializable) ----
43const GN_F_AGE: i64 = 0
44const GN_O_TRAIT: i64 = 1 // [1..13)
45const GN_F_LINE: i64 = 13 // lineage hash
46const GN_LEN: i64 = 16
47const GN_ADULT_AGE: i64 = 18
48const GN_SCHEMA: i64 = 1066 // F1066 lineage
49// ---- errors (output untouched on refusal) ----
50const GN_E_MINOR: i64 = 0-1
51const GN_E_TRAIT: i64 = 0-2
52const GN_E_RANGE: i64 = 0-3
53const GN_CK_SEED: i64 = 2166136261
54const GN_CK_PRIME: i64 = 16777619
55const GN_CK_MASK: i64 = 4611686018427387903
56const GN_SPECIES_JUMP: i64 = 37 // 1-in-37 breedings shift species one step (rare, bounded)
57
58func gn_mix(c0: i64, v: i64) -> i64 {
59 var c: i64 = c0
60 c = (c + v + 1) % GN_CK_MASK
61 c = (c * GN_CK_PRIME) % GN_CK_MASK
62 return c
63}
64func gn_hash3(a: i64, b: i64, c: i64) -> i64 {
65 var h: i64 = GN_CK_SEED
66 h = gn_mix(h, a)
67 h = gn_mix(h, b)
68 h = gn_mix(h, c)
69 if h < 0 { h = 0 - h }
70 return h
71}
72// trait bounds + mutation step (range/32, min 1) -- DATA, one place
73func gn_lo(t: i64) -> i64 { return 0 }
74func gn_hi(t: i64) -> i64 {
75 if t == GN_T_SPECIES { return 7 }
76 if t == GN_T_SEX { return 1 }
77 if t >= GN_T_VIGOR { if t <= GN_T_TEMPER { return 31 } }
78 return 1000
79}
80func gn_mut(t: i64) -> i64 {
81 if t == GN_T_SPECIES { return 0 } // species shifts via the rare jump, not drift
82 if t == GN_T_SEX { return 0 }
83 let r: i64 = gn_hi(t) - gn_lo(t)
84 var m: i64 = r / 32
85 if m < 1 { m = 1 }
86 return m
87}
88func gn_clamp(v: i64, lo: i64, hi: i64) -> i64 { if v<lo { return lo } if v>hi { return hi } return v }
89
90func gn_get(g: *i64, t: i64) -> i64 { return g[GN_O_TRAIT + t] }
91func gn_ck(g: *i64) -> i64 {
92 var c: i64 = GN_CK_SEED
93 var i: i64 = 0
94 while i < GN_LEN { c = gn_mix(c, g[i]); i = i + 1 }
95 return c
96}
97
98// deterministic wild genome within bounds; age is CALLER-declared (a wild adult vs a juvenile NPC)
99func gn_wild(g: *i64, seed: i64, age: i64) -> i64 {
100 var i: i64 = 0
101 while i < GN_LEN { g[i] = 0; i = i + 1 }
102 g[GN_F_AGE] = age
103 var t: i64 = 0
104 while t < GN_NT {
105 let span: i64 = gn_hi(t) - gn_lo(t) + 1
106 g[GN_O_TRAIT + t] = gn_lo(t) + gn_hash3(seed, t, 1) % span
107 t = t + 1
108 }
109 g[GN_F_LINE] = gn_hash3(seed, 0, 2)
110 return 0
111}
112
113// breed: seeded per-trait inheritance. REFUSES on a minor parent with `out` BIT-UNTOUCHED.
114func gn_breed(a: *i64, b: *i64, seed: i64, out: *i64) -> i64 {
115 if a[GN_F_AGE] < GN_ADULT_AGE { return GN_E_MINOR }
116 if b[GN_F_AGE] < GN_ADULT_AGE { return GN_E_MINOR }
117 var i: i64 = 0
118 while i < GN_LEN { out[i] = 0; i = i + 1 }
119 out[GN_F_AGE] = GN_ADULT_AGE // every emitted child is adult BY CONSTRUCTION
120 var t: i64 = 0
121 while t < GN_NT {
122 var v: i64 = gn_get(a, t)
123 if gn_hash3(seed, t, 3) % 2 == 1 { v = gn_get(b, t) }
124 let m: i64 = gn_mut(t)
125 if m > 0 {
126 let d: i64 = gn_hash3(seed, t, 4) % (2*m + 1) - m
127 v = v + d
128 }
129 if t == GN_T_SPECIES {
130 if gn_hash3(seed, t, 5) % GN_SPECIES_JUMP == 0 {
131 v = v + (gn_hash3(seed, t, 6) % 2) * 2 - 1
132 }
133 }
134 out[GN_O_TRAIT + t] = gn_clamp(v, gn_lo(t), gn_hi(t))
135 t = t + 1
136 }
137 out[GN_F_LINE] = gn_mix(gn_mix(a[GN_F_LINE], b[GN_F_LINE]), seed)
138 return 0
139}
140
141// morph: the transformation verb. Bounded delta to ONE trait; every other word bit-identical.
142func gn_morph(g: *i64, t: i64, delta: i64) -> i64 {
143 if t < 0 { return GN_E_TRAIT }
144 if t >= GN_NT { return GN_E_TRAIT }
145 let nv: i64 = gn_clamp(gn_get(g, t) + delta, gn_lo(t), gn_hi(t))
146 g[GN_O_TRAIT + t] = nv
147 return 0
148}
149
150// normalized trait distance (permil of each trait's range, summed) -- the heritability ruler
151func gn_dist(a: *i64, b: *i64) -> i64 {
152 var d: i64 = 0
153 var t: i64 = 0
154 while t < GN_NT {
155 var dv: i64 = gn_get(a, t) - gn_get(b, t)
156 if dv < 0 { dv = 0 - dv }
157 let r: i64 = gn_hi(t) - gn_lo(t)
158 if r > 0 { d = d + (dv * 1000) / r }
159 t = t + 1
160 }
161 return d
162}