コード例 #1
0
ファイル: minimax.py プロジェクト: ao9000/tic-tac-toe-ai
def minimax(board, depth, is_maximizing_player):
    """
    Using minimax algorithm to find the optimal move for the current state of the game.

    :param board: type: numpy.ndarray
    The current state of the Tic Tac Toe board game

    :param depth: type: int
    How deep the decision tree to search
    The depth at the top of the tree is 0, as you go deeper, depth increases
    Maximum depth is 9, as Tic Tact Toe only have 9 moves available

    :param is_maximizing_player: type: bool
    True if maximizing player's turn (Bot)
    False if minimizing player's turn (Human)

    :return: type: tuple
    Contains the best minimax score and a list of moves that is derived from that score
    """
    # Check if last node
    if win_check(board) or depth == 9:
        return heuristic_evaluation(board, depth), None

    best_moves = []

    if is_maximizing_player:
        max_score = -inf

        # Loop possible moves in a single turn
        for branch, move in zip(*get_possible_branches(board, True)):
            score, _ = minimax(branch, depth + 1, False)

            if score > max_score:
                max_score = score
                best_moves = [move]
            elif score == max_score:
                best_moves.append(move)

        return max_score, best_moves
    else:
        min_score = +inf

        # Loop possible moves in a single turn
        for branch, move in zip(*get_possible_branches(board, False)):
            score, _ = minimax(branch, depth + 1, True)

            if score < min_score:
                min_score = score
                best_moves = [move]
            elif score == min_score:
                best_moves.append(move)

        return min_score, best_moves
コード例 #2
0
ファイル: __init__.py プロジェクト: ao9000/tic-tac-toe-ai
def get_all_possible_board_states(turn_num, primary_state, secondary_state):
    """
    Loops through all possible states of the board starting from the turn_num provided and ending on the last turn.
     Finally, collates them in a list

    :param turn_num: type: int
    The number of moves on the board

    :param primary_state: type: int
    The state of the player that had the first move, HUMAN_STATE or BOT_STATE

    :param secondary_state: type: int
    The state of the player that had the second move, HUMAN_STATE or BOT_STATE

    :return: type: list
    Containing all the possible board states given a turn number
    """
    # Check for valid turn number
    if turn_num < 0 or turn_num > 9:
        raise ValueError("Turn number must be between 0 and 9")

    # Return blank board for turn 0
    if not turn_num:
        return create_board()

    boards = []
    temp_boards = []

    board = create_board()

    # Player for that turn number
    for move in combinations(get_possible_moves(board), ceil(turn_num / 2)):
        temp_boards.append(deepcopy(board))
        for row, box in move:
            temp_boards[-1][row][
                box] = secondary_state if turn_num % 2 == 0 else primary_state

    # Other player
    for board in temp_boards:
        for move in combinations(get_possible_moves(board),
                                 floor(turn_num / 2)):
            boards.append(deepcopy(board))
            for row, box in move:
                boards[-1][row][
                    box] = primary_state if turn_num % 2 == 0 else secondary_state

            # Remove all won/terminal board states
            if win_check(boards[-1]):
                del boards[-1]

    return boards
コード例 #3
0
ファイル: run.py プロジェクト: ao9000/tic-tac-toe-ai
def main():
    """
    The main function of the application.
    Creates the board, players and loop their turns infinitely until a winner is found.
    """
    # Introduction
    print(
        "Welcome to the game of Tic Tac Toe, your opponent is a bot running the Minimax algorithm"
    )

    # Create Tic Tac Toe board
    board = create_board()

    # Create players
    human_mark, bot_mark = choose_mark()
    bot = Player(bot=True, state=BOT_STATE, mark=bot_mark)
    human = Player(bot=False, state=HUMAN_STATE, mark=human_mark)

    # Random starting player
    players = [human, bot]
    random.shuffle(players)

    # Loop turns
    while True:
        for player in players:
            print("\n{}'s turn".format(
                player_name := "Bot" if player.bot else "Human"))
            move = player.make_move(board)
            update_board(board, move, player)
            display_board(board, players)

            # Break infinite loop under conditions
            if win_check(board):
                return print("Game over. {} wins!".format(player_name))
            elif is_board_full(board):
                return print("Game over. Draw!")
コード例 #4
0
ファイル: minimax.py プロジェクト: ao9000/tic-tac-toe-ai
def minimax_alpha_beta(board, depth, is_maximizing_player, alpha, beta):
    """
    Using minimax algorithm to find the optimal move for the current state of the game.

    This version of the minimax algorithm includes alpha beta pruning. The pruning prunes all the values that is
    equal or smaller/larger than the required threshold depending on the player. Thus, this version can only return 1
    move, as the other moves are pruned.

    :param board: type: numpy.ndarray
    The current state of the Tic Tac Toe board game

    :param depth: type: int
    How deep the decision tree to search
    The depth at the top of the tree is 0, as you go deeper, depth increases
    Maximum depth is 9, as Tic Tact Toe only have 9 moves available

    :param is_maximizing_player: type: bool
    True if maximizing player's turn (Bot)
    False if minimizing player's turn (Human)

    :return: type: tuple
    Contains the best minimax score and a single move that is derived from that score
    """
    # Check if leaf node
    if win_check(board) or depth == 9:
        return heuristic_evaluation(board, depth), None

    best_moves = None

    if is_maximizing_player:
        max_score = -inf

        # Loop possible moves in a single turn
        for branch, move in zip(*get_possible_branches(board, True)):
            score, _ = minimax_alpha_beta(branch, depth + 1, False, alpha,
                                          beta)

            if score > max_score:
                max_score = score
                best_moves = [move]

            # Alpha beta pruning
            alpha = max(alpha, score)
            if beta <= alpha:
                break

        return max_score, best_moves
    else:
        min_score = +inf

        # Loop possible moves in a single turn
        for branch, move in zip(*get_possible_branches(board, False)):
            score, _ = minimax_alpha_beta(branch, depth + 1, True, alpha, beta)

            if score < min_score:
                min_score = score
                best_moves = [move]
            elif score == min_score:
                best_moves.append(move)

            # Alpha beta pruning
            beta = min(beta, score)
            if beta <= alpha:
                break

        return min_score, best_moves
コード例 #5
0
def main():
    """
    The main function of the game.
    Responsible for the setup of game window properties, creating players, scheduling scenes in the game, recording
    player statistics and looping the game.
    """
    # Setup game
    screen, clock = setup_game()

    # Create list of players
    players = []
    # Define whose turn
    player = None

    # Define stats recording
    records = {
        # Record turn number
        'turn_num': 0,
        # Record bot wins
        'bot_win': 0,
        # Record human wins
        'human_win': 0,
        # Record draws
        'draw': 0
    }

    # Define screen states
    intro = True
    game = True

    # Create a blank Tic Tac Toe board
    board = create_board()

    # Game loop
    while True:
        # tick rate
        clock.tick(30)

        mouse_position = pygame.mouse.get_pos()
        mouse_clicked = False

        for event in pygame.event.get():
            # Break loop if window is closed
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

            # Break loop if ESC key is pressed
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    pygame.quit()
                    sys.exit()

            elif event.type == pygame.MOUSEBUTTONDOWN:
                mouse_clicked = True

        # White background/clear previous objects
        screen.fill(color_to_rgb("white"))

        if intro:
            # Draw selection screen
            interface_items = selection_screen(screen, mouse_position)
            render_items_to_screen(screen, interface_items)

            # Handle user input
            if mouse_clicked:
                human_input_selection_screen_handler(interface_items, players,
                                                     mouse_position)

            # Proceed to next screen if user selected a choice & assign players
            if players:
                # Unpack players
                bot, human = players[0], players[1]

                # Random starting player
                player = random.choice(players)

                # Move on to game screen
                intro = False
        elif game:
            # Game scene
            # Draw board information
            interface_items = board_information(screen, records)
            render_items_to_screen(screen, interface_items)

            # Draw tic tac toe board
            interface_items = game_board(screen, board, players)
            render_items_to_screen(screen, interface_items)

            # Check if game is finished
            if win_check(board):
                # Game is finished
                # Highlight the winning row
                interface_items = highlight_win(interface_items, board)
                render_items_to_screen(screen, interface_items)

                # Add delay
                post_game_delay()

                # Record stats
                record_win(player, records)

                # Reset board
                board = create_board()

                # Next game, random starting turn again
                player = random.choice(players)
            elif is_board_full(board):
                # Game is finished

                # Add delay
                post_game_delay()

                # Record stats
                record_draw(records)

                # Reset board
                board = create_board()

                # Next game, random starting turn again
                player = random.choice(players)
            else:
                # Game not finished
                # Make a move (bot/human)
                if player.bot:
                    # Bot turn
                    bot_move_input_handler(board, bot)
                else:
                    if mouse_clicked:
                        # Human turn
                        human_move_input_handler(board, interface_items,
                                                 mouse_position, human)

                # Cycle turns
                if get_turn_number(board) != records["turn_num"]:
                    if not win_check(board) and not is_board_full(board):
                        # Subsequent turns
                        player = human if player.bot else bot
                        records["turn_num"] = get_turn_number(board)

        # Update screen
        pygame.display.update()