'After a certain number of moves my tictactoe program raise an exception

I'm trying to build an AI to play tictactoe(cs50ai pset0). i have built 7 essential functions for this purpose.

  1. player function that takes as an argument a board and returns whose turn is it.
  2. actions function that takes as an argument a board and returns the possible actions on the board as a set of tubles.
  3. result function which takes an action and a board as arguments, and returns the new board caused of that action.
  4. winner function which takes a board and returns whose the winner of that board if found, otherwise returns none.
  5. terminal functin which takes a board and determine if that board is a terminal board if so it will return True, otherwise it will return false.
  6. utility function which takes a board and returns whose the utility of that board.
  7. minimax function which takes a board and returns the optimal move for that board.

However, when i run my code sometimes after a number of moves it raise an exception(ActionError) i have tested all my function except mininmax and didn't catch the bug. Even though i can tell my minimax function doesn't choose the optimal move . """ Tic Tac Toe Player """

from math import inf
import copy
class ActionError(Exception):
    pass

X = "X"
O = "O"
EMPTY = None
dictx = {}
dicto ={}


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


def player(board):
    """
    Returns player who has the next turn on a board.
    """
    if not terminal(board):
        x =0
        o =0
        for i in board:
            for j in i:
                if X == j:
                     x +=1
                elif O == j:
                    o +=1
        if o < x:
            return O
        else:
            return X


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

    if terminal(board):
        return (0,0)
    moves = set()
    for i in range(len(board)):
        for j in range(len(board)):
            if board[i][j] == EMPTY:
                moves.add((i,j))

    return moves


def result(board, action):
    """
    Returns the board that results from making move (i, j) on the board.
    """
    play = player(board)
    if action not in actions(board):
        raise ActionError
    new_board = copy.deepcopy(board)
    for i in range(len(new_board)):
            for j in range(len(new_board)):
                if (i,j) == action:
                    new_board[i][j] = play
                    return new_board


def winner(board):
    """
    Returns the winner of the game, if there is one.
    """
    #horizontal
    for i in board:
        if i.count(X) == 3:
            return X
        if i.count(O) == 3:
            return O
    #vertical
    v= set()
    for i in range(3):
         if len(v) == 1:
           return list(v)[0]
         v= set()
         for j in range(3):
            v.add(board[j][i])

    #diagonal
    n= set()
    v= set()
    for i in range(3):
         for _ in range(1):
             n.add(board[i][i])
             v.add(board[i][2 - i])
    if len(n) == 1:
         return list(n)[0]
    if len(v) == 1:
         return list(v)[0]

    return None


def terminal(board):
    """
    Returns True if game is over, False otherwise.
    """
    counter = 0
    if winner(board) != None:
        return True
    for i in board:
        for j in i:
            if j == EMPTY:
                counter +=1
    if winner(board) == None and counter == 0:
        return True
    return False


def utility(board):
    """
    Returns 1 if X has won the game, -1 if O has won, 0 otherwise.
    """
    if winner(board) == X:
        return 1
    elif winner(board) == O:
        return -1
    else:
        return 0


def minimax(board):
    if terminal(board):
        return None
    elif player(board) == X:
        return max(dictx,key=dictx.get)
    elif player(board) == O:
        value1 = Min_Value(board)
        return min(dicto,key=dicto.get)

    


def Min_Value(board):
    if terminal(board):
        return utility(board)
    v = inf
    for a in actions(board):
        v = Min(v, Max_Value(result(board,a)))
        dicto[a] = v
    return v


def Max_Value(board):
    if terminal(board):
        return utility(board)
    v = -inf
    for a in actions(board):
        v = Max(v, Min_Value(result(board,a)))
        dictx[a] = v
    return v

def Max(a,b):
    if a > b:
        return a
    elif a < b:
        return b
    else:
        return b
def Min(a,b):
    if a > b:
        return b
    elif a < b:
        return a
    else:
        return b

here is the error message:

PS C:\Users\AHMED ALAGHA PC\Downloads\tictactoe\tictactoe> py runner.py
pygame 2.1.2 (SDL 2.0.18, Python 3.10.4)
Hello from the pygame community. https://www.pygame.org/contribute.html
Traceback (most recent call last):
  File "C:\Users\AHMED ALAGHA PC\Downloads\tictactoe\tictactoe\runner.py", line 116, in <module>
    board = ttt.result(board, move)
  File "C:\Users\AHMED ALAGHA PC\Downloads\tictactoe\tictactoe\tictactoe.py", line 67, in result
    raise ActionError
tictactoe.ActionError


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source