code wiki / _hdl_build / nx_game_genetics.nx

nx_game_genetics.nx source

↩ module page · 479 lines · 21009 B

1// nx_game_genetics.nx -- REAL MENDELIAN GENETICS as the breeding core (ws=game-interact rung 3b; 2// operator: "use Mendelian genetics so we can really reuse systems, fun not overwhelming... educational 3// while entertaining, teach real world science"). 4// 5// THE SCIENCE (this is textbook genetics, implemented honestly -- not a "genetics-flavoured" rng): 6// * DIPLOID genome: every trait carries TWO alleles, one from each parent. 7// * MENDEL'S 1st LAW (SEGREGATION): a parent passes ONE randomly-chosen allele of its pair (a gamete). 8// * MENDEL'S 2nd LAW (INDEPENDENT ASSORTMENT): each trait segregates independently of the others. 9// * The famous emergent ratios are therefore NOT hardcoded -- they FALL OUT: monohybrid Aa x Aa gives 10// genotypes 1:2:1 and phenotypes 3:1; a dihybrid cross gives 9:3:3:1; a test cross gives 1:1. 11// The gate MEASURES them. That is the difference between teaching science and cosplaying it. 12// * Three real inheritance MODES, each the textbook exemplar: 13// SIMPLE dominant/recessive (pea plants; horned/hornless here) 14// INCOMPLETE blending heterozygote (snapdragon flower colour: red x white -> PINK, and F2 is 15// 1:2:1 PHENOTYPES -- the fact that distinguishes it from simple dominance) 16// CODOM multiple alleles + codominance (human ABO blood groups: A and B both expressed -> AB) 17// * CARRIERS are real and visible-to-the-game: a heterozygote showing the dominant phenotype still 18// carries the recessive allele -- the whole reason selective breeding is interesting. 19// 20// FUN, NOT OVERWHELMING (the design constraint): FIVE visible traits, each with an obvious readable 21// phenotype. The player never sees a genotype string unless they ask; the Punnett square (gx_punnett) 22// is the ONE teaching widget -- 4 cells, the real thing, computed not illustrated. 23// 24// Genome packs into ONE i64: trait t occupies 4 bits (two 2-bit alleles) => 5 traits = 20 bits, and it 25// rides the existing companion component word, so nothing else in the parts library changes shape. 26// All integer, deterministic. LIB, no main. license_tier: ORIGINAL 27import "nx_syscalls.nx" 28 29// ---- traits (DATA-indexed; adding a trait = adding a row, never editing logic) ---- 30const GX_NTRAIT: i64 = 5 31const GX_HORN: i64 = 0 // SIMPLE H dominant (horned) / h recessive (hornless) 32const GX_WING: i64 = 1 // SIMPLE W dominant (wingless) / w recessive (winged -- rare, prized) 33const GX_COAT: i64 = 2 // INCOMPLETE crimson / rose (blend) / white 34const GX_ELEM: i64 = 3 // CODOM ABO-style: A, B codominant, O recessive 35const GX_TAIL: i64 = 4 // SIMPLE T dominant (long) / t recessive (short) 36 37const GX_SIMPLE: i64 = 0 38const GX_INCOMPLETE: i64 = 1 39const GX_CODOM: i64 = 2 40 41func gx_mode(t: i64) -> i64 { 42 if t == GX_COAT { return GX_INCOMPLETE } 43 if t == GX_ELEM { return GX_CODOM } 44 return GX_SIMPLE 45} 46 47// ---- genome word access: trait t -> bits [t*4 .. t*4+3], allele0 low 2 bits, allele1 next 2 ---- 48func gx_a0(g: i64, t: i64) -> i64 { return (g >> (t*4)) & 3 } 49func gx_a1(g: i64, t: i64) -> i64 { return (g >> (t*4 + 2)) & 3 } 50func gx_set(g: i64, t: i64, a: i64, b: i64) -> i64 { 51 let mask: i64 = 15 << (t*4) 52 var v: i64 = g & (0 - 1 - mask) 53 v = v | ((a & 3) << (t*4)) 54 v = v | ((b & 3) << (t*4 + 2)) 55 return v 56} 57// homozygous / heterozygous -- the vocabulary the game teaches by using it 58func gx_homozygous(g: i64, t: i64) -> i64 { if gx_a0(g, t) == gx_a1(g, t) { return 1 } return 0 } 59func gx_heterozygous(g: i64, t: i64) -> i64 { if gx_a0(g, t) != gx_a1(g, t) { return 1 } return 0 } 60 61// ---- PHENOTYPE: what you actually SEE. The mode decides how the pair is expressed. ---- 62// SIMPLE: allele 1 = dominant. phenotype 1 if either allele is 1, else 0. 63// INCOMPLETE: 0/0 -> 0 (white), 0/1 or 1/0 -> 1 (rose blend), 1/1 -> 2 (crimson). 64// CODOM: alleles 0=O 1=A 2=B. phenotypes 0=O 1=A 2=B 3=AB. 65func gx_phenotype(g: i64, t: i64) -> i64 { 66 let a: i64 = gx_a0(g, t) 67 let b: i64 = gx_a1(g, t) 68 let m: i64 = gx_mode(t) 69 if m == GX_SIMPLE { 70 if a == 1 { return 1 } 71 if b == 1 { return 1 } 72 return 0 73 } 74 if m == GX_INCOMPLETE { 75 return a + b 76 } 77 // CODOM (ABO) 78 var hasA: i64 = 0 79 var hasB: i64 = 0 80 if a == 1 { hasA = 1 } 81 if b == 1 { hasA = 1 } 82 if a == 2 { hasB = 1 } 83 if b == 2 { hasB = 1 } 84 if hasA == 1 { if hasB == 1 { return 3 } } 85 if hasA == 1 { return 1 } 86 if hasB == 1 { return 2 } 87 return 0 88} 89 90// a CARRIER shows the dominant phenotype but hides a recessive allele (SIMPLE traits only) 91func gx_carrier(g: i64, t: i64) -> i64 { 92 if gx_mode(t) != GX_SIMPLE { return 0 } 93 if gx_phenotype(g, t) != 1 { return 0 } 94 return gx_heterozygous(g, t) 95} 96 97// ---- names (the teaching surface; the UI reads these, never raw numbers) ---- 98func gx_trait_name(t: i64) -> *u8 { 99 if t == GX_HORN { return "Horns" as *u8 } 100 if t == GX_WING { return "Wings" as *u8 } 101 if t == GX_COAT { return "Coat" as *u8 } 102 if t == GX_ELEM { return "Affinity" as *u8 } 103 return "Tail" 104} 105func gx_mode_name(t: i64) -> *u8 { 106 let m: i64 = gx_mode(t) 107 if m == GX_INCOMPLETE { return "incomplete dominance" as *u8 } 108 if m == GX_CODOM { return "codominant, multiple alleles" as *u8 } 109 return "simple dominance" 110} 111func gx_pheno_name(t: i64, ph: i64) -> *u8 { 112 if t == GX_HORN { if ph == 1 { return "horned" as *u8 } return "hornless" } 113 if t == GX_WING { if ph == 1 { return "wingless" as *u8 } return "WINGED" } 114 if t == GX_COAT { 115 if ph == 2 { return "crimson" as *u8 } 116 if ph == 1 { return "rose" as *u8 } 117 return "white" 118 } 119 if t == GX_ELEM { 120 if ph == 3 { return "dual-affinity (AB)" as *u8 } 121 if ph == 2 { return "storm (B)" as *u8 } 122 if ph == 1 { return "ember (A)" as *u8 } 123 return "null (O)" 124 } 125 if ph == 1 { return "long tail" as *u8 } 126 return "short tail" 127} 128// allele letter for a genotype readout, e.g. "Hh" -- real notation, the way a textbook writes it 129func gx_allele_char(t: i64, a: i64) -> *u8 { 130 if t == GX_ELEM { 131 if a == 1 { return "A" as *u8 } 132 if a == 2 { return "B" as *u8 } 133 return "O" 134 } 135 if t == GX_COAT { 136 if a == 1 { return "R" as *u8 } 137 return "W" 138 } 139 if t == GX_HORN { if a == 1 { return "H" as *u8 } return "h" } 140 if t == GX_WING { if a == 1 { return "W" as *u8 } return "w" } 141 if a == 1 { return "T" as *u8 } 142 return "t" 143} 144 145// ---- rng (caller-owned stream => deterministic, replayable) ---- 146func gx_rng(s: *i64) -> i64 { 147 var x: i64 = s[0] 148 x = x ^ (x << 13); x = x ^ (x >> 7); x = x ^ (x << 17) 149 s[0] = x 150 if x < 0 { return 0 - x } 151 return x 152} 153 154// ---- MENDEL'S 1st LAW: a gamete carries ONE allele of the pair, chosen 50/50 ---- 155func gx_gamete(g: i64, t: i64, s: *i64) -> i64 { 156 if (gx_rng(s) & 1) == 1 { return gx_a1(g, t) } 157 return gx_a0(g, t) 158} 159 160// ---- MENDEL'S 2nd LAW: every trait assorts independently -> the child genome ---- 161func gx_cross(gA: i64, gB: i64, s: *i64) -> i64 { 162 var child: i64 = 0 163 var t: i64 = 0 164 while t < GX_NTRAIT { 165 let fromA: i64 = gx_gamete(gA, t, s) 166 let fromB: i64 = gx_gamete(gB, t, s) 167 child = gx_set(child, t, fromA, fromB) 168 t = t + 1 169 } 170 return child 171} 172 173// ---- THE PUNNETT SQUARE: the teaching widget, computed not drawn from a lookup ---- 174// Fills out[0..3] with the four possible child genotype pairs (packed a*4+b, in the classic 175// row-major order A0xB0, A0xB1, A1xB0, A1xB1) and out[4..7] with each cell's PHENOTYPE. 176// Returns the number of DISTINCT phenotypes among the four cells (1..4) -- what the player learns. 177func gx_punnett(gA: i64, gB: i64, t: i64, out: *i64) -> i64 { 178 let a0: i64 = gx_a0(gA, t) 179 let a1: i64 = gx_a1(gA, t) 180 let b0: i64 = gx_a0(gB, t) 181 let b1: i64 = gx_a1(gB, t) 182 out[0] = a0*4 + b0 183 out[1] = a0*4 + b1 184 out[2] = a1*4 + b0 185 out[3] = a1*4 + b1 186 var i: i64 = 0 187 while i < 4 { 188 let pa: i64 = out[i] / 4 189 let pb: i64 = out[i] % 4 190 out[4 + i] = gx_phenotype(gx_set(0, t, pa, pb), t) 191 i = i + 1 192 } 193 var distinct: i64 = 0 194 var j: i64 = 0 195 while j < 4 { 196 var seen: i64 = 0 197 var k: i64 = 0 198 while k < j { if out[4 + k] == out[4 + j] { seen = 1 } k = k + 1 } 199 if seen == 0 { distinct = distinct + 1 } 200 j = j + 1 201 } 202 return distinct 203} 204// probability (in permil) that this pairing yields phenotype `ph` for trait t -- the Punnett square 205// read as odds, which is exactly how a breeder plans a pairing. 206func gx_odds_permil(gA: i64, gB: i64, t: i64, ph: i64) -> i64 { 207 let sq: *i64 = sys_mmap(16 * 8) as *i64 208 gx_punnett(gA, gB, t, sq) 209 var hits: i64 = 0 210 var i: i64 = 0 211 while i < 4 { if sq[4 + i] == ph { hits = hits + 1 } i = i + 1 } 212 return hits * 250 213} 214 215// ---- founder genomes: a starting population with real allele diversity (so breeding has somewhere 216// to go). Homozygous-dominant founders would make the recessive traits unreachable = a dead game. 217func gx_founder(seed: i64) -> i64 { 218 let s: *i64 = sys_mmap(16) as *i64 219 s[0] = seed * 2654435761 + 12345 220 var g: i64 = 0 221 var t: i64 = 0 222 while t < GX_NTRAIT { 223 var hi: i64 = 2 224 if t == GX_ELEM { hi = 3 } 225 let a: i64 = gx_rng(s) % hi 226 let b: i64 = gx_rng(s) % hi 227 g = gx_set(g, t, a, b) 228 t = t + 1 229 } 230 return g 231} 232 233// a compact "how rare is this creature" score in permil: recessive/rare phenotypes are worth more. 234// Winged (recessive), crimson (homozygous), and AB (needs both alleles) are the prizes -- so the 235// player learns WHY they are rare by having to breed for them. 236func gx_rarity_permil(g: i64) -> i64 { 237 var r: i64 = 0 238 if gx_phenotype(g, GX_WING) == 0 { r = r + 400 } // winged: needs ww 239 if gx_phenotype(g, GX_COAT) == 2 { r = r + 250 } // crimson: needs RR 240 if gx_phenotype(g, GX_ELEM) == 3 { r = r + 250 } // AB: needs A and B 241 if gx_phenotype(g, GX_HORN) == 0 { r = r + 50 } 242 if gx_phenotype(g, GX_TAIL) == 0 { r = r + 50 } 243 return r 244} 245 246// =================================================================================================== 247// MODERN GENETICS LAYER -- the phenomena that BREAK naive Mendelism, which is exactly why they teach 248// it. Each one is a real, named mechanism with the textbook behaviour, and each has a gate tooth that 249// MEASURES the deviation rather than asserting it. (operator: "modern with epigenetics and all the 250// other things so its fun but fundamentals are definitely learned") 251// =================================================================================================== 252 253// ---- 1. SEX DETERMINATION + SEX-LINKED INHERITANCE (the ZW system: birds/reptiles/butterflies) ---- 254// We use ZW (female ZW, male ZZ) rather than XY on purpose: in ZW it is the MOTHER who determines 255// offspring sex, which corrects the common misconception that it is always the father. 256// The sex-linked trait sits on the Z: a male (ZZ) has two copies, a female (ZW) has ONE -- so a 257// female shows a recessive Z allele with no second copy to mask it (HEMIZYGOUS). That asymmetry is 258// the classic sex-linkage signature (colour-blindness in humans, barring in chickens). 259const GX_SEX_F: i64 = 0 260const GX_SEX_M: i64 = 1 261// sex + the Z-linked allele(s) pack into their own word: bit0 = sex, bits1-2 = Z allele A, 262// bits3-4 = Z allele B (males only; females carry W = no allele) 263func gx_sex(sg: i64) -> i64 { return sg & 1 } 264func gx_z0(sg: i64) -> i64 { return (sg >> 1) & 3 } 265func gx_z1(sg: i64) -> i64 { return (sg >> 3) & 3 } 266func gx_sexgene(sex: i64, z0: i64, z1: i64) -> i64 { return (sex & 1) | ((z0 & 3) << 1) | ((z1 & 3) << 3) } 267// the Z-linked trait: 1 = dominant (plain), 0 = recessive (LUMINOUS -- the prize) 268func gx_z_phenotype(sg: i64) -> i64 { 269 if gx_sex(sg) == GX_SEX_M { 270 if gx_z0(sg) == 1 { return 1 } 271 if gx_z1(sg) == 1 { return 1 } 272 return 0 273 } 274 // female: hemizygous, the single Z allele is expressed with nothing to mask it 275 if gx_z0(sg) == 1 { return 1 } 276 return 0 277} 278// a MALE heterozygote (Z^1 Z^0) is a carrier; a female CANNOT be a carrier -- she just shows it. 279func gx_z_carrier(sg: i64) -> i64 { 280 if gx_sex(sg) != GX_SEX_M { return 0 } 281 if gx_z0(sg) == gx_z1(sg) { return 0 } 282 return 1 283} 284// ZW cross: mother (ZW) gives Z or W; father (ZZ) always gives a Z. W from mother => daughter. 285func gx_cross_sex(sgMother: i64, sgFather: i64, s: *i64) -> i64 { 286 let fromFather: i64 = gx_z0(sgFather) 287 var ff: i64 = fromFather 288 if (gx_rng(s) & 1) == 1 { ff = gx_z1(sgFather) } 289 if (gx_rng(s) & 1) == 1 { 290 // mother passed her Z -> son (ZZ), one Z from each parent 291 return gx_sexgene(GX_SEX_M, gx_z0(sgMother), ff) 292 } 293 // mother passed W -> daughter (ZW), her single Z comes from her FATHER 294 return gx_sexgene(GX_SEX_F, ff, 0) 295} 296 297// ---- 2. GENETIC LINKAGE + RECOMBINATION (Morgan/Sturtevant: genes on the same chromosome) ---- 298// Mendel's 2nd law assumes independent assortment -- true only for genes on DIFFERENT chromosomes. 299// Linked genes travel together UNLESS crossing-over separates them. Recombination frequency is the 300// map distance (1% recombination = 1 centimorgan) -- how the first gene maps were ever drawn. 301// HORN and TAIL are linked here at 12 cM: they co-inherit 88% of the time. 302const GX_LINK_A: i64 = 0 // = GX_HORN 303const GX_LINK_B: i64 = 4 // = GX_TAIL 304const GX_LINK_CM: i64 = 120 // recombination frequency in per-mille (120 = 12 cM) 305// a linked gamete: pick a haplotype (which parental chromosome), then allow crossing-over 306func gx_gamete_linked(g: i64, s: *i64, out: *i64) -> i64 { 307 var side: i64 = 0 308 if (gx_rng(s) & 1) == 1 { side = 1 } 309 var aA: i64 = gx_a0(g, GX_LINK_A) 310 var aB: i64 = gx_a0(g, GX_LINK_B) 311 if side == 1 { aA = gx_a1(g, GX_LINK_A); aB = gx_a1(g, GX_LINK_B) } 312 var recombined: i64 = 0 313 if (gx_rng(s) % 1000) < GX_LINK_CM { 314 // crossing-over: the B locus comes from the OTHER chromosome 315 recombined = 1 316 if side == 1 { aB = gx_a0(g, GX_LINK_B) } 317 if side == 0 { aB = gx_a1(g, GX_LINK_B) } 318 } 319 out[0] = aA 320 out[1] = aB 321 return recombined 322} 323// full cross honouring linkage for the linked pair and independent assortment for everything else 324func gx_cross_linked(gA: i64, gB: i64, s: *i64, info: *i64) -> i64 { 325 let ga: *i64 = sys_mmap(8 * 8) as *i64 326 let gb: *i64 = sys_mmap(8 * 8) as *i64 327 let rA: i64 = gx_gamete_linked(gA, s, ga) 328 let rB: i64 = gx_gamete_linked(gB, s, gb) 329 info[0] = rA + rB // how many recombinant gametes this birth used 330 var child: i64 = 0 331 child = gx_set(child, GX_LINK_A, ga[0], gb[0]) 332 child = gx_set(child, GX_LINK_B, ga[1], gb[1]) 333 var t: i64 = 0 334 while t < GX_NTRAIT { 335 var linked: i64 = 0 336 if t == GX_LINK_A { linked = 1 } 337 if t == GX_LINK_B { linked = 1 } 338 if linked == 0 { child = gx_set(child, t, gx_gamete(gA, t, s), gx_gamete(gB, t, s)) } 339 t = t + 1 340 } 341 return child 342} 343 344// ---- 3. POLYGENIC TRAIT (quantitative genetics: many genes -> a continuous bell-curve trait) ---- 345// Size is not one gene. Four additive loci (0/1 each, both alleles counted) give a dose 0..8, which 346// is the discrete version of the normal distribution real quantitative traits follow. This is why 347// height is not "tall vs short" -- and why selective breeding SHIFTS A MEAN rather than flipping a switch. 348const GX_POLY_N: i64 = 4 349func gx_poly_dose(pg: i64) -> i64 { 350 var d: i64 = 0 351 var i: i64 = 0 352 while i < GX_POLY_N * 2 { d = d + ((pg >> i) & 1); i = i + 1 } 353 return d 354} 355func gx_poly_gamete(pg: i64, s: *i64) -> i64 { 356 var out: i64 = 0 357 var i: i64 = 0 358 while i < GX_POLY_N { 359 var a: i64 = (pg >> (i*2)) & 1 360 if (gx_rng(s) & 1) == 1 { a = (pg >> (i*2 + 1)) & 1 } 361 out = out | (a << i) 362 i = i + 1 363 } 364 return out 365} 366func gx_poly_cross(pgA: i64, pgB: i64, s: *i64) -> i64 { 367 let ha: i64 = gx_poly_gamete(pgA, s) 368 let hb: i64 = gx_poly_gamete(pgB, s) 369 var child: i64 = 0 370 var i: i64 = 0 371 while i < GX_POLY_N { 372 child = child | (((ha >> i) & 1) << (i*2)) 373 child = child | (((hb >> i) & 1) << (i*2 + 1)) 374 i = i + 1 375 } 376 return child 377} 378func gx_size_name(dose: i64) -> *u8 { 379 if dose <= 1 { return "tiny" as *u8 } 380 if dose <= 3 { return "small" as *u8 } 381 if dose <= 5 { return "average" as *u8 } 382 if dose <= 7 { return "large" as *u8 } 383 return "colossal" 384} 385 386// ---- 4. EPIGENETICS (the modern layer: heritable expression change WITHOUT DNA sequence change) ---- 387// Real mechanisms, honestly modelled: 388// * METHYLATION SILENCES a gene: the allele is still there and still passed on, but not expressed. 389// (Agouti mice: identical DNA, diet-driven methylation, visibly different coats.) 390// * The mark is acquired from the ENVIRONMENT the parent lived in -- not from its genes. 391// * It is INHERITED but REVERSIBLE and it FADES across generations (transgenerational epigenetic 392// inheritance decays; this is the key difference from a mutation, which is permanent). 393// * THE FUNDAMENTAL THE PLAYER MUST LEARN: a silenced dominant allele still SEGREGATES normally. 394// Breed from a silenced parent and the allele reappears in grandchildren -- genotype is untouched. 395// Epigenome word: bits 0-4 = methylation mark per trait, bits 8-12 = mark "strength" (fades). 396const GX_EPI_FADE: i64 = 1 397func gx_epi_marked(eg: i64, t: i64) -> i64 { return (eg >> t) & 1 } 398func gx_epi_strength(eg: i64, t: i64) -> i64 { return (eg >> (8 + t*3)) & 7 } 399func gx_epi_set(eg: i64, t: i64, strength: i64) -> i64 { 400 var v: i64 = eg 401 if strength > 0 { v = v | (1 << t) } 402 if strength <= 0 { v = v & (0 - 1 - (1 << t)) } 403 let mask: i64 = 7 << (8 + t*3) 404 v = v & (0 - 1 - mask) 405 var st: i64 = strength 406 if st > 7 { st = 7 } 407 if st < 0 { st = 0 } 408 v = v | (st << (8 + t*3)) 409 return v 410} 411// environment imprints a mark (e.g. a lush vs harsh den) -- acquired, not inherited from genes 412func gx_epi_imprint(eg: i64, t: i64, env_intensity: i64) -> i64 { 413 return gx_epi_set(eg, t, env_intensity) 414} 415// EXPRESSED phenotype = genotype phenotype, unless silenced -> the recessive/base phenotype shows 416func gx_expressed(g: i64, eg: i64, t: i64) -> i64 { 417 if gx_epi_marked(eg, t) == 0 { return gx_phenotype(g, t) } 418 if gx_mode(t) == GX_SIMPLE { return 0 } // dominant allele silenced -> recessive look 419 if gx_mode(t) == GX_INCOMPLETE { 420 let ph: i64 = gx_phenotype(g, t) 421 if ph > 0 { return ph - 1 } // one step toward white 422 return 0 423 } 424 return 0 // codominant silenced -> null (O) 425} 426// inheritance of the marks: passed on but WEAKER each generation, and gone at zero (reversibility) 427func gx_epi_inherit(egA: i64, egB: i64) -> i64 { 428 var child: i64 = 0 429 var t: i64 = 0 430 while t < GX_NTRAIT { 431 var st: i64 = gx_epi_strength(egA, t) 432 let sb: i64 = gx_epi_strength(egB, t) 433 if sb > st { st = sb } 434 st = st - GX_EPI_FADE 435 if st < 0 { st = 0 } 436 child = gx_epi_set(child, t, st) 437 t = t + 1 438 } 439 return child 440} 441 442// ---- 5. MUTATION (rare, permanent, the ultimate source of all the variation above) ---- 443// Distinguished from an epigenetic mark BY CONSTRUCTION: this edits the genome word itself, so it 444// does not fade and it is passed on forever. Rate is per-birth in per-mille. 445const GX_MUT_PERMIL: i64 = 8 446func gx_mutate(g: i64, s: *i64, out: *i64) -> i64 { 447 out[0] = 0 448 if (gx_rng(s) % 1000) >= GX_MUT_PERMIL { return g } 449 let t: i64 = gx_rng(s) % GX_NTRAIT 450 var hi: i64 = 2 451 if t == GX_ELEM { hi = 3 } 452 let newa: i64 = gx_rng(s) % hi 453 out[0] = 1 454 out[1] = t 455 if (gx_rng(s) & 1) == 1 { return gx_set(g, t, gx_a0(g, t), newa) } 456 return gx_set(g, t, newa, gx_a1(g, t)) 457} 458 459// ---- THE FULL MODERN CROSS: one call, every mechanism, deterministic. ---- 460// Creature record (caller-held, stride 4): [genome, sexgene, polygene, epigenome] 461// info out: [0]=recombinations [1]=mutated(0/1) [2]=mutated_trait [3]=child sex 462const GX_REC: i64 = 4 463func gx_breed_modern(pA: *i64, pB: *i64, s: *i64, child: *i64, info: *i64) -> i64 { 464 // mother = whichever parent is female; ZW means SHE decides the child's sex 465 var mother: *i64 = pA 466 var father: *i64 = pB 467 if gx_sex(pA[1]) == GX_SEX_M { mother = pB; father = pA } 468 let mut_out: *i64 = sys_mmap(8 * 8) as *i64 469 var g: i64 = gx_cross_linked(pA[0], pB[0], s, info) 470 g = gx_mutate(g, s, mut_out) 471 child[0] = g 472 child[1] = gx_cross_sex(mother[1], father[1], s) 473 child[2] = gx_poly_cross(pA[2], pB[2], s) 474 child[3] = gx_epi_inherit(pA[3], pB[3]) 475 info[1] = mut_out[0] 476 info[2] = mut_out[1] 477 info[3] = gx_sex(child[1]) 478 return 0 479}