Search code examples
pythonpython-2.7tic-tac-toeminimax

The Minimax function returns the same evaluation for all possible moves in TicTacToe


In the main program loop, the for loop goes through each of the empty positions in the TicTacToe board and then applies the minimax function on each position to give the evaluation of that particular move. However, for each particular board configuration at a moment based on input, it returns the same evaluation for all the empty spots.


board= [ "X", 1, 2, 
         3, "O", 5, 
         6 , 7, 8]

human = "O"
robot= "X"

def evaluation(board):
    if (winning(board, robot)):
        return +10
    elif (winning(board, human)):
        return -10
    else:
        return 0


def minimax(empty_spots, depth, maximizing_player):
    if depth==0 or len(empty_spots)==0:
        eval= evaluation(board)
        # print(f'The evaluation function returns: {evaluation(board)}')
        # return evaluation(board)
        return eval

    if maximizing_player:
        maxEval= -1000000
        empty_spots=list(filter(lambda spot: (spot!="O" and spot!="X"), board))
        # print(f'testing empty spots from maximizing_player{empty_spots}')

        #for the child of that position
        for spot in empty_spots:
            board[spot]=robot
            eval = minimax(empty_spots, depth-1, False)

            #then remove the move and replace it with the number
            board[spot]=spot
            maxEval= max(maxEval, eval)
            # print(f'The maxEval is {maxEval}')
        # print(f'The maxEval is {maxEval}')
        return maxEval

    else:
        minEval= +1000000
        empty_spots=list(filter(lambda spot: (spot!="O" and spot!="X"), board))
        # print(f'testing empty spots from minimizing_player{empty_spots}')

        #for each child of that position
        for spot in empty_spots:
            board[spot]=human
            eval= minimax(empty_spots, depth-1, True)

            #then remove the spot
            board[spot]=spot
            minEval=min(minEval, eval)
        # print(f'The minEval is {minEval}')
        return minEval



#the main program loop
while(True):

    while(True):
        try:    
            human_input=int(input("Please enter an integer from 0 to 8: "))
            #this should only take in empty spots
            if(human_input>=0 and human_input<=8):
                break
        except ValueError:
            print("Input must be an integer value.")    

    board[human_input]= human

    #returns a list of empty positions in the array 
    empty_spots=list(filter(lambda spot: (spot!="O" and spot!="X"), board))
    print(empty_spots)

    moves= []
    for spot in empty_spots:
        spot_eval= minimax(empty_spots, len(empty_spots), True)
        print(f'The spot eval for {spot} is {spot_eval}')
        moves.append(spot_eval)

    print(f'This is the moves array {moves}')
    #go through the moves array and pick out the best
    best_move= empty_spots[0]
    for move in moves:
        best_move= max(best_move, move)
    print(f'The best move is {best_move}')



Where I expected the output of the move array to be [10,-10,0 ..] and so on for each empty spot. My output is instead, for example :

enter image description here


Solution

  • Let's look at this loop you have in the program:

    for spot in empty_spots:
        spot_eval = minimax(empty_spots, len(empty_spots), True)
        print(f'The spot eval for {spot} is {spot_eval}')
        moves.append(spot_eval)
    

    Note that the call to minimax function is the same on all iterations of the for loop because it doesn't depend on the spot variable. So basically you do len(empty_spots) calls to minimax function in this loop (which are all the same). As a result, the moves array is filled with the same value, which is equal to the value returned by minimax(empty_spots, len(empty_spots), True)