def test_whenMainBoardIsFinishedThenGetValidBoardsIsEmpty(): main_board = MainBoard() main_board = force_sub_board_win(main_board, 0, 0, Player.ME) main_board = force_sub_board_win(main_board, 1, 1, Player.ME) main_board = force_sub_board_win(main_board, 2, 2, Player.ME) assert len(main_board.get_playable_coords()) == 0
def test_whenSubBoardsArePlayedThenGetSubBoardReturnsCorrectly(): main_board = MainBoard(3) main_board = force_sub_board_win(main_board, 0, 0, Player.ME) sub_board = main_board.get_sub_board(MainBoardCoords(0, 0)) assert (sub_board.is_finished) assert (sub_board.winner == Player.ME) other_sub_board = main_board.get_sub_board(MainBoardCoords(0, 1)) assert (not other_sub_board.is_finished)
def test_row_col_checks_work(): main_board = MainBoard() for game_event in get_game_events('engine/tests/logs/diag_fail.gamelog'): main_board = main_board._add_move(game_event[0], game_event[1], game_event[2]) assert is_diagonal_won(main_board._board[0][1]._board, Player.OPPONENT) is True assert is_col_won(main_board._board[0][2]._board, MainBoardCoords(0, 1), Player.ME) is True assert is_row_won(main_board._board[2][1]._board, MainBoardCoords(1, 1), Player.ME) is True
def __init__(self): super().__init__( ) #means: get all initialisation attributes from base class self.children = [] self.visited = True self.plays = 0 self.wins = 0 self.player = 1 self.board_state = MainBoard( 3) #should be an array storing the game state self.sub_board = MonteCarlo.pick_next_main_board_coords(board_state)
def test_whenBoardInitializedThenAllSubBoardsInitialized(): board = MainBoard(3)._board assert len(board) == 3 # Rows for row in board: assert len(row) == 3 # Columns for sub_board in row: assert str(sub_board) == '0 0 0 \n0 0 0 \n0 0 0 \n'
def test_whenAllSubBoardsAreFinishedThenMainBoardIsFinished(): main_board = MainBoard(3) for row in main_board._board: for sub_board in row: sub_board._is_finished = True assert main_board.is_finished == True
def test_whenNextBoardIsFinishedThenGetValidBoardsReturnsAllAvailableBoards(): main_board = MainBoard() # Force some sub_board plays to finish a board finished_sub_board = main_board._board[2][2] \ .add_my_move(SubBoardCoords(0, 0)) \ .add_my_move(SubBoardCoords(1, 1)) \ .add_my_move(SubBoardCoords(2, 2)) # Set that sub-board where the sub_board_next_player_must_play will be main_board._board[2][2] = finished_sub_board # Play a move that will make the finished board the next board (Move 2, 2) main_board = main_board.add_my_move(MainBoardCoords(0, 0), SubBoardCoords(2, 2)) # Playing anywhere is now allowed valid_boards = main_board.get_playable_coords() assert len(valid_boards) == 8 assert valid_boards == [MainBoardCoords(0, 0), MainBoardCoords(0, 1), MainBoardCoords(0, 2), MainBoardCoords(1, 0), MainBoardCoords(1, 1), MainBoardCoords(1, 2), MainBoardCoords(2, 0), MainBoardCoords(2, 1)]
def test_whenRowOfSubBoardsIsWonThenMainBoardIsWon(): # LTR Diag check ltr_check = MainBoard() ltr_check = force_sub_board_win(ltr_check, 0, 0, Player.ME) ltr_check = force_sub_board_win(ltr_check, 1, 1, Player.ME) ltr_check = force_sub_board_win(ltr_check, 2, 2, Player.ME) print(str(ltr_check)) assert ltr_check.is_finished == True assert ltr_check.winner == Player.ME # RTL Diag check rtl_check = MainBoard() rtl_check = force_sub_board_win(rtl_check, 0, 2, Player.OPPONENT) rtl_check = force_sub_board_win(rtl_check, 1, 1, Player.OPPONENT) rtl_check = force_sub_board_win(rtl_check, 2, 0, Player.OPPONENT) assert rtl_check.is_finished == True assert rtl_check.winner == Player.OPPONENT # Row check row_check = MainBoard() row_check = force_sub_board_win(row_check, 0, 0, Player.ME) row_check = force_sub_board_win(row_check, 1, 0, Player.ME) row_check = force_sub_board_win(row_check, 2, 0, Player.ME) assert row_check.is_finished == True assert row_check.winner == Player.ME # Col check col_check = MainBoard() col_check = force_sub_board_win(col_check, 0, 2, Player.OPPONENT) col_check = force_sub_board_win(col_check, 1, 2, Player.OPPONENT) col_check = force_sub_board_win(col_check, 2, 2, Player.OPPONENT) assert col_check.is_finished == True assert col_check.winner == Player.OPPONENT
def test_whenAllSubBoardsFinishedWithNoWinnerThenResultIsATie(): main_board = MainBoard(3) main_board = force_sub_board_win(main_board, 0, 0, Player.ME) main_board = force_sub_board_win(main_board, 1, 1, Player.OPPONENT) main_board = force_sub_board_tie(main_board, 2, 2) main_board = force_sub_board_tie(main_board, 0, 1) main_board = force_sub_board_tie(main_board, 1, 0) main_board = force_sub_board_tie(main_board, 1, 2) main_board = force_sub_board_tie(main_board, 2, 1) main_board = force_sub_board_win(main_board, 0, 2, Player.ME) main_board = force_sub_board_win(main_board, 2, 0, Player.OPPONENT) assert main_board.is_finished == True assert main_board.winner == Player.NONE
def test_whenBoardIsPrettyPrintedThenItIsRenderedCorrectly(): string_board = str(MainBoard(3).add_my_move(MainBoardCoords(0, 0), SubBoardCoords(1, 1)) .add_opponent_move(MainBoardCoords(1, 1), SubBoardCoords(2, 2)) .add_opponent_move(MainBoardCoords(2, 2), SubBoardCoords(0, 0))) assert string_board == "0 0 0 | 0 0 0 | 0 0 0 \n" \ "0 1 0 | 0 0 0 | 0 0 0 \n" \ "0 0 0 | 0 0 0 | 0 0 0 \n" \ "- - - | - - - | - - - \n" \ "0 0 0 | 0 0 0 | 0 0 0 \n" \ "0 0 0 | 0 0 0 | 0 0 0 \n" \ "0 0 0 | 0 0 2 | 0 0 0 \n" \ "- - - | - - - | - - - \n" \ "0 0 0 | 0 0 0 | 2 0 0 \n" \ "0 0 0 | 0 0 0 | 0 0 0 \n" \ "0 0 0 | 0 0 0 | 0 0 0 \n"
def test_whenNextBoardIsFinishedThenAnyBoardCanBePlayed(): main_board = MainBoard() # Force some sub_board plays to finish a board finished_sub_board = main_board._board[2][2] \ .add_my_move(SubBoardCoords(0, 0)) \ .add_my_move(SubBoardCoords(1, 1)) \ .add_my_move(SubBoardCoords(2, 2)) # Set that sub-board where the sub_board_next_player_must_play will be main_board._board[2][2] = finished_sub_board # Play a move that will make the finished board the next board (Move 2, 2) main_board = main_board.add_my_move(MainBoardCoords(0, 0), SubBoardCoords(2, 2)) # Playing anywhere is now allowed assert main_board.sub_board_next_player_must_play == None assert main_board.is_playing_on_sub_board_allowed(MainBoardCoords(1, 1)) == True main_board.add_opponent_move(MainBoardCoords(0, 0), SubBoardCoords(1, 1))
def test_whenNextBoardIsAvailableThenGetValidBoardsReturnsOnlyThatBoard(): board = MainBoard().add_my_move(MainBoardCoords(0, 0), SubBoardCoords(2, 2)) # Only valid board now should be 2, 2 assert len(board.get_playable_coords()) == 1 assert board.get_playable_coords()[0] == MainBoardCoords(2, 2)
def test_whenNoGivenSizeThenSizeIs3(): board = MainBoard()._board assert len(board) == 3 # Rows for row in board: assert len(row) == 3 # Columns
def test_whenNewMoveIsOutsideValidSubBoardBoundsThenExceptionRaised(): with pytest.raises(MoveOutsideSubBoardError): MainBoard().add_my_move(MainBoardCoords(1, 1), SubBoardCoords(1, 3))
def test_whenNewMoveIsNotOnGameNextBoardThenExceptionRaised(): board = MainBoard().add_my_move(MainBoardCoords(0, 0), SubBoardCoords(1, 1)) # Move must now be on board at 1, 1 with pytest.raises(MoveNotOnNextBoardError): board.add_opponent_move(MainBoardCoords(1, 0), SubBoardCoords(1, 1))
def test_whenNewMoveSubBoardCoordsAreOutOfBoundsThenExceptionRaised(): with pytest.raises(MoveOutsideMainBoardError): MainBoard().add_my_move(MainBoardCoords(1, 3), SubBoardCoords(0, 0)) with pytest.raises(MoveOutsideMainBoardError): MainBoard().add_opponent_move(MainBoardCoords(3, 1), SubBoardCoords(0, 0))
def test_whenInitSizeIsStringThenExceptionRaised(): with pytest.raises(ValueError): MainBoard("mustFail")
def test_whenBoardNewThenBoardIsNotFinished(): assert MainBoard().is_finished == False
def test_whenInitSizeNot3ExceptionRaised(): with pytest.raises(ValueError): MainBoard(4) with pytest.raises(ValueError): MainBoard(2)
def test_whenMainBoardIsNotFinishedThenWinnerCheckRaisesException(): with pytest.raises(BoardNotFinishedError): MainBoard().winner
def test_whenMainBoardIsFinishedThenNewMoveRaisesException(): main_board = MainBoard() main_board._is_finished = True with pytest.raises(MoveInFinishedBoardError): main_board.add_my_move(MainBoardCoords(1, 1), SubBoardCoords(1, 1))
class MonteCarlo(StdOutPlayer): def __init__(self): super().__init__( ) #means: get all initialisation attributes from base class self.children = [] self.visited = True self.plays = 0 self.wins = 0 self.player = 1 self.board_state = MainBoard( 3) #should be an array storing the game state self.sub_board = MonteCarlo.pick_next_main_board_coords(board_state) def CreateChildren(self): legal_moves = list( self.board_state.get_sub_board.get_playable_coords()) for move in legal_moves: self.children.append(Node(self.board_state.add_my_move(move))) def get_my_move(self): # -> Tuple[MainBoardCoords, SubBoardCoords main_board_coords = self.pick_next_main_board_coords() sub_board = self.main_board.get_sub_board(main_board_coords) sub_board_coords = self.pick_random_sub_board_coords(sub_board) return main_board_coords, sub_board_coords def pick_next_main_board_coords(self) -> MainBoardCoords: if self.main_board.sub_board_next_player_must_play is None: return random.choice(self.main_board.get_playable_coords()) else: return self.main_board.sub_board_next_player_must_play @staticmethod def pick_random_sub_board_coords( sub_board: SubBoard ) -> SubBoardCoords: #this means: argument should be of type 'SubBoard' and # #do we need to pass legal moves now so we can make the kids?) self.CreateChildren() print(main_board) return monte_carlo_tree_search(self) def monte_carlo_tree_search(node): begin = datetime.datetime.utcnow() while datetime.datetime.utcnow() - begin < datetime.timedelta( milliseconds=4.9): #if all root node's children are visited: if all(child.visited for child in node.children): for child in node.children: print(child.visited) #next node to explore is one with max UCT return max_uct(node) #this returns the best poss next move #if some unvisited: else: node.run_simulation(node) return max_uct(node) def run_simulation(node): pdb.set_trace() print('mainboard:' + self.main_board) if self.MainBoard.is_finished(): ### node.plays += 1 #shouldn't be self.? if MainBoard.winner == 1: node.wins += 1 return node.plays, node.wins elif MainBoard.winner == 2: node.wins -= 1 return node.plays, node.wins else: #draw return node.plays #just return result? don't need three if statements else: node.CreateChildren() next_child = random.choice(node.children) print('next child:', next_child) node.visited = True node.wins = wins + run_simulation(next_child) print('wins:', node.wins) #make this wins, play = ? def max_uct(node, c=1.4): for child in node.children: print('wins:', child.wins, 'plays:', child.plays) uct_vals = [ (child.wins / (child.plays)) + c * np.sqrt( (2 * np.log(node.wins) / (node.plays))) for child in node.children ] #first terms = ratio wins of child, second term is total ratio wins return 'hello' #node.children[np.argmax(uct_vals)]