def test_noughts_and_crosses_instances_have_correct_size( self, size, mocker): mock_game = mocker.MagicMock() rows, columns = size NoughtsAndCrosses.__init__(mock_game, *size) assert mock_game.rows == rows assert mock_game.columns == columns
def test_display_function_outputs_correct_string_for_3x3( self, state, expected_output, capsys, mocker): mock_game = mocker.MagicMock( rows=3, columns=3, _actions_to_binary=actions_to_binary_list[0]) NoughtsAndCrosses.display(mock_game, state) output = capsys.readouterr().out assert output == expected_output
def test_legal_actions_raises_exception_on_terminal_input_state( self, mocker): mock_game = mocker.MagicMock() mock_game.is_terminal = mocker.MagicMock(return_value=True) mock_state = mocker.MagicMock() with pytest.raises(ValueError) as exception_info: NoughtsAndCrosses.legal_actions(mock_game, mock_state) assert str( exception_info.value) == ("Legal actions can not be computed" " for a terminal state.")
def test_utility_raises_exception_on_non_terminal_input_state( self, size, state, mocker): rows, columns = size mock_is_terminal = mocker.MagicMock(return_value=False) mock_game = mocker.MagicMock(rows=rows, columns=columns, is_terminal=mock_is_terminal) with pytest.raises(ValueError) as exception_info: NoughtsAndCrosses.utility(mock_game, state) assert str(exception_info.value) == ("Utility can not be calculated " "for a non-terminal state.") mock_is_terminal.assert_called_once_with(state)
def test_random_noughts_and_crosses_player_gives_equal_action_probabilities(): nac = NoughtsAndCrosses() player = RandomPlayer(game=nac) action, action_probs = player.choose_action(nac.initial_state, return_probabilities=True) next_states = nac.legal_actions(nac.initial_state) expected_action_probs = { action: 1 / len(next_states) for action in next_states.keys() } for action in expected_action_probs.keys(): np.testing.assert_almost_equal(action_probs[action], expected_action_probs[action])
def test_is_terminal_returns_false_for_non_terminal_states( self, size, actions_to_binary, win_bitmasks, state, mocker): rows, columns = size mock_game = mocker.MagicMock(rows=rows, columns=columns, _actions_to_binary=actions_to_binary, _win_bitmasks=win_bitmasks) assert NoughtsAndCrosses.is_terminal(mock_game, state) is False
def test_utility_function_returns_correct_outcomes(self, size, win_bitmasks, state, outcome, mocker): rows, columns = size mock_game = mocker.MagicMock(rows=rows, columns=columns, _win_bitmasks=win_bitmasks) assert NoughtsAndCrosses.utility(mock_game, state) == outcome
def test_calculating_row_win_bitmasks(self, size, actions_to_binary, win_bitmasks, mocker): rows, columns = size mock_game = mocker.MagicMock(rows=rows, columns=columns, _actions_to_binary=actions_to_binary) row_win_bitmasks = NoughtsAndCrosses._calculate_row_bitmasks(mock_game) print([(bin(x), bin(y)) for x, y in zip(row_win_bitmasks, win_bitmasks["row"])]) assert row_win_bitmasks == win_bitmasks["row"]
def test_generating_a_dict_of_all_possible_next_states( self, size, actions_to_binary, state, player, expected_states, mocker): # TODO: need to split this into two tests: one testing the _next_state function and one testing legal actions rows, columns = size mock_game = mocker.MagicMock(rows=rows, columns=columns, _actions_to_binary=actions_to_binary) mock_game.is_terminal = mocker.MagicMock(return_value=False) mock_game.current_player = mocker.MagicMock(return_value=player) assert NoughtsAndCrosses.legal_actions(mock_game, state) == expected_states
def test_mcts_noughts_and_crosses_player_gives_optimal_moves( state, optimal_actions): # seed the random number generator. np.random.seed(0) nac = NoughtsAndCrosses() estimator = create_trivial_estimator(nac) player = MCTSPlayer(game=nac, estimator=estimator, mcts_iters=100, c_puct=0.5, tau=1) action, action_probs = player.choose_action(state, return_probabilities=True) print(action_probs) assert max(action_probs, key=action_probs.get) in optimal_actions
def test_action_to_binary_is_correct(self, size, actions_to_binary, mocker): mock_game = mocker.MagicMock() NoughtsAndCrosses.__init__(mock_game, *size) assert mock_game._actions_to_binary == actions_to_binary
def test_initial_state_is_correct(self, size, mocker): mock_game = mocker.MagicMock() NoughtsAndCrosses.__init__(mock_game, *size) assert mock_game.initial_state == (0, 0, 1)
def test_current_player_returns_correct_player(self, player, state, mocker): mock_game = mocker.MagicMock() assert NoughtsAndCrosses.current_player(mock_game, state) == player
"""This program plays noughts and crosses using Monte Carlo Tree Search and a trivial evaluator. For nonterminal states, the evaluator returns the uniform probability distribution over available actions and a value of 0. In a terminal state, we back up the utility returned by the game. """ import numpy as np from alphago.games.noughts_and_crosses import NoughtsAndCrosses from alphago.estimator import create_trivial_estimator from alphago.player import MCTSPlayer if __name__ == "__main__": nac = NoughtsAndCrosses() evaluator = create_trivial_estimator(nac.legal_actions) state = nac.INITIAL_STATE computer_player_no = np.random.choice([1, 2]) computer_player = MCTSPlayer(nac, evaluator, mcts_iters=2000, c_puct=0.5, tau=0.01) human_player_no = 1 if computer_player_no == 2 else 2 print("You are player: {}".format(human_player_no)) while not nac.is_terminal(state): player_no = nac.current_player(state) next_states = nac.legal_actions(state) if player_no == computer_player_no: action = computer_player.choose_action(state) computer_player.update(action) print("Taking action: {}".format(action))