Search code examples
pythonartificial-intelligencecs50tic-tac-toe

my tic tac toe ai is not working . is there a way to fix it?


"""
Tic Tac Toe Player
"""

import math
import copy
X = "X"
O = "O"
EMPTY = None


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.
    """
    countx=0
    counto=0
    for i in range(0,3):
        for j in range(0,3):
            if board[i][j]==X:
                countx+=1
            if board[i][j]==O:
                counto+=1

    if counto>countx:
        return X
    if countx>counto:
        return O
                
                

    


def actions(board):
    """
    Returns set of all possible actions (i, j) available on the board.
    """
    allaction=set()
    for i in range(0,3):
        for j in range(0,3):
            if board[i][j]==EMPTY:
                print(i,j)
                allaction.add((i,j))
    
    return allaction
    


def result(board, action):
    """
    Returns the board that results from making move (i, j) on the board.
    """
    current_player=player(board)
    new_all_actions=actions(board)
    if action not in  new_all_actions:
        raise Exception("move not in the set ")
    i,j=action 
    copy_board=copy.deepcopy(board)
    copy_board[i][j]=current_player
    return copy_board
        

def check_row(board,player):
    for i in range(0,3):
        if board[i][0]==player and board[i][1]==player and board[i][2]==player:
            return True
    return False
def check_column(board,player):
    for i in range(len(board)-1):
        if board[0][i]==player and board[1][i]==player and board[2][i]==player:
            return True
    return False

def check_diagonals(board,player):
    if board[0][0]==player and board[1][1]==player and board[2][2]==player:
        return True
    elif board[0][2]==player and board[1][1]==player and board[2][0]==player:
        return True
    return False

def winner(board):
    """
    Returns the winner of the game, if there is one.
    """
    
    if check_column(board,X)==True or check_row(board,X)==True or check_diagonals(board,X)==True:
        return X
    elif check_column(board,O)==True or check_row(board,O)==True or check_diagonals(board,O)==True:
        return O
    return None
    


def terminal(board):
    """
    Returns True if game is over, False otherwise.
    """
    for i in range(0,3):
        for j in range(0,3):
            if board[i][j]==EMPTY:
                return False
    if winner(board)==X or winner(board)==O:
        return True
    else:
        return True
    
    

    
        
        


def utility(board):
    """
    Returns 1 if X has won the game, -1 if O has won, 0 otherwise.
    """
    if terminal(board)==False:
        raise Exception("game not over")
    if winner(board)==X:
        return 1
    if winner(board)==O:
        return -1
    else:
        return 0

def max_value(board):
        v=-math.inf
        if terminal(board):
            return utility(board)
        for action in actions(board):
            v=max(v,min_value(result(board,action)))
        return v
        
def min_value(board):
    v=math.inf
    if terminal(board):
        return utility(board)
    for action in actions(board):
        v=min(v,max_value(result(board,action)))
    return v 

def minimax(board):
    if terminal(board):
        return None
    elif player(board)==X:
        max_variable=-math.inf
        max_index=0
        score_play=[]
        for action in actions(board):
            score_play.append(min_value(result(board,action)),action)
        for i in range(len(score_play)-1):
            if score_play[i][0]>max_variable:
                max_variable=score_play[i][0]
                max_index=i
        
        return score_play[max_index][1]
    
#its returns min of max_value of all possible boards from the result
    elif player(board)==O:
       
        min_varible=math.inf
        min_index=0
        score_play=[]
        for action in actions(board):
            score_play.append(max_value(result(board,action),action))
        for j in range(len(score_play)-1):
            if score_play[j][0]<min_varible:
                min_varible=score_play[j][0]
                min_index=j
        for q in score_play[min_index][1] :
            print(q)
        return score_play[min_index][1]
    
        
    

    

this is my code for a tic tac toe ai. On runnig it it shows the following error

Traceback (most recent call last): File "C:\Users\Dell\Downloads\tictactoe\tictactoe\runner.py", line 116, in board = ttt.result(board, move) File "C:\Users\Dell\Downloads\tictactoe\tictactoe\tictactoe.py", line 66, in result raise Exception("move not in the set ") Exception: move not in the set

on further looking i got to know that the action passed to the result function is none but dont know why

I tried debugging it for a long time . the only error that can happen is maybe in the minmax function


Solution

  • There are these issues:

    • terminal returns False for any board that has an empty spot. You should only start the empty spot check when it is already verified that there is no winner. NB: the if..else is overkill, since in both cases you return True, so just ... return True without condition. Also, you can use the in operator to check if a row has EMPTY:

      def terminal(board):
          if not winner(board):  # Only check for empty spot when no winner
              for row in board:
                  if EMPTY in row:
                      return False
          return True  # No condition needed
      
    • player can return None when the two counters are equal. The function should never return None. Assuming X is the first player, you should return X when the count is equal and O otherwise:

          if counto==countx:
              return X
          return O
      
    • check_column only iterates twice with range(len(board)-1), but it should iterate 3 times. So:

      for i in range(len(board)):  # not -1 !!
      
    • In minimax parentheses are put wrongly in score_play.append(max_value(result(board,action),action)). Both max_value and append should be called with one argument, so:

      score_play.append((max_value(result(board,action)),action))
      

      A similar change is needed where you call min_value

    • In minimax the loop over score_play does not visit the last entry when having range(len(score_play)-1). The loop should be:

      for i in range(len(score_play)):
      

      Change this in both places where this occurs.

    NB: You could have detected all of these issues yourself if you would have used a debugger, stepping through the code, setting break points and inspecting variables.