test_root_policy.py
python · 92 lines · Exact bytes
Version & provenance · data-cm7
Recorded SHA-256 7030d8a401bf376ca48542a8ae7148bde7e8b4161ed508ac5fe8090a5b3b9058Rendered SHA-256 7030d8a401bf376ca48542a8ae7148bde7e8b4161ed508ac5fe8090a5b3b9058
Apache-2.0 original project code; dependencies retain their own licenses. License notes. Line links are stable within this snapshot. CM-7 completed implementation source; original and public rendered hashes recorded. Host-specific paths use /work placeholders; inspect reproduction instructions before running. Separate Rust helper retains its GPL license.
1import random,unittest
2from root_policy import *
3import chess.syzygy
4from acquire_inputs import ROOT
5
6class FixedTB:
7 def __init__(self,w,d):self.w=w;self.d=d
8 def probe_wdl(self,b):return self.w
9 def probe_dtz(self,b):return self.d
10
11def long_history(n):
12 for attempt in range(20):
13 rng=random.Random(28+attempt)
14 b=c.chess.Board('8/8/8/8/8/4K3/R7/7k w - - 0 1');seen={c.position_key(b)}
15 for _ in range(n):
16 options=[]
17 for m in b.legal_moves:
18 if b.is_zeroing(m):continue
19 child=b.copy(stack=True);child.push(m)
20 if child.is_game_over(claim_draw=False) or c.position_key(child) in seen:continue
21 options.append(m)
22 if not options:break
23 b.push(rng.choice(options));seen.add(c.position_key(b))
24 if len(b.move_stack)==n:return b
25 raise AssertionError('fixture could not construct history')
26
27class PolicyTests(unittest.TestCase):
28 @classmethod
29 def setUpClass(cls):cls.tb=chess.syzygy.open_tablebase(str(ROOT),max_fds=32)
30 @classmethod
31 def tearDownClass(cls):cls.tb.close()
32 def test_wdl_dtz_sign_and_cursed_blessed(self):
33 for w,d,v in [(-2,-1,-1),(-1,-101,0),(0,0,0),(1,101,0),(2,100,1)]:
34 check_probe(w,d);self.assertEqual(wdl_outcome(w),v)
35 for w,d in [(2,-4),(-2,4),(0,1),(1,100),(-1,-99),(2,101)]:
36 with self.assertRaises(ValueError):check_probe(w,d)
37 b=c.chess.Board('8/8/8/8/8/4K3/R7/7k w - - 0 1');b.push_uci('a2a3')
38 for w,d in [(1,101),(-1,-101)]:self.assertEqual(Solver(FixedTB(w,d)).value(b,2).lo,0)
39 def test_unknown_and_truncated_history_excluded(self):
40 b=long_history(6)
41 with self.assertRaises(Unresolved):root_policy(self.tb,b)
42 with self.assertRaises(Unresolved):root_policy(self.tb,c.chess.Board(b.fen()),history_known=True)
43 validate_history(b,True)
44 def test_corrupt_history_clock_rejected(self):
45 b=long_history(6);b.halfmove_clock=5
46 with self.assertRaises((ValueError,Unresolved)):validate_history(b,True)
47 def test_high_counter_claim_and_board_unchanged(self):
48 b=long_history(99);before=b.fen();self.assertTrue(b.can_claim_fifty_moves())
49 v=Solver(self.tb).value(b,1)
50 self.assertEqual((v.lo,v.hi),(0,0))
51 r=root_policy(self.tb,b,history_known=True,depth=1)
52 self.assertTrue(r['draw_claim_optimal']);self.assertFalse(r['move_only_ready']);self.assertEqual(before,b.fen())
53 def test_known_repetition_disables_margin(self):
54 b=c.chess.Board('8/8/8/8/8/4K3/R7/7k b - - 0 1')
55 for u in ['h1g1','a2a3','g1h1','a3a2']*2:b.push_uci(u)
56 validate_history(b,True);self.assertFalse(no_repetition_since_zeroing(b));self.assertTrue(b.can_claim_threefold_repetition())
57 r=root_policy(self.tb,b,history_known=True,depth=1)
58 self.assertEqual(r['outcome_interval'],[0,0]);self.assertFalse(r['move_only_ready'])
59 def test_queen_ties_and_perspective(self):
60 b=c.chess.Board('7k/8/5KQ1/8/8/8/8/8 w - - 0 1')
61 r=root_policy(self.tb,b,history_known=True)
62 self.assertEqual(r['outcome_interval'],[1,1]);self.assertTrue(r['move_only_ready']);self.assertGreater(len(r['preserving_moves']),1)
63 for m in r['moves']:
64 child=b.copy();child.push_uci(m['move'])
65 if child.is_stalemate():self.assertEqual((m['lo'],m['hi']),(0,0))
66 if child.is_checkmate():self.assertEqual((m['lo'],m['hi']),(1,1))
67 def test_zeroing_promotions_and_underpromotions(self):
68 b=c.chess.Board('7k/P7/6K1/8/8/8/8/8 w - - 0 1')
69 r=root_policy(self.tb,b,history_known=True)
70 promotion=[m for m in r['moves'] if len(m['move'])==5];self.assertEqual(len(promotion),4)
71 for m in promotion:
72 child=b.copy();child.push_uci(m['move']);self.assertEqual(m['child_halfmove_clock'],0)
73 self.assertEqual(m['lo'],-wdl_outcome(self.tb.probe_wdl(child)))
74 def test_stalemate_and_mate_override_counter(self):
75 for fen,v in [('7k/5Q2/6K1/8/8/8/8/8 b - - 150 100',0),('7k/6Q1/5K2/8/8/8/8/8 b - - 150 100',-1)]:
76 b=c.chess.Board(fen);r=Solver(self.tb).value(b,0);self.assertEqual((r.lo,r.hi),(v,v))
77 def test_budget_withholds_unresolved_ties(self):
78 b=c.chess.Board('7k/8/5KQ1/8/8/8/8/8 w - - 0 1')
79 r=root_policy(self.tb,b,history_known=True,max_nodes=1)
80 self.assertFalse(r['move_only_ready']);self.assertTrue(r['unresolved_ties']);self.assertLessEqual(r['nodes'],1)
81 def test_interval_negation_property(self):
82 for lo in (-1,0,1):
83 for hi in range(lo,2):
84 v=Interval(lo,hi,'test');n=negate(negate(v));self.assertEqual((n.lo,n.hi),(lo,hi))
85 def test_real_cursed_and_blessed_fixtures(self):
86 for fen,wdl,dtz in [('8/1N6/3p4/1N6/8/5k2/8/2K5 b - - 0 1',-1,-103),('2N5/7p/8/8/8/4k3/8/4N1K1 w - - 0 1',1,106)]:
87 b=c.chess.Board(fen);self.assertEqual((self.tb.probe_wdl(b),self.tb.probe_dtz(b)),(wdl,dtz))
88 v=Solver(self.tb).value(b,0);self.assertEqual((v.lo,v.hi),(0,0))
89 with self.assertRaises(Unresolved):root_policy(self.tb,b)
90
91if __name__=='__main__':unittest.main(verbosity=2)
92