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}