def test_pick_moves(self): player = initialize_basic_player() root = player.root root.child_N[coords.to_flat(utils_test.BOARD_SIZE, (2, 0))] = 10 root.child_N[coords.to_flat(utils_test.BOARD_SIZE, (1, 0))] = 5 root.child_N[coords.to_flat(utils_test.BOARD_SIZE, (3, 0))] = 1 # move 81, or 361, or... Endgame. root.position.n = utils_test.BOARD_SIZE**2 # Assert we're picking deterministically self.assertTrue(root.position.n > player.temp_threshold) move = player.pick_move() self.assertEqual(move, (2, 0)) # But if we're in the early part of the game, pick randomly root.position.n = 3 self.assertFalse(player.root.position.n > player.temp_threshold) with unittest.mock.patch('random.random', lambda: .5): move = player.pick_move() self.assertEqual(move, (2, 0)) with unittest.mock.patch('random.random', lambda: .99): move = player.pick_move() self.assertEqual(move, (3, 0))
def test_parallel_tree_search(self): player = initialize_almost_done_player() # check -- white is losing. self.assertEqual(player.root.position.score(), -0.5) # initialize the tree so that the root node has populated children. player.tree_search(num_parallel=1) # virtual losses should enable multiple searches to happen simultaneously # without throwing an error... for i in range(5): player.tree_search(num_parallel=4) # uncomment to debug this test # print(player.root.describe()) # Search should converge on D9 as only winning move. flattened = coords.to_flat( utils_test.BOARD_SIZE, coords.from_kgs(utils_test.BOARD_SIZE, 'D9')) best_move = np.argmax(player.root.child_N) self.assertEqual(best_move, flattened) # D9 should have a positive value self.assertGreater(player.root.children[flattened].Q, 0) self.assertGreaterEqual(player.root.N, 20) # passing should be ineffective. self.assertLess(player.root.child_Q[-1], 0) # no virtual losses should be pending self.assertNoPendingVirtualLosses(player.root)
def _make_tf_example_from_pwc(board_size, position_w_context): features = features_lib.extract_features( board_size, position_w_context.position) pi = _one_hot(board_size, coords.to_flat( board_size, position_w_context.next_move)) value = position_w_context.result return make_tf_example(features, pi, value)
def test_do_not_explore_past_finish(self): probs = np.array([0.02] * (utils_test.BOARD_SIZE * utils_test.BOARD_SIZE + 1), dtype=np.float32) root = MCTSNode(utils_test.BOARD_SIZE, go.Position(utils_test.BOARD_SIZE)) root.select_leaf().incorporate_results(probs, 0, root) first_pass = root.maybe_add_child( coords.to_flat(utils_test.BOARD_SIZE, None)) first_pass.incorporate_results(probs, 0, root) second_pass = first_pass.maybe_add_child( coords.to_flat(utils_test.BOARD_SIZE, None)) with self.assertRaises(AssertionError): second_pass.incorporate_results(probs, 0, root) node_to_explore = second_pass.select_leaf() # should just stop exploring at the end position. self.assertEqual(node_to_explore, second_pass)
def test_select_leaf(self): flattened = coords.to_flat( utils_test.BOARD_SIZE, coords.from_kgs(utils_test.BOARD_SIZE, 'D9')) probs = np.array([.02] * (utils_test.BOARD_SIZE * utils_test.BOARD_SIZE + 1)) probs[flattened] = 0.4 root = MCTSNode(utils_test.BOARD_SIZE, SEND_TWO_RETURN_ONE) root.select_leaf().incorporate_results(probs, 0, root) self.assertEqual(root.position.to_play, go.WHITE) self.assertEqual(root.select_leaf(), root.children[flattened])
def test_make_dataset_from_sgf(self): with tempfile.NamedTemporaryFile() as sgf_file, \ tempfile.NamedTemporaryFile() as record_file: sgf_file.write(TEST_SGF.encode('utf8')) sgf_file.seek(0) preprocessing.make_dataset_from_sgf(utils_test.BOARD_SIZE, sgf_file.name, record_file.name) recovered_data = self.extract_data(record_file.name) start_pos = go.Position(utils_test.BOARD_SIZE) first_move = coords.from_sgf('fd') next_pos = start_pos.play_move(first_move) second_move = coords.from_sgf('cf') expected_data = [ (features.extract_features(utils_test.BOARD_SIZE, start_pos), preprocessing._one_hot( utils_test.BOARD_SIZE, coords.to_flat(utils_test.BOARD_SIZE, first_move)), -1), (features.extract_features(utils_test.BOARD_SIZE, next_pos), preprocessing._one_hot( utils_test.BOARD_SIZE, coords.to_flat(utils_test.BOARD_SIZE, second_move)), -1) ] self.assertEqualData(expected_data, recovered_data)
def play_move(self, c): """Play a move.""" # Notable side effects: # - finalizes the probability distribution according to # this roots visit counts into the class' running tally, `searches_pi` # - Makes the node associated with this move the root, for future # `inject_noise` calls. if not self.two_player_mode: self.searches_pi.append( self.root.children_as_pi( self.root.position.n < self.temp_threshold)) self.qs.append(self.root.Q) # Save our resulting Q. self.comments.append(self.root.describe()) self.root = self.root.maybe_add_child( coords.to_flat(self.board_size, c)) self.position = self.root.position # for showboard del self.root.parent.children return True # GTP requires positive result.
def test_dont_pass_if_losing(self): player = initialize_almost_done_player() # check -- white is losing. self.assertEqual(player.root.position.score(), -0.5) for i in range(20): player.tree_search() # uncomment to debug this test # print(player.root.describe()) # Search should converge on D9 as only winning move. flattened = coords.to_flat( utils_test.BOARD_SIZE, coords.from_kgs(utils_test.BOARD_SIZE, 'D9')) best_move = np.argmax(player.root.child_N) self.assertEqual(best_move, flattened) # D9 should have a positive value self.assertGreater(player.root.children[flattened].Q, 0) self.assertGreaterEqual(player.root.N, 20) # passing should be ineffective. self.assertLess(player.root.child_Q[-1], 0) # no virtual losses should be pending self.assertNoPendingVirtualLosses(player.root)