LittlePlyResearch notebook

test_root_policy.py

python · 92 lines · Exact bytes

Line 1 ↗
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