def start_board(): """ """ gs = GameState(size=7) gs.do_move((1, 1)) gs.do_move((2, 2)) return gs
def _sgf_init_gamestate(sgf_root): """ Helper function to set up a GameState object from the root node of an SGF file """ props = sgf_root.properties s_size = int(props.get('SZ', ['19'])[0]) s_player = props.get('PL', ['B'])[0] # init board with specified size gs = GameState(size=s_size) # handle 'add black' property if 'AB' in props: for stone in props['AB']: gs.place_handicap_stone(_parse_sgf_move(stone), go.BLACK) # handle 'add white' property if 'AW' in props: for stone in props['AW']: gs.place_handicap_stone(_parse_sgf_move(stone), go.WHITE) # setup done; set player according to 'PL' property gs.current_player = go.BLACK if s_player == 'B' else go.WHITE return gs
def setUp(self): self.gs = GameState() self.mcts = MCTSearch(policy_value_generator(random_policy, zero_value), config, max_playout=1)
class TestMCTS(unittest.TestCase): def setUp(self): self.gs = GameState() self.mcts = MCTSearch(policy_value_generator(random_policy, zero_value), config, max_playout=1) def _tree_traversal_helper(self, node): if node._children != {}: expansions = 1 else: return 0 for action, _ in sorted(random_policy(self.gs), key=itemgetter(1), reverse=True): if action in node._children: expansions += self._tree_traversal_helper( node._children[action]) return expansions def _count_expansions(self): """Helper function to count the number of expansions past the root using the dummy policy """ node = self.mcts._root return self._tree_traversal_helper(node) - 1 # remove root node count def test_playout(self): for _ in range(8): self.mcts._playout(self.gs.copy(), self.mcts._root) # for i in range(19): # for j in range(19): # if self.mcts._root._children[(i, j)].visit_count >= 1: # print((i, j), self.mcts._root._children[(i, j)].visit_count) self.mcts._playout(self.gs.copy(), self.mcts._root) # Assert that the most likely child was visited (according to the dummy policy below). self.assertEqual(1, self.mcts._root._children[(18, 18)].visit_count) # Assert that the search depth expanded nodes 8 times. self.assertEqual(8, self._count_expansions()) # def test_playout_with_pass(self): # # Test that playout handles the end of the game (i.e. passing/no moves). Mock this by # # creating a policy that returns nothing after 4 moves. # def stop_early_policy(state): # if state.turns <= 4: # return random_policy(state) # else: # return [] # # self.mcts = MCTSearch(policy_value_generator(stop_early_policy, zero_value), config, max_playout=8) # for _ in range(8): # self.mcts._playout(self.gs.copy(), self.mcts._root) # # Assert that (18, 18) and (18, 17) are still only visited once. # self.assertEqual(1, self.mcts._root._children[(18, 18)].visit_count) # # Assert that no expansions happened after reaching the "end" in 4 moves. # self.assertEqual(5, self._count_expansions()) def test_get_move(self): move = self.mcts.calc_move(self.gs) self.mcts.update_with_move(move) # success if no errors def test_update_with_move(self): move = self.mcts.calc_move(self.gs) self.gs.do_move(move) self.mcts.update_with_move(move) # Assert that the new root still has children. self.assertTrue(len(self.mcts._root._children) > 0) # Assert that the new root has no parent (the rest of the tree will be garbage collected). self.assertIsNone(self.mcts._root._parent) # Assert that the next best move according to the root is (18, 17), according to the # dummy policy below. self.assertEqual((18, 18), move) self.assertEqual((18, 17), self.mcts._root.select()[0])
def setUp(self): self.gs = GameState() self.node = MCTreeNode(None, 1.0)
def simple_board(): """ """ gs = GameState(size=7) # make a tiny board for the sake of testing and hand-coding expected results # # X # 0 1 2 3 4 5 6 # B W . . . . . 0 # B W . . . . . 1 # B . . . B . . 2 # Y . . . B k B . 3 # . . . W B W . 4 # . . . . W . . 5 # . . . . . . . 6 # # where k is a ko position (white was just captured) # ladder-looking thing in the top-left gs.do_move((0, 0)) # B gs.do_move((1, 0)) # W gs.do_move((0, 1)) # B gs.do_move((1, 1)) # W gs.do_move((0, 2)) # B # ko position in the middle gs.do_move((3, 4)) # W gs.do_move((3, 3)) # B gs.do_move((4, 5)) # W gs.do_move((4, 2)) # B gs.do_move((5, 4)) # W gs.do_move((5, 3)) # B gs.do_move((4, 3)) # W - the ko position gs.do_move((4, 4)) # B - does the capture return gs
def play(self, save_path, save_filename, is_selfplay=False): """ Plays the game and saves to a sgf file. """ mcts1 = MCTSearch(random_state_transform, lambda s: self.player1.response([s])) mcts2 = MCTSearch(random_state_transform, lambda s: self.player2.response([s])) mcts = [mcts1, mcts2] gs = GameState(size=self.board_size) # Randomly assign colors player1_is_black = random.randint(0, 1) == 0 # player 1 refers to mcts[0] if player1_is_black: current_player = 0 else: current_player = 1 # Create a list to store search probabilities for self-play # if is_selfplay is not necessary, creating a list won't have side effects search_probs_history = [] # Play the game for turn in range(self.max_moves): # Whether to enable exploration depends on the mode, # exploration is only enabled in the first 30 turns in a self-play if is_selfplay and turn + 1 <= 30: dirichlet = True else: dirichlet = False # Record search probabilities for self-play if is_selfplay: move, search_probs = mcts[current_player].calc_move_with_probs( gs, dirichlet) search_probs_history.append(search_probs) else: move = mcts[current_player].calc_move(gs, dirichlet) # Make the move and update both player's search tree # TODO: Should we use a single search tree for self-play? gs.do_move(move) for p in range(2): mcts[p].update_with_move(gs) # Toggle player current_player = 1 - current_player # Check if the game ends if gs.is_end_of_game: break # Game ends result = gs.get_winner() # TODO: Detailed result info, i.e. W+Resign if result is WHITE: result_string = "W+" elif result is BLACK: result_string = "B+" else: # TODO: How should we deal with ties? discarding ties for now return 0 save_gamestate_to_sgf(gs, save_path, save_filename + '.sgf', result=result_string) # Return an extra list of search probabilities in the same order, other features can be extracted in sgf # search_probs_history is a list of a list of (action, probs) if is_selfplay: return player1_is_black, result, search_probs_history else: return player1_is_black, result
def empty_board(): """ """ gs = GameState(size=7) return gs