0

I'm making a Tic-Tac-Toe game using PyGame on Python where the user plays against an AI. Now I tried making a MiniMax function to recursively find the most optimal move, and by also using Alpha-Beta Pruning. I have (successfully?) made the rest of the game, but I am unable to get this part done. It has incredibly hard and messy. I tried searching everywhere, but none of them made the game like I did. So I'm here. Can someone please help me out?

(The complete code, if needed)

X = 'X'
O = 'O'
EMPTY = None

X_count = 0
O_count = 0


def initial_state():
    """Returns starting state of the board
    """
    return [
        [EMPTY, EMPTY, EMPTY],
        [EMPTY, EMPTY, EMPTY],
        [EMPTY, EMPTY, EMPTY]
    ]


def display(board, autoprint=False):
    """Displays the board nested list as
a 3x3 matrix for board visualization
"""
    vis_board = ''

    for row in board:
        for playr in row:
            if playr is None:
                playr = ' '

            vis_board += playr + ' '

        vis_board += '\n'

    if autoprint:
        print(vis_board)
        return None

    return vis_board


def player(board):
    """Returns player who has the next turn on a board
    """
    global X, O, X_count, O_count

    for i, row in enumerate(board):
        for j, move in enumerate(row):
            if move == X:
                X_count += 1

            elif move == O:
                O_count += 1

    # `X` always starts first
    if O_count < X_count:
        return O

    return X


def actions(board):
    """Returns set of all possible actions
(i, j) available on the board
    """

    global EMPTY

    action_set = set()

    for i, row in enumerate(board):
        for j, move in enumerate(row):
            if move is EMPTY:
                action_set.add((i, j))

    return action_set


def result(board, action):
    """Returns the board that results from
making move (i, j) on the board.
    """

    global EMPTY

    if type(action) is not tuple or len(action) != 2:
        raise Exception('invalid action taken')

    # Duplicating *board* by slicing the entire list
    # into another variable with a different `id`
    dup_board = board[:]

    # Unpack the coordinates as `I` and `J`
    I, J = action

    # Check if place has not already been used
    if dup_board[I][J] is EMPTY:
        dup_board[I][J] = player(dup_board)

    #else:
        #raise Exception('invalid action taken')

    return dup_board


def is_full(board):
    """Returns True if all places have been occupied, else returns False
"""

    global EMPTY

    for row in board:
        for playr in row:
            if playr is EMPTY:
                return False

    return True


def winner(board):
    """Returns the winner of the game, if there is one.
    """

    winr = None # Initial declaration to avoid errors if no winner found

    # Check diagonally
    if (board[1][1] == board[0][0] and board[0][0] == board[2][2])\
         or (board[1][1] == board[0][2] and board[0][2] == board[2][0]):
            winr = board[1][1]
            return winr

    for i in range(3):
        # Check each row for three-in-a-row
        if board[i][0] == board[i][1] and board[i][1] == board[i][2]:
            winr = board[i][1]
            break

        # Check each column for three-in-a-column
        elif board[0][i] == board[1][i] and board[1][i] == board[2][i]:
            winr = board[1][i]
            break

    return winr


def terminal(board):
    """Returns True if game is over, False otherwise.
    """

    if winner(board) is None and not is_full(board):
        return False

    return True


def utility(board):
    """Returns 1 if X has won the game, -1 if O has won, 0 otherwise.
    """
    global X, O

    if terminal(board):
        winr = winner(board)

        if winr == X:
            util = 1

        elif winr == O:
            util = -1

        else:
            util = 0

        return util

    return None


def minimax(board):
    """Returns the optimal action for the current player on the board.
    """

    #### THIS FUNCTION WAS JUST MADE TO RUN THE PROGRAM
    #### AND IS WHAT I DON'T UNDERSTAND SO PLEASE DO NOT
    #### CONSIDER THIS FUNCTION WHILE ANSWERING THE QUESTION,
    #### RATHER, PLEASE TELL ME THE ACTUAL METHODOLOGY
    #### AND/OR SOME CODE FOR MINIMAX (PROBABLY WITH
    #### ALPHA-BETA PRUNING). THANK YOU!

    if terminal(board):
        return None

    max_ = -float('inf')
    best_action = None

    # Recursively choose the best action in `actions` of *board*
    for action in actions(board):
        rslt = result(board, action)

        if utility(rslt) == 1:
            best_action = action
            break

    return best_action

Also, if there are any other bugs/errors, please tell me so I can correct them.

Thank you in advance!

SFM61319
  • 134
  • 3
  • 13
  • 1
    minimax() always returns the last action tried – Jeff Jun 06 '20 at 20:56
  • 1
    I don't see that method utility recursively tries successive possible actions after each action to evaluate the best action. Here's explanation and Python code [Mastering Tic-Tac-Toe with Minimax Algorithm in Python](https://levelup.gitconnected.com/mastering-tic-tac-toe-with-minimax-algorithm-3394d65fa88f) – DarrylG Jun 06 '20 at 21:22
  • @Jeff the `minimax` function written was just to run the program and can be considered fake. I tried my best but it did not work and gave errors, so I replaced it with a fake code that simply returns the las action and not the best action. – SFM61319 Jun 07 '20 at 12:20
  • @DarrylG I already went through [that site](https://levelup.gitconnected.com/mastering-tic-tac-toe-with-minimax-algorithm-3394d65fa88f "GitConnected"). But the thing is I'm trying to limit the parameters of the function `minimax` to one (which is the board itself, a nested list of coordinates) which led to confusion when I looked at the code from that site (which had two functions with more than one parameters). – SFM61319 Jun 07 '20 at 12:25
  • @SFM61319--seems you would want at least two parameters--the board, and who's turn it is. Otherwise, it would be wasteful to figure out who's turn by calling `player` as you recursed through minimax. – DarrylG Jun 07 '20 at 12:39
  • @DarrylG [this is the new, revised question](https://stackoverflow.com/questions/62248564/game-ai-works-powerfully-on-one-side-and-becomes-dumb-on-the-other-in-tic-tac-to "New question") that I actually wanted to ask. Also, I gave the complete code over there, and a few more problems that I encountered. Also, the no. of parameters must not be changed, according to the site that gave these questions. And `player` determines whose turn it is, so that the GUI file knows when to take input and when not to. I've clearly explained everything there. And thank you for helping! I'm outta chars.. – SFM61319 Jun 07 '20 at 16:54

0 Answers0