| 1 | """Rule and strategic-policy regressions on the installed RLCard 1.2.0 engine.""" |
| 2 | import importlib.util |
| 3 | import pathlib |
| 4 | import sys |
| 5 | import unittest |
| 6 | |
| 7 | import rlcard |
| 8 | from rlcard.games.doudizhu.judger import DoudizhuJudger |
| 9 | |
| 10 | ROOT = pathlib.Path(__file__).resolve().parents[1] |
| 11 | sys.path.insert(0, str(ROOT / 'referee')) |
| 12 | from play import points |
| 13 | spec = importlib.util.spec_from_file_location('strategic', ROOT / 'field/strategic/agent.py') |
| 14 | strategy = importlib.util.module_from_spec(spec) |
| 15 | spec.loader.exec_module(strategy) |
| 16 | |
| 17 | |
| 18 | def state(hand, legal, trace=(), me=1, left=(5, 5, 5)): |
| 19 | return {'raw_legal_actions': legal, 'raw_obs': { |
| 20 | 'self': me, 'landlord': 0, 'current_hand': hand, |
| 21 | 'others_hand': '3456789TJQKA2BR', 'num_cards_left': list(left), |
| 22 | 'trace': list(trace), 'actions': legal}} |
| 23 | |
| 24 | |
| 25 | class Rules(unittest.TestCase): |
| 26 | def test_fixed_landlord_and_pass_reset(self): |
| 27 | env = rlcard.make('doudizhu') |
| 28 | env.seed(23) |
| 29 | s, p = env.reset() |
| 30 | self.assertEqual(p, 0) |
| 31 | self.assertEqual(s['raw_obs']['num_cards_left'], [20, 17, 17]) |
| 32 | self.assertNotIn('pass', s['raw_legal_actions']) |
| 33 | move = s['raw_obs']['current_hand'][0] |
| 34 | s, p = env.step(move, raw_action=True) |
| 35 | self.assertIn('pass', s['raw_legal_actions']) |
| 36 | s, p = env.step('pass', raw_action=True) |
| 37 | s, p = env.step('pass', raw_action=True) |
| 38 | self.assertEqual(p, 0) |
| 39 | self.assertNotIn('pass', s['raw_legal_actions']) |
| 40 | |
| 41 | def test_engine_combinations(self): |
| 42 | legal = DoudizhuJudger.playable_cards_from_hand('3334445566TJQKA2BR') |
| 43 | self.assertIn('3334445566', legal) |
| 44 | self.assertIn('TJQKA', legal) |
| 45 | self.assertIn('BR', legal) |
| 46 | self.assertNotIn('JQKA2', legal) |
| 47 | self.assertNotIn('333444BR', legal) |
| 48 | |
| 49 | def test_bombs_spring_and_zero_sum(self): |
| 50 | scores, bombs, spring = points([(0, '3333'), (1, 'pass'), (2, 'pass'), (0, 'BR')], True) |
| 51 | self.assertEqual((scores, bombs, spring), ([16, -8, -8], 2, True)) |
| 52 | scores, _, spring = points([(0, '3'), (1, '4')], False) |
| 53 | self.assertEqual(scores, [-4, 2, 2]) |
| 54 | self.assertTrue(spring) |
| 55 | self.assertEqual(sum(scores), 0) |
| 56 | |
| 57 | def test_farmer_does_not_overtake_partner_after_pass(self): |
| 58 | s = state('5A', ['5', 'A', 'pass'], [(2, '4'), (0, 'pass')], me=1) |
| 59 | self.assertEqual(strategy.Agent().step(s), 'pass') |
| 60 | self.assertEqual(strategy.target(s['raw_obs']['trace']), (2, '4')) |
| 61 | |
| 62 | def test_finish_takes_priority_over_partner(self): |
| 63 | self.assertEqual(strategy.Agent().step(state('AA', ['AA', 'pass'], [(2, '44')], me=1)), 'AA') |
| 64 | |
| 65 | def test_guard_landlord_with_one_card(self): |
| 66 | s = state('3AR', ['A', 'R', 'pass'], [(1, 'K')], me=2, left=(1, 3, 3)) |
| 67 | self.assertEqual(strategy.Agent().step(s), 'R') |
| 68 | |
| 69 | def test_plan_keeps_straight_and_plays_attachments(self): |
| 70 | hand = '3334456789' |
| 71 | s = state(hand, sorted(DoudizhuJudger.playable_cards_from_hand(hand)), me=0, left=(10, 17, 17)) |
| 72 | self.assertIn(strategy.Agent().step(s), ['33344', '56789']) |
| 73 | |
| 74 | def test_nibble_subset_check_does_not_borrow(self): |
| 75 | self.assertTrue(strategy.contains(strategy.mask('33344'), strategy.mask('334'))) |
| 76 | self.assertFalse(strategy.contains(strategy.mask('344'), strategy.mask('334'))) |
| 77 | |
| 78 | |
| 79 | if __name__ == '__main__': |
| 80 | unittest.main() |