code wiki / (root) / nx_folkgame_race_gate.nx

nx_folkgame_race_gate.nx source

↩ module page · 167 lines · 7862 B

1// nx_folkgame_race_gate.nx -- GATE for the race (track) family of nx_folkgame_race_lib. 2// 3// THE LOAD-BEARING TOOTH IS T2/T3: the opening pip count is 167 PER SIDE. That is a published 4// constant of the game -- two checkers 24 points from home, five 13, three 8, five 6 -- not a number 5// this estate picked. It is checked for BOTH players because a single side proves almost nothing: an 6// off-by-one in the track indexing, a reversed direction, or a pip formula that reads the wrong end 7// would still let ONE side total 167 by luck. Both sides landing on it at once pins the direction, 8// the indexing and the formula together. 9// T6 IS THE OTHER HALF: a race is not deterministic but a REPLAY of one must be. The randomiser's 10// seed lives inside the saved position, so the same seed must produce the same sequence -- and a 11// SECOND seed must produce a different one, or the tooth would pass on a die that always returns the 12// same face. 13// license_tier: ORIGINAL No hw writes (Rule 26). 14import "nx_folkgame_race_lib.nx" 15import "nx_gate_verdict.nx" 16 17const FGRG_SPEC: *u8 = "knowledge/compare/folkgames.race" 18// Published: the backgammon opening position is 167 pips for each player. 19const FGRG_PIP: i64 = 167 20const FGRG_TRACK: i64 = 24 21const FGRG_PIECES: i64 = 15 22// Two arbitrary but FIXED seeds. They are not tuned to make anything pass -- any two distinct values 23// exercise the same property, and the second exists only so "same seed reproduces" cannot be 24// satisfied by a die that ignores its seed entirely. 25const FGRG_SEED_A: i64 = 20260827 26const FGRG_SEED_B: i64 = 99999937 27const FGRG_ROLLS: i64 = 64 28const FGRG_PLIES: i64 = 200 29 30func fgrg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 31 32func main() -> i64 { 33 let ctr: *i64 = gv_ctr() 34 gv_head("nx_folkgame_race -- the track family opens at the published pip count and conserves its checkers" as *u8) 35 36 let bg: i64 = fg_parse_named_file(FGRG_SPEC, "backgammon" as *u8) 37 gv_check("T1 backgammon section parses from the shared family spec" as *u8, (bg != 0) as i64, ctr) 38 var ok2: i64 = 0 39 var ok3: i64 = 0 40 var ok4: i64 = 0 41 var ok5: i64 = 0 42 var p1: i64 = 0 - 1 43 var p2: i64 = 0 - 1 44 if bg != 0 { 45 if fg_cells(bg) == FGRG_TRACK { if fgr_pieces(bg) == FGRG_PIECES { ok2 = 1 } } 46 fgr_reset(bg, FGRG_SEED_A) 47 p1 = fgr_pip(bg, 1) 48 p2 = fgr_pip(bg, 2) 49 gv_puts(" opening pips: first player " as *u8) 50 gv_num(p1) 51 gv_puts(", second player " as *u8) 52 gv_num(p2) 53 gv_puts(", published constant 167\n" as *u8) 54 if p1 == FGRG_PIP { ok3 = 1 } 55 if p2 == FGRG_PIP { ok4 = 1 } 56 if fgr_total(bg, 1) == FGRG_PIECES { if fgr_total(bg, 2) == FGRG_PIECES { ok5 = 1 } } 57 } 58 gv_check("T1b twenty-four points and fifteen checkers a side" as *u8, ok2, ctr) 59 gv_check("T2 the FIRST player opens on exactly 167 pips, the published constant" as *u8, ok3, ctr) 60 gv_check("T3 the SECOND player opens on exactly 167 too, which pins direction and indexing together" as *u8, ok4, ctr) 61 gv_check("T4 fifteen checkers a side at the opening, counting board, bar and borne off" as *u8, ok5, ctr) 62 63 var ok6: i64 = 0 64 var ok7: i64 = 0 65 var inrange: i64 = 1 66 if bg != 0 { 67 let a: *i64 = sys_mmap(FGRG_ROLLS * FG_WORD) as *i64 68 let b: *i64 = sys_mmap(FGRG_ROLLS * FG_WORD) as *i64 69 let c: *i64 = sys_mmap(FGRG_ROLLS * FG_WORD) as *i64 70 fgr_reset(bg, FGRG_SEED_A) 71 var i: i64 = 0 72 while i < FGRG_ROLLS { a[i] = fgr_die(bg); i = i + 1 } 73 fgr_reset(bg, FGRG_SEED_A) 74 var j: i64 = 0 75 while j < FGRG_ROLLS { b[j] = fgr_die(bg); j = j + 1 } 76 fgr_reset(bg, FGRG_SEED_B) 77 var k: i64 = 0 78 while k < FGRG_ROLLS { c[k] = fgr_die(bg); k = k + 1 } 79 var same: i64 = 1 80 var diff: i64 = 0 81 var m: i64 = 0 82 while m < FGRG_ROLLS { 83 if a[m] != b[m] { same = 0 } 84 if a[m] != c[m] { diff = 1 } 85 if a[m] < 1 { inrange = 0 } 86 if a[m] > 6 { inrange = 0 } 87 m = m + 1 88 } 89 if same == 1 { ok6 = 1 } 90 if diff == 1 { ok7 = 1 } 91 } 92 gv_check("T5 every die face is inside one to six, so the randomiser cannot produce an impossible roll" as *u8, inrange, ctr) 93 gv_check("T6 the SAME seed reproduces the same sequence, so a saved position replays the die that follows it" as *u8, ok6, ctr) 94 gv_check("neg-control-a-DIFFERENT-seed-diverges so T6 cannot be satisfied by a die that ignores its seed" as *u8, ok7, ctr) 95 96 var ok8: i64 = 0 97 var plies: i64 = 0 98 var bad: i64 = 0 99 if bg != 0 { 100 let mm: i64 = fg_maxmoves(bg) 101 let out: *i64 = sys_mmap(mm * FG_WORD) as *i64 102 fgr_reset(bg, FGRG_SEED_A) 103 var t: i64 = 0 104 while t < FGRG_PLIES { 105 if fgr_terminal(bg) != FG_T_ONGOING { t = FGRG_PLIES } 106 if t < FGRG_PLIES { 107 let d: i64 = fgr_die(bg) 108 let n: i64 = fgr_moves(bg, d, out) 109 if n > 0 { 110 fgr_apply(bg, out[0], d) 111 plies = plies + 1 112 } 113 if fgr_total(bg, 1) != FGRG_PIECES { bad = bad + 1 } 114 if fgr_total(bg, 2) != FGRG_PIECES { bad = bad + 1 } 115 let sc: *i64 = fg_scal(bg) 116 sc[FG_S_SIDE] = 3 - sc[FG_S_SIDE] 117 t = t + 1 118 } 119 } 120 gv_puts(" played " as *u8) 121 gv_num(plies) 122 gv_puts(" plies, checker-count violations: " as *u8) 123 gv_num(bad) 124 gv_puts("\n" as *u8) 125 if bad == 0 { if plies > 0 { ok8 = 1 } } 126 } 127 gv_subjects("plies played under the conservation check" as *u8, plies, ctr) 128 gv_check("T7 checkers are conserved at every ply of a real played-out race, across board, bar and borne off" as *u8, ok8, ctr) 129 130 var ok9: i64 = 0 131 if bg != 0 { 132 fgr_reset(bg, FGRG_SEED_A) 133 let before: i64 = fgr_pip(bg, 1) 134 fgr_apply(bg, 0, 1) 135 let after: i64 = fgr_pip(bg, 1) 136 if after == before - 1 { ok9 = 1 } 137 } 138 gv_check("neg-control-the-pip-count-can-move moving one checker one point lowers the first player's pips by exactly one, so the 167 teeth are measuring something live" as *u8, ok9, ctr) 139 140 var ok10: i64 = 0 141 let nd: i64 = fg_parse_named_file(FGRG_SPEC, "nard" as *u8) 142 if nd != 0 { 143 if bg != 0 { 144 fgr_reset(nd, FGRG_SEED_A) 145 let hdn: *i64 = fg_hdr(nd) 146 let hdb: *i64 = fg_hdr(bg) 147 if fgr_pip(nd, 1) == FGRG_PIP { 148 if hdn[FG_H_HIT] == 0 { if hdb[FG_H_HIT] == 1 { ok10 = 1 } } 149 } 150 } 151 } 152 gv_check("T8 nard opens on the same 167 pips as backgammon and differs from it in exactly the hit flag, which is a DATA difference and not a second program" as *u8, ok10, ctr) 153 154 let b1: *u8 = "family|race\ncells|24\npieces|15\ndice|2|6\n" as *u8 155 let b2: *u8 = "family|race\ncells|24\ntrack|24\npieces|15\n" as *u8 156 let b3: *u8 = "family|race\ncells|20\ntrack|24\npieces|15\ndice|2|6\n" as *u8 157 let r1: i64 = fg_parse(b1, fgrg_slen(b1)) 158 let r2: i64 = fg_parse(b2, fgrg_slen(b2)) 159 let r3: i64 = fg_parse(b3, fgrg_slen(b3)) 160 gv_check("neg-control-no-track-refused a race game without a track row is REFUSED" as *u8, (r1 == 0) as i64, ctr) 161 gv_check("neg-control-no-dice-refused a race with no randomiser is not a race, and is REFUSED by name" as *u8, (r2 == 0) as i64, ctr) 162 gv_check("neg-control-track-mismatch-refused cells that do not match the declared track are REFUSED with both numbers named" as *u8, (r3 == 0) as i64, ctr) 163 164 let rc: i64 = gv_verdict("FOLKGAME-RACE" as *u8, ctr, "the track family opens at the published 167 pips on both sides and conserves its checkers" as *u8) 165 sys_exit(rc) 166 return rc 167}