Example #1
0
def test_players(player1: Player, player2: Player, iterations: int) -> None:
    """
    test_players is a function that runs <iterations> number of games between player1
    and player2
    """
    white = 0
    black = 0
    ties = 0
    for _ in range(iterations):
        game = ReversiGame()
        prev_move = (-1, -1)
        while game.get_winner() is None:
            move = player1.make_move(game, prev_move)
            game.try_make_move(move)
            if game.get_winner() is None:
                prev_move = player2.make_move(game, move)
                game.try_make_move(prev_move)
        if game.get_winner() == 'white':
            print('White WINS')
            white += 1
        elif game.get_winner() == 'black':
            print('Black WINS')
            black += 1
        else:
            print('TIE')
            ties += 1
    print("Player 1 Wins: " + str(black))
    print("Player 2 Wins: " + str(white))
Example #2
0
def _run_ai_simulation(game_surface: pygame.Surface, size: int,
                       player1: Union[MobilityTreePlayer, PositionalTreePlayer, RandomPlayer,
                                      ReversiGame, MCTSTimeSavingPlayer],
                       player2: Union[MobilityTreePlayer, PositionalTreePlayer, RandomPlayer,
                                      ReversiGame, MCTSTimeSavingPlayer]) -> None:
    if size == 8:
        background = pygame.image.load('assets/gameboard8.png')
    elif size == 6:
        background = pygame.image.load('assets/gameboard6.png')
    else:
        raise ValueError("invalid size.")
    game_surface.blit(background, (0, 0))
    pygame.display.flip()
    game = ReversiGame(size)
    previous_move = '*'
    board = game.get_game_board()
    _draw_game_state(game_surface, background, size, board)
    pass_move = pygame.image.load('assets/pass.png')
    player1_side = BLACK
    while game.get_winner() is None:
        if previous_move == '*' or game.get_current_player() == player1_side:
            move = player1.make_move(game, previous_move)
        else:
            move = player2.make_move(game, previous_move)
        previous_move = move
        game.make_move(move)
        if move == 'pass':
            surface = game_surface
            game_surface.blit(pass_move, (300, 300))
            pygame.display.flip()
            pygame.time.wait(500)
            game_surface.blit(surface, (0, 0))
            pygame.display.flip()
        else:
            board = game.get_game_board()
            _draw_game_state(game_surface, background, size, board)
        pygame.time.wait(500)
    winner = game.get_winner()
    if winner == BLACK:
        victory = pygame.image.load('assets/player1_victory.png')
        game_surface.blit(victory, (300, 300))
        pygame.display.flip()
        pygame.time.wait(3000)
        return
    elif winner == WHITE:
        defeat = pygame.image.load('assets/player2_victory.png')
        game_surface.blit(defeat, (300, 300))
        pygame.display.flip()
        pygame.time.wait(3000)
        return
    else:
        draw = pygame.image.load('assets/draw.png')
        game_surface.blit(draw, (300, 300))
        pygame.display.flip()
        pygame.time.wait(3000)
        return
Example #3
0
    def _value_eval(self, game: ReversiGame, piece: str) -> Union[float, int]:
        """The evaluation function for minimax. For Positional Player, the evaluation function
        will evaluate the positional advantage of the pieces on the board.

        Preconditions:
            - piece in {BLACK, WHITE}

        :param game: the current game state for evaluation
        :return: value evaluated from the current game state
        """
        if game.get_winner() is not None:
            if game.get_winner() == piece:  # win
                return math.inf
            elif game.get_winner() == 'Draw':  # draw
                return 0
            else:  # lose
                return -math.inf
        else:
            num_black, num_white = game.get_num_pieces(
            )[BLACK], game.get_num_pieces()[WHITE]
            corner_black, corner_white = check_corners(game)
            board_filled = (num_black + num_white) / (game.get_size()**2)

            if piece == BLACK:
                if board_filled < 0.80:  # early to middle game
                    return 10 * (corner_black - corner_white) + len(
                        game.get_valid_moves())
                else:  # end game
                    return num_black / num_white
            else:
                if board_filled < 0.80:  # early to middle game
                    return 10 * (corner_white - corner_black) + len(
                        game.get_valid_moves())
                else:  # end game
                    return num_white / num_black
Example #4
0
def run_game(black: Player,
             white: Player,
             size: int,
             verbose: bool = False) -> tuple[str, list[str]]:
    """Run a Reversi game between the two given players.
    Return the winner and list of moves made in the game.
    """
    game = ReversiGame(size)

    move_sequence = []
    previous_move = None
    timer = {BLACK: 0, WHITE: 0}
    current_player = black

    if verbose:
        game.print_game()

    while game.get_winner() is None:
        t0 = time.time()  # record time before player make move
        previous_move = current_player.make_move(game, previous_move)
        t = time.time()  # record time after player make move

        game.make_move(previous_move)
        move_sequence.append(previous_move)
        if verbose:
            if current_player is black:
                print(f'{BLACK} moved {previous_move}. Used {t - t0:.2f}s')
            else:
                print(f'{WHITE} moved {previous_move}. Used {t - t0:.2f}s')
            game.print_game()

        if current_player is black:
            timer[BLACK] += t - t0
            current_player = white
        else:
            timer[WHITE] += t - t0
            current_player = black

    # print winner
    if verbose:
        print(f'Winner: {game.get_winner()}')
        print(
            f'{BLACK}: {game.get_num_pieces()[BLACK]}, {WHITE}: {game.get_num_pieces()[WHITE]}'
        )
        print(
            f'{BLACK} used {timer[BLACK]:.2f}s, {WHITE} used {timer[WHITE]:.2f}s'
        )

    return game.get_winner(), move_sequence
Example #5
0
def check_corners(game: ReversiGame) -> tuple[int, int]:
    """Return a tuple representing the number of corner taken by each side

    :param game: the game state to be checked
    :return: (corner taken by black, corner taken by white)
    """
    board = game.get_game_board()
    corner_black, corner_white = 0, 0
    for i in [0, game.get_size() - 1]:
        for j in [0, game.get_size() - 1]:
            if board[i][j] == BLACK:
                corner_black += 1
            elif board[i][j] == WHITE:
                corner_white += 1
    return corner_black, corner_white
Example #6
0
    def _minimax(self, root_move: tuple[int, int], depth: int,
                 game: ReversiGame, alpha: float, beta: float) -> GameTree:
        """
        _minimax is a minimax function with alpha-beta pruning implemented that returns
        a full GameTree where each node stores the given evaluation

        Preconditions
            - depth >= 0
        """
        white_move = (game.get_current_player() == -1)
        ret = GameTree(move=root_move, is_white_move=white_move)
        # early return at max depth
        if depth == self.depth:
            ret.evaluation = heuristic(game, self.heuristic_list)
            return ret
        possible_moves = list(game.get_valid_moves())
        if not possible_moves:
            if game.get_winner() == 'white':
                ret.evaluation = 10000
            elif game.get_winner() == 'black':
                ret.evaluation = -10000
            else:
                ret.evaluation = 0
            return ret
        random.shuffle(possible_moves)
        best_value = float('-inf')
        if not white_move:
            best_value = float('inf')
        for move in possible_moves:
            new_game = game.copy_and_make_move(move)
            new_tree = self._minimax(move, depth + 1, new_game, alpha, beta)
            ret.add_subtree(new_tree)
            # we update the alpha value when the maximizer is playing (white)
            if white_move and best_value < new_tree.evaluation:
                best_value = new_tree.evaluation
                alpha = max(alpha, best_value)
                if beta <= alpha:
                    break
            # we update the beta value when the minimizer is playing (black)
            elif not white_move and best_value > new_tree.evaluation:
                best_value = new_tree.evaluation
                beta = min(beta, best_value)
                if beta <= alpha:
                    break
        ret.evaluation = best_value
        return ret
Example #7
0
def player_to_string(game: reversi.ReversiGame, player_colour: str, player: ai_players.Player) \
        -> str:
    """ Returns the string representation of the type of the player.

    Preconditions:
        - player_colour in {'white', 'black'}
    """
    if game.get_human_player() == 1 and player_colour == 'black':
        return 'Human'
    elif game.get_human_player() == -1 and player_colour == 'white':
        return 'Human'
    else:
        # the player is one of the AI players
        if isinstance(player, ai_players.RandomPlayer):
            return 'Random Moves'
        elif (isinstance(player, ai_players.MinimaxPlayer)
              or isinstance(player, ai_players.MinimaxABPlayer)):
            return 'Minimax ' + str(player.depth)
Example #8
0
    def __init__(self, window: VisualReversi, size: int) -> None:
        tk.Frame.__init__(self)
        self.window = window
        self._board_pos = (84, 120)
        self._board_pixel_size = 637
        self.previous_move_dis = ''

        self._click_wanted = tk.BooleanVar()

        # initialize game
        self.game = ReversiGame(size)
Example #9
0
 def _minimax(self, root_move: tuple[int, int], game: ReversiGame,
              depth: int) -> GameTree:
     """
     _minimax is a function that returns a tree where each node has a value determined by
     the minimax search algorithm
     """
     white_move = (game.get_current_player() == -1)
     ret = GameTree(move=root_move, is_white_move=white_move)
     # early return if we have reached max depth
     if depth == self.depth:
         ret.evaluation = heuristic(game, self.heuristic_list)
         return ret
     possible_moves = list(game.get_valid_moves())
     # game is over if there are no possible moves in a position
     if not possible_moves:
         # if there are no moves, then the game is over so we check for the winner
         if game.get_winner() == 'white':
             ret.evaluation = 10000
         elif game.get_winner() == 'black':
             ret.evaluation = -10000
         else:
             ret.evaluation = 0
         return ret
     # shuffle for randomness
     random.shuffle(possible_moves)
     # best_value tracks the best possible move that the player can make
     # this value is maximized by white and minimized by black
     best_value = float('-inf')
     if not white_move:
         best_value = float('inf')
     for move in possible_moves:
         new_game = game.copy_and_make_move(move)
         new_subtree = self._minimax(move, new_game, depth + 1)
         if white_move:
             best_value = max(best_value, new_subtree.evaluation)
         else:
             best_value = min(best_value, new_subtree.evaluation)
         ret.add_subtree(new_subtree)
     # update the evaluation value of the tree once all subtrees are added
     ret.evaluation = best_value
     return ret
Example #10
0
def positional_early(game: ReversiGame, selected_board_weight: list,
                     player: str) -> Union[float, int]:
    """Evaluates a board based on the positional advantage of black

    Preconditions:
        player in {BLACK, WHITE}
    """
    if player == BLACK:
        opponent = WHITE
    else:
        opponent = BLACK

    eval_so_far = 0
    board = game.get_game_board()
    for i in range(game.get_size() - 1):
        for j in range(game.get_size() - 1):
            if board[i][j] == player:
                eval_so_far += selected_board_weight[i][j]
            elif board[i][j] == opponent:
                eval_so_far -= selected_board_weight[i][j]
    return eval_so_far
Example #11
0
def check_same(player1: Player, player2: Player) -> None:
    """
    check_same is a function that determines if two players return the same move throughout a game.
    this is particularly useful for comparison between MinimaxPlayer and MinimaxABPlayer.
    It also gives the time that each player takes to find a move. You must comment out the
    random.shuffle() line of code in both players before testing
    """
    game = ReversiGame()
    prev_move = (-1, -1)
    while game.get_winner() is None:
        start_time = time.time()
        print("Player 1 CHOOSING")
        move1 = player1.make_move(game, prev_move)
        print("--- %s seconds ---" % (time.time() - start_time))
        start_time = time.time()
        print("Player 2 CHOOSING")
        move2 = player2.make_move(game, prev_move)
        print("--- %s seconds ---" % (time.time() - start_time))
        print("Player 1 chose: ", move1, "  Player 2 chose: ", move2)
        assert move1 == move2
        game.try_make_move(move1)
        prev_move = move1
Example #12
0
    def make_move(self, game: ReversiGame,
                  previous_move: Optional[str]) -> str:
        """Make a move given the current game.

        previous_move is the opponent player's most recent move, or None if no moves
        have been made.

        Preconditions:
            - There is at least one valid move for the given game state
            - len(game.get_valid_moves) > 0

        :param game: the current game state
        :param previous_move: the opponent player's most recent move, or None if no moves
        have been made
        :return: a move to be made
        """
        if len(game.get_valid_moves()) == 1:
            return game.get_valid_moves()[0]

        if previous_move is None:
            tree = MCTSTree(START_MOVE, copy.deepcopy(game))
        else:
            tree = MCTSTree(previous_move, copy.deepcopy(game))

        # assert self._tree.get_game_after_move().get_game_board() == game.get_game_board()
        # assert self._tree.get_game_after_move().get_current_player() == game.get_current_player()

        runs_so_far = 0  # the counter for the rounds of MCTS run
        time_start = time.time()

        # at least run 1 second, ends when exceeds time limit or finishes n runs
        while not (time.time() - time_start > max(self.time_limit, 1)
                   or runs_so_far == self.n):
            tree.mcts_round(self._c)
            runs_so_far += 1

        # update tree with the decided move
        move = tree.get_most_confident_move()
        return move
Example #13
0
    def make_move(self, game: ReversiGame,
                  previous_move: Optional[str]) -> str:
        """Make a move given the current game.

        previous_move is the opponent player's most recent move, or None if no moves
        have been made.

        Preconditions:
            - There is at least one valid move for the given game state
            - len(game.get_valid_moves) > 0

        :param game: the current game state
        :param previous_move: the opponent player's most recent move, or None if no moves
        have been made
        :return: a move to be made
        """
        if self._tree is None:  # initialize a tree if there is no tree
            if previous_move is None:
                self._tree = MCTSTree(START_MOVE, copy.deepcopy(game))
            else:
                self._tree = MCTSTree(previous_move, copy.deepcopy(game))
        else:  # update tree with previous move if there is a tree
            if len(self._tree.get_subtrees()) == 0:
                self._tree.expand()
            if previous_move is not None:
                self._tree = self._tree.find_subtree_by_move(previous_move)

        assert self._tree.get_game_after_move().get_game_board(
        ) == game.get_game_board()
        assert self._tree.get_game_after_move().get_current_player(
        ) == game.get_current_player()

        for _ in range(self._n):
            self._tree.mcts_round(self._c)

        # update tree with the decided move
        move = self._tree.get_most_confident_move()
        self._tree = self._tree.find_subtree_by_move(move)
        return move
Example #14
0
def helper_dropdown_select_board_size(g: ReversiGame, colour_to_player: Dict,
                                      text: str) -> None:
    """
    Set the board size given the text.
    Preconditions:
        - text is of the form '<int>x<int>' where the two
          integers are the same and greater than 0.
    """
    global board_size_current

    # Update the current board size (why?)
    board_size_current = int(text.split('x')[0])

    # Set new heuristics for players
    colour_to_player[1].set_heuristic(board_size_current)

    colour_to_player[-1].set_heuristic(board_size_current)

    # Update game board size
    g.set_board_size(board_size_current)

    # Start new game.
    g.start_game(human_player=g.get_human_player())
Example #15
0
def heuristic(game: ReversiGame, heuristic_list: list[list[int]]) -> float:
    """
    heuristic takes a given heuristic_list and returns the game-state value
    given by the list
    """
    if game.get_winner() is None:
        pieces = game.get_board().pieces
        black = 0
        white = 0
        length = len(pieces)
        for i in range(length):
            for m in range(length):
                if pieces[i][m] == 1:
                    black += heuristic_list[i][m]
                elif pieces[i][m] == -1:
                    white += heuristic_list[i][m]
        return white - black
    elif game.get_winner() == 'white':
        return 100000
    elif game.get_winner() == 'black':
        return -100000
    else:
        return 0
Example #16
0
def helper_dropdown_select_player(g: ReversiGame, text: str) -> None:
    """HELPER FUNCTION: Select the players given the dropdown option selected."""

    if text == "Human vs. AI":
        g.start_game(human_player=1)
    elif text == "AI vs. Human":
        g.start_game(human_player=-1)
    else:
        g.start_game(human_player=0)
Example #17
0
    def build_minimax_tree(self,
                           game: ReversiGame,
                           piece: str,
                           depth: int,
                           find_max: bool,
                           previous_move: str,
                           alpha: float = -math.inf,
                           beta: float = math.inf) -> MinimaxTree:
        """Construct a tree with a height of depth, prune branches based on the Tree's
        evaluate function"""
        game_tree = MinimaxTree(move=previous_move,
                                maximize=find_max,
                                alpha=alpha,
                                beta=beta)

        if game.get_winner() is not None or depth == 0:
            game_tree.eval = self._value_eval(game, piece)
        else:
            valid_moves = game.get_valid_moves()
            random.shuffle(valid_moves)
            for move in valid_moves:

                game_after_move = game.simulate_move(move)
                subtree = self.build_minimax_tree(game_after_move,
                                                  piece,
                                                  depth - 1,
                                                  not find_max,
                                                  move,
                                                  alpha=game_tree.alpha,
                                                  beta=game_tree.beta)

                game_tree.add_subtree(subtree)

                if game_tree.beta <= game_tree.alpha:
                    break

        return game_tree
Example #18
0
    def make_move(self, game: ReversiGame,
                  previous_move: Optional[str]) -> str:
        """Make a move given the current game.

        previous_move is the opponent player's most recent move, or None if no moves
        have been made.

        Preconditions:
            - There is at least one valid move for the given game state
            - len(game.get_valid_moves) > 0

        :param game: the current game state
        :param previous_move: the opponent player's most recent move, or None if no moves
        have been made
        :return: a move to be made
        """
        piece = game.get_current_player()
        tree = self.build_minimax_tree(game,
                                       piece,
                                       depth=self._depth,
                                       find_max=True,
                                       previous_move=previous_move)
        return tree.get_best()
Example #19
0
    def _value_eval(self, game: ReversiGame, piece: str) -> Union[float, int]:
        """The evaluation function for minimax. The evaluation function will
        return the number of piece of its side

        Preconditions:
            - piece in {BLACK, WHITE}

        :param game: the current game state for evaluation
        :return: the evaluated value of the current state
        """
        if game.get_winner() is not None:
            if game.get_winner() == piece:  # win
                return math.inf
            elif game.get_winner() == 'Draw':  # draw
                return 0
            else:  # lose
                return -math.inf
        else:
            if piece == BLACK:
                return game.get_num_pieces()[BLACK] / game.get_num_pieces(
                )[WHITE]
            else:
                return game.get_num_pieces()[WHITE] / game.get_num_pieces(
                )[BLACK]
Example #20
0
    def _value_eval(self, game: ReversiGame, piece: str) -> Union[float, int]:
        """The evaluation function for minimax. For Positional Player, the evaluation function
        will evaluate the positional advantage of the pieces on the board.

        Preconditions:
            - piece in {BLACK, WHITE}

        :param game: the current game state for evaluation
        :return: value evaluated from the current game state
        """
        if game.get_winner() is not None:
            if game.get_winner() == piece:  # win
                return math.inf
            elif game.get_winner() == 'Draw':  # draw
                return 0
            else:  # lose
                return -math.inf
        else:
            num_black, num_white = game.get_num_pieces(
            )[BLACK], game.get_num_pieces()[WHITE]
            board_filled = (num_black + num_white) / (game.get_size()**2)
            if game.get_size() == 8:
                selected_board_weight = BOARD_WEIGHT_8
            else:
                selected_board_weight = BOARD_WEIGHT_6

        if board_filled < 0.80:
            return positional_early(game, selected_board_weight, piece)
        else:
            if piece == BLACK:
                return num_black / num_white

            if piece == WHITE:
                return num_white / num_black

        return 0
Example #21
0
from window import Window
from reversi import ReversiGame
from board_manager import BoardManager
from ai_players import MinimaxABPlayer

if __name__ == "__main__":

    # Initialize PyGame
    pygame.init()

    # Create a window wrapper class instance
    window = Window()

    # Create a ReversiGame instance
    # Change human_player to 1 or -1 to play against AI, 0 if no human player
    game = ReversiGame(human_player=1)  # Set to 1 by default.

    # Setup the BoardManager instance
    board_manager = BoardManager(window)

    ui_handler = UIHandler()

    # Minimax Player

    player1 = MinimaxABPlayer(2, 8)
    player2 = MinimaxABPlayer(2, 8)
    colour_to_player = {1: player1, -1: player2}

    # Set number of games
    num_games_left = 40
Example #22
0
def _run_ai_game(game_surface: pygame.Surface, size: int,
                 ai_player: Union[MobilityTreePlayer, PositionalTreePlayer, RandomPlayer,
                                  ReversiGame, MCTSTimeSavingPlayer],
                 user_side: str = BLACK) -> None:
    if size == 8:
        background = pygame.image.load('assets/gameboard8.png')
    elif size == 6:
        background = pygame.image.load('assets/gameboard6.png')
    else:
        raise ValueError("invalid size.")
    game_surface.blit(background, (0, 0))
    pygame.display.flip()
    game = ReversiGame(size)
    previous_move = '*'
    if user_side == BLACK:
        ai_side: str = WHITE
    else:
        ai_side: str = BLACK
    board = game.get_game_board()
    _draw_game_state(game_surface, background, size, board)

    pass_move = pygame.image.load('assets/pass.png')

    while game.get_winner() is None:
        if (previous_move == '*' and user_side == WHITE) or game.get_current_player() == user_side:
            if game.get_valid_moves() == ['pass']:
                game.make_move('pass')
                previous_move = 'pass'

                surface = game_surface
                game_surface.blit(pass_move, (300, 300))
                pygame.display.flip()
                pygame.time.wait(1000)
                game_surface.blit(surface, (0, 0))
                pygame.display.flip()

                continue
            while True:
                event = pygame.event.wait()
                if event.type == pygame.MOUSEBUTTONDOWN:
                    mouse_pos = pygame.mouse.get_pos()
                    if 585 <= mouse_pos[0] <= 795 and 10 <= mouse_pos[1] <= 41:
                        return
                    else:
                        move = _search_for_move(mouse_pos, size)
                        print(move)
                        if move == '' or move not in game.get_valid_moves():
                            continue
                        else:
                            previous_move = move
                            game.make_move(move)
                            board = game.get_game_board()
                            _draw_game_state(game_surface, background, size, board)
                            pygame.time.wait(1000)
                            break
                if event.type == pygame.QUIT:
                    return
        else:
            move = ai_player.make_move(game, previous_move)
            previous_move = move
            game.make_move(move)
            if move == 'pass':
                surface = game_surface
                game_surface.blit(pass_move, (300, 300))
                pygame.display.flip()
                pygame.time.wait(1000)
                game_surface.blit(surface, (0, 0))
                pygame.display.flip()
            else:
                board = game.get_game_board()
                _draw_game_state(game_surface, background, size, board)
    winner = game.get_winner()
    if winner == user_side:
        victory = pygame.image.load('assets/victory.png')
        game_surface.blit(victory, (300, 300))
        pygame.display.flip()
        pygame.time.wait(3000)
        return
    elif winner == ai_side:
        defeat = pygame.image.load('assets/defeat.png')
        game_surface.blit(defeat, (300, 300))
        pygame.display.flip()
        pygame.time.wait(3000)
        return
    else:
        draw = pygame.image.load('assets/draw.png')
        game_surface.blit(draw, (300, 300))
        pygame.display.flip()
        pygame.time.wait(3000)
        return
Example #23
0
 def make_move(self, game: ReversiGame,
               previous_move: tuple[int, int]) -> tuple[int, int]:
     """Make a random move."""
     return random.choice(list(game.get_valid_moves()))