code wiki / _hdl_build / nx_breeder_lab.nx

nx_breeder_lab.nx source

↩ module page · 240 lines · 11769 B

1// nx_breeder_lab.nx -- THE GENETICS LAB SCREEN + the tooth that proves the science is VISIBLE. 2// Emits a real game screen: two parents, their Punnett square, and the litter they actually produced -- 3// and then MEASURES the rendered litter's visible phenotype ratios against the Punnett prediction. 4// That measurement is the difference between "we have genetics" and "the player can SEE genetics". 5// Educational payload, on screen: genotype notation (Hh), the 4-cell square, the predicted odds, and 6// the observed count -- the exact loop a biology student runs by hand. 7// license_tier: ORIGINAL 8import "nx_syscalls.nx" 9import "nx_game_taming.nx" 10import "nx_creature_render.nx" 11import "nx_game_art.nx" 12import "nx_game_critic.nx" 13import "nx_png_write.nx" 14 15const LW: i64 = 640 16const LH: i64 = 400 17 18func lp(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 19func ln2(v: i64) -> i64 { 20 let t: *u8 = sys_mmap(32) as *u8 21 var m: i64 = v 22 var w: i64 = 0 23 if m < 0 { t[w] = 45 as u8; w = w + 1; m = 0 - m } 24 if m == 0 { t[w] = 48 as u8; sys_write(1, t, w + 1); return 0 } 25 let d: *u8 = sys_mmap(32) as *u8 26 var k: i64 = 0 27 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 28 var j: i64 = 0 29 while j < k { t[w] = d[k - 1 - j]; w = w + 1; j = j + 1 } 30 sys_write(1, t, w) 31 return 0 32} 33func lnl() -> i64 { lp("\n" as *u8); return 0 } 34 35func lab_png(fb: *i64, path: *u8) -> i64 { 36 let rgb: *u8 = sys_mmap(LW*LH*3) as *u8 37 var p2: i64 = 0 38 let n: i64 = LW*LH 39 while p2 < n { 40 let v: i64 = fb[p2] 41 rgb[p2*3] = (v & 0xff) as u8 42 rgb[p2*3+1] = ((v >> 8) & 0xff) as u8 43 rgb[p2*3+2] = ((v >> 16) & 0xff) as u8 44 p2 = p2 + 1 45 } 46 return nx_png_write_rgb(path, rgb, LW, LH) 47} 48 49// draw a parent's genotype in real notation, e.g. "Hh" -- the vocabulary the game teaches by using it 50func lab_geno_str(fb: *i64, x: i64, y: i64, g: i64, t: i64, col: i64) -> i64 { 51 var cx2: i64 = gr_text(fb, LW, LH, x, y, gx_allele_char(t, gx_a0(g, t)), col) 52 cx2 = gr_text(fb, LW, LH, cx2, y, gx_allele_char(t, gx_a1(g, t)), col) 53 return cx2 54} 55 56func main() -> i64 { 57 let fb: *i64 = sys_mmap(LW*LH*8) as *i64 58 art_nebula(fb, LW, LH, 12, 10, 30, 34, 18, 58) 59 art_stardust(fb, LW, LH, 150) 60 let ink: i64 = gr_pack(238,234,255) 61 let dim: i64 = gr_pack(150,146,180) 62 let hot: i64 = gr_pack(255,200,110) 63 64 gr_rect(fb, LW, LH, 0, 0, LW-1, 24, gr_pack(16,14,34)) 65 gr_text(fb, LW, LH, 10, 8, "NISHI BREEDERS -- GENETICS LAB" as *u8, ink) 66 gr_text(fb, LW, LH, 300, 8, "coat: incomplete dominance (RR crimson / RW rose / WW white)" as *u8, dim) 67 68 // ---- the cross under study: two ROSE (heterozygous) parents. Textbook F1 x F1. ---- 69 let ros: *i64 = sys_mmap(en_bytes(300, COMP_NC)) as *i64 70 en_init(ros, 300, COMP_NC) 71 var het: i64 = gx_set(0, GX_COAT, 0, 1) // R W -> rose 72 het = gx_set(het, GX_HORN, 0, 1) // H h -> horned carrier 73 het = gx_set(het, GX_WING, 0, 1) // W w -> wingless carrier 74 het = gx_set(het, GX_TAIL, 1, 1) 75 het = gx_set(het, GX_ELEM, 1, 2) // A B -> codominant AB 76 77 let mum: i64 = en_spawn(ros) 78 en_set(ros, mum, C_SPEC, 1); en_set(ros, mum, C_IV0, 16); en_set(ros, mum, C_IV1, 16) 79 en_set(ros, mum, C_IV2, 16); en_set(ros, mum, C_IV3, 16); en_set(ros, mum, C_LVL, 6) 80 en_set(ros, mum, C_XP, 0); en_set(ros, mum, C_NICK, 0) 81 en_set(ros, mum, C_GENO, het); en_set(ros, mum, C_SEXG, gx_sexgene(GX_SEX_F, 1, 0)) 82 en_set(ros, mum, C_POLY, 85); en_set(ros, mum, C_EPI, 0) 83 en_set(ros, mum, C_TRUST, 800); en_set(ros, mum, C_WILL, 200); en_set(ros, mum, C_DISP, 800) 84 85 let dad: i64 = en_spawn(ros) 86 en_set(ros, dad, C_SPEC, 1); en_set(ros, dad, C_IV0, 16); en_set(ros, dad, C_IV1, 16) 87 en_set(ros, dad, C_IV2, 16); en_set(ros, dad, C_IV3, 16); en_set(ros, dad, C_LVL, 6) 88 en_set(ros, dad, C_XP, 0); en_set(ros, dad, C_NICK, 1) 89 en_set(ros, dad, C_GENO, het); en_set(ros, dad, C_SEXG, gx_sexgene(GX_SEX_M, 1, 0)) 90 en_set(ros, dad, C_POLY, 85); en_set(ros, dad, C_EPI, 0) 91 en_set(ros, dad, C_TRUST, 800); en_set(ros, dad, C_WILL, 200); en_set(ros, dad, C_DISP, 800) 92 93 // ---- parents panel ---- 94 gr_rect(fb, LW, LH, 8, 32, 210, 190, gr_pack(20,18,42)) 95 gr_rect(fb, LW, LH, 8, 32, 210, 34, gr_pack(70,62,120)) 96 gr_text(fb, LW, LH, 14, 40, "PARENTS" as *u8, ink) 97 cr_creature_gx(fb, LW, LH, 62, 110, 36, 1, 16,16,16,16, het, 85, 0) 98 cr_creature_gx(fb, LW, LH, 156, 110, 36, 1, 16,16,16,16, het, 85, 0) 99 var gx1: i64 = gr_text(fb, LW, LH, 30, 165, "coat " as *u8, dim) 100 lab_geno_str(fb, gx1, 165, het, GX_COAT, hot) 101 var gx2: i64 = gr_text(fb, LW, LH, 124, 165, "coat " as *u8, dim) 102 lab_geno_str(fb, gx2, 165, het, GX_COAT, hot) 103 gr_text(fb, LW, LH, 22, 176, "rose (heterozygous)" as *u8, dim) 104 105 // ---- PUNNETT SQUARE: computed by gx_punnett, drawn as the real 2x2 ---- 106 let sq: *i64 = sys_mmap(16 * 8) as *i64 107 gx_punnett(het, het, GX_COAT, sq) 108 gr_rect(fb, LW, LH, 224, 32, 420, 190, gr_pack(20,18,42)) 109 gr_rect(fb, LW, LH, 224, 32, 420, 34, gr_pack(70,62,120)) 110 gr_text(fb, LW, LH, 230, 40, "PUNNETT SQUARE (coat)" as *u8, ink) 111 // header alleles 112 gr_text(fb, LW, LH, 296, 56, gx_allele_char(GX_COAT, gx_a0(het, GX_COAT)), hot) 113 gr_text(fb, LW, LH, 356, 56, gx_allele_char(GX_COAT, gx_a1(het, GX_COAT)), hot) 114 gr_text(fb, LW, LH, 262, 84, gx_allele_char(GX_COAT, gx_a0(het, GX_COAT)), hot) 115 gr_text(fb, LW, LH, 262, 132, gx_allele_char(GX_COAT, gx_a1(het, GX_COAT)), hot) 116 var cell: i64 = 0 117 while cell < 4 { 118 let ccol: i64 = cell % 2 119 let crow: i64 = cell / 2 120 let bx: i64 = 284 + ccol*60 121 let by: i64 = 72 + crow*48 122 gr_rect(fb, LW, LH, bx, by, bx+52, by+40, gr_pack(30,27,58)) 123 let pa: i64 = sq[cell] / 4 124 let pb2: i64 = sq[cell] % 4 125 // the cell's own coat swatch = the phenotype that pairing produces 126 let ph: i64 = sq[4 + cell] 127 gr_rect(fb, LW, LH, bx+4, by+4, bx+48, by+20, 128 gr_pack(cr_coat_rgb(ph,0), cr_coat_rgb(ph,1), cr_coat_rgb(ph,2))) 129 var tx2: i64 = gr_text(fb, LW, LH, bx+8, by+24, gx_allele_char(GX_COAT, pa), ink) 130 gr_text(fb, LW, LH, tx2, by+24, gx_allele_char(GX_COAT, pb2), ink) 131 cell = cell + 1 132 } 133 let odds_rose: i64 = gx_odds_permil(het, het, GX_COAT, 1) 134 let odds_crim: i64 = gx_odds_permil(het, het, GX_COAT, 2) 135 let odds_white: i64 = gx_odds_permil(het, het, GX_COAT, 0) 136 var ox: i64 = gr_text(fb, LW, LH, 230, 170, "predicted crimson " as *u8, dim) 137 ox = gr_num(fb, LW, LH, ox, 170, odds_crim, hot) 138 ox = gr_text(fb, LW, LH, ox, 170, " rose " as *u8, dim) 139 ox = gr_num(fb, LW, LH, ox, 170, odds_rose, hot) 140 ox = gr_text(fb, LW, LH, ox, 170, " white " as *u8, dim) 141 gr_num(fb, LW, LH, ox, 170, odds_white, hot) 142 143 // ---- THE LITTER: actually breed 24 children and render them ---- 144 let bt: *i64 = sys_mmap((1 + 256*BRD_BEAT_STRIDE) * 8) as *i64 145 bt[0] = 0 146 let info: *i64 = sys_mmap(8 * 8) as *i64 147 gr_rect(fb, LW, LH, 8, 198, 632, 392, gr_pack(18,16,38)) 148 gr_rect(fb, LW, LH, 8, 198, 632, 200, gr_pack(70,62,120)) 149 gr_text(fb, LW, LH, 14, 206, "THE LITTER -- 24 children of these two parents" as *u8, ink) 150 var nC: i64 = 0 151 var nR: i64 = 0 152 var nW: i64 = 0 153 var nHorn: i64 = 0 154 var nWing: i64 = 0 155 var kid: i64 = 0 156 while kid < 24 { 157 let ch: i64 = brd_breed_info(ros, mum, dad, 1, 90210 + kid*31, bt, 256, kid, info) 158 if ch > 0 { 159 let g: i64 = en_get(ros, ch, C_GENO) 160 let ph: i64 = gx_phenotype(g, GX_COAT) 161 if ph == 2 { nC = nC + 1 } 162 if ph == 1 { nR = nR + 1 } 163 if ph == 0 { nW = nW + 1 } 164 if gx_phenotype(g, GX_HORN) == 1 { nHorn = nHorn + 1 } 165 if gx_phenotype(g, GX_WING) == 0 { nWing = nWing + 1 } 166 let col: i64 = kid % 8 167 let row: i64 = kid / 8 168 cr_creature_gx(fb, LW, LH, 48 + col*76, 246 + row*52, 21, 169 en_get(ros, ch, C_SPEC), en_get(ros, ch, C_IV0), en_get(ros, ch, C_IV1), 170 en_get(ros, ch, C_IV2), en_get(ros, ch, C_IV3), 171 g, en_get(ros, ch, C_POLY), en_get(ros, ch, C_EPI)) 172 } 173 kid = kid + 1 174 } 175 var sx: i64 = gr_text(fb, LW, LH, 14, 376, "observed crimson " as *u8, dim) 176 sx = gr_num(fb, LW, LH, sx, 376, nC, hot) 177 sx = gr_text(fb, LW, LH, sx, 376, " rose " as *u8, dim) 178 sx = gr_num(fb, LW, LH, sx, 376, nR, hot) 179 sx = gr_text(fb, LW, LH, sx, 376, " white " as *u8, dim) 180 sx = gr_num(fb, LW, LH, sx, 376, nW, hot) 181 sx = gr_text(fb, LW, LH, sx, 376, " of 24 (1:2:1 predicts 6:12:6)" as *u8, dim) 182 183 // ---- the tooth: the VISIBLE ratios must track the Punnett prediction over a big sample ---- 184 var bC: i64 = 0 185 var bR: i64 = 0 186 var bW: i64 = 0 187 var big: i64 = 0 188 while big < 1200 { 189 let ch2: i64 = brd_breed_info(ros, mum, dad, 1, 500000 + big*17, bt, 256, big, info) 190 if ch2 > 0 { 191 let ph2: i64 = gx_expressed(en_get(ros, ch2, C_GENO), en_get(ros, ch2, C_EPI), GX_COAT) 192 if ph2 == 2 { bC = bC + 1 } 193 if ph2 == 1 { bR = bR + 1 } 194 if ph2 == 0 { bW = bW + 1 } 195 en_destroy(ros, ch2) 196 } 197 big = big + 1 198 } 199 let tot: i64 = bC + bR + bW 200 var pC: i64 = 0 201 var pR: i64 = 0 202 var pW2: i64 = 0 203 if tot > 0 { pC = bC*1000/tot; pR = bR*1000/tot; pW2 = bW*1000/tot } 204 // ★THE LAW OF LARGE NUMBERS, on screen: a 24-child litter is NOISY (this one came out 8:8:8, not 205 // 6:12:6) while 1200 children land on the prediction. Showing both is the honest teaching -- a real 206 // breeder's small sample deviates, and that is not the theory being wrong. 207 var lx: i64 = gr_text(fb, LW, LH, 14, 386, "over 1200 children: " as *u8, dim) 208 lx = gr_num(fb, LW, LH, lx, 386, pC, hot) 209 lx = gr_text(fb, LW, LH, lx, 386, " / " as *u8, dim) 210 lx = gr_num(fb, LW, LH, lx, 386, pR, hot) 211 lx = gr_text(fb, LW, LH, lx, 386, " / " as *u8, dim) 212 lx = gr_num(fb, LW, LH, lx, 386, pW2, hot) 213 lx = gr_text(fb, LW, LH, lx, 386, " permil -- small samples are noisy, the law holds at scale" as *u8, dim) 214 215 lab_png(fb, "knowledge/nx_breeder_lab.png" as *u8) 216 217 let cout: *i64 = sys_mmap(GC_N * 8) as *i64 218 gc_score(fb, LW, LH, cout) 219 let q: i64 = gc_quality(cout) 220 221 lp("=== nx_breeder_lab: GENETICS LAB screen emitted ===\n" as *u8) 222 lp(" frame knowledge/nx_breeder_lab.png (640x400)\n" as *u8) 223 lp(" VISIBLE coat phenotypes over " as *u8); ln2(tot); lp(" rendered children: crimson " as *u8); ln2(pC) 224 lp(" / rose " as *u8); ln2(pR); lp(" / white " as *u8); ln2(pW2); lp(" permil\n" as *u8) 225 lp(" Punnett predicts crimson " as *u8); ln2(odds_crim) 226 lp(" / rose " as *u8); ln2(odds_rose); lp(" / white " as *u8); ln2(odds_white); lp(" permil\n" as *u8) 227 lp(" litter shown: horned " as *u8); ln2(nHorn); lp("/24, winged " as *u8); ln2(nWing); lp("/24\n" as *u8) 228 lp(" critic quality " as *u8); ln2(q); lp(" = " as *u8); lp(gc_verdict(q)); lnl() 229 var dC: i64 = pC - odds_crim 230 if dC < 0 { dC = 0 - dC } 231 var dR: i64 = pR - odds_rose 232 if dR < 0 { dR = 0 - dR } 233 var dW: i64 = pW2 - odds_white 234 if dW < 0 { dW = 0 - dW } 235 var ok: i64 = 0 236 if dC <= 60 { if dR <= 60 { if dW <= 60 { if tot == 1200 { ok = 1 } } } } 237 if ok == 1 { lp("VERDICT GREEN -- what the player SEES matches the Punnett square (max deviation " as *u8); ln2(dC+dR+dW); lp(" permil summed)\n" as *u8); return 0 } 238 lp("VERDICT RED -- rendered phenotypes drifted from the prediction\n" as *u8) 239 return 1 240}