nx_dr_elo_gate.nx source
↩ module page · 86 lines · 3681 B
1// nx_dr_elo_gate.nx -- KAT + neg-control for the sovereign Elo tournament ranker (DR-1).
2// Proves: expected-score table (equal/symmetry/gap/monotonic), correct rating updates
3// (an UPSET is rewarded more than an expected win), a real 3-player tournament ranks
4// the dominant player #1, and the NEG-CONTROL: ignoring match outcomes CANNOT
5// reproduce that ranking (outcomes drive the result). Uses the DRY nx_gate_verdict
6// lib (D001 migrate-on-touch law). exit 0 = GREEN. Sovereign lane (nx_cc->nxasm).
7import "nx_dr_elo.nx"
8import "nx_gate_verdict.nx"
9
10func main() -> i64 {
11 let ctr: *i64 = gv_ctr()
12 gv_head("nx_dr_elo -- sovereign deterministic Elo tournament ranker (DR-1)")
13 let tab: *i64 = el_make_etab()
14
15 // T1: equal ratings -> expected 500 (coin flip)
16 let e_eq: i64 = el_expected(1500, 1500, tab)
17 var ok1: i64 = 0
18 if e_eq == 500 { ok1 = 1 }
19 gv_check("T1 equal ratings expect 500", ok1, ctr)
20
21 // T2: expected-score symmetry E(a,b)+E(b,a)=1000
22 let e_ab: i64 = el_expected(1700, 1500, tab)
23 let e_ba: i64 = el_expected(1500, 1700, tab)
24 var ok2: i64 = 0
25 if (e_ab + e_ba) == 1000 { ok2 = 1 }
26 gv_check("T2 expected-score symmetry sums 1000", ok2, ctr)
27
28 // T3: a +400 stronger player expects ~909 (0.91)
29 let e_400: i64 = el_expected(1900, 1500, tab)
30 var ok3: i64 = 0
31 if e_400 >= 900 { if e_400 <= 918 { ok3 = 1 } }
32 gv_check("T3 +400 gap expects ~909", ok3, ctr)
33
34 // T4: monotonic -- bigger gap -> higher expected
35 var ok4: i64 = 0
36 if e_400 > e_ab { if e_ab > e_eq { ok4 = 1 } }
37 gv_check("T4 expected monotonic in rating gap", ok4, ctr)
38
39 // T5/T6: an UPSET (underdog 1500 beats favorite 1700) is rewarded more than a
40 // favorite's expected win; and it moves both ratings the right way.
41 let e_up: i64 = el_expected(1500, 1700, tab)
42 let up_new: i64 = el_update(1500, e_up, 1000, 32)
43 let up_gain: i64 = up_new - 1500
44 let e_fav: i64 = el_expected(1700, 1500, tab)
45 let fav_win_new: i64 = el_update(1700, e_fav, 1000, 32)
46 let fav_gain: i64 = fav_win_new - 1700
47 let fav_lose_new: i64 = el_update(1700, e_fav, 0, 32)
48 var ok5: i64 = 0
49 if up_gain > fav_gain { ok5 = 1 }
50 gv_check("T5 upset gain exceeds expected-win gain", ok5, ctr)
51 var ok6: i64 = 0
52 if up_gain > 0 { if fav_lose_new < 1700 { ok6 = 1 } }
53 gv_check("T6 upset moves ratings (underdog+ favorite-)", ok6, ctr)
54
55 // A real 3-player tournament: C(2) beats A(0) and B(1); A beats B -> rank [C,A,B].
56 let rat: *i64 = sys_mmap(3 * 8) as *i64
57 rat[0] = 1500; rat[1] = 1500; rat[2] = 1500
58 let ma: *i64 = sys_mmap(3 * 8) as *i64
59 let mb: *i64 = sys_mmap(3 * 8) as *i64
60 let mo: *i64 = sys_mmap(3 * 8) as *i64
61 ma[0] = 2; mb[0] = 0; mo[0] = 1000
62 ma[1] = 2; mb[1] = 1; mo[1] = 1000
63 ma[2] = 0; mb[2] = 1; mo[2] = 1000
64
65 // T7 NEG-CONTROL (before running): ignoring outcomes does NOT lead C(2).
66 let ord0: *i64 = sys_mmap(3 * 8) as *i64
67 el_rank(rat, 3, ord0)
68 var ok7: i64 = 0
69 if ord0[0] != 2 { ok7 = 1 }
70 gv_check("T7 neg-control ignoring outcomes does not lead C", ok7, ctr)
71
72 // run the tournament, then re-rank
73 el_run_tournament(rat, 3, ma, mb, mo, 3, 32, tab)
74 let ord1: *i64 = sys_mmap(3 * 8) as *i64
75 el_rank(rat, 3, ord1)
76 var ok8: i64 = 0
77 if ord1[0] == 2 { ok8 = 1 }
78 gv_check("T8 tournament ranks dominant player C first", ok8, ctr)
79 var ok9: i64 = 0
80 if ord1[0] == 2 { if ord1[1] == 0 { if ord1[2] == 1 { ok9 = 1 } } }
81 gv_check("T9 full order equals C A B", ok9, ctr)
82
83 let rc: i64 = gv_verdict("DR-ELO", ctr, "Elo table+update+tournament+rank, neg-control")
84 sys_exit(rc)
85 return rc
86}