Search code examples
pythonalgorithmminimax

Minimax algorithm only returning specific set of values


I implemented a minimax algorithm for a basic tic-tac-toe AI in Python like this:

def minimax(currentBoard, player):
    if isGameOver(currentBoard):
        score = evaluate(currentBoard)
        return score
    for cell in getEmptySpots(currentBoard):
        x = cell[0]
        y = cell[1]
        currentBoard[x][y] = player
        bestScore = -1000000
        score = minPlay(currentBoard, -player)
        currentBoard[x][y] = 0
        if score > bestScore:
            bestScore = score
            bestMove = cell
            print('Best move:')
            print(bestMove)
            print('\n')
        return bestMove

def minPlay(currentBoard, player):
    if isGameOver(currentBoard):
        score = evaluate(currentBoard)
        return score
    for cell in getEmptySpots(currentBoard):
        x = cell[0]
        y = cell[1]
        currentBoard[x][y] = player
        bestScore = 1000000
        score = maxPlay(currentBoard, -player)
        currentBoard[x][y] = 0
        if score < bestScore:
            bestScore = score
        return bestScore

def maxPlay(currentBoard, player):
    if isGameOver(currentBoard):
        score = evaluate(currentBoard)
        return score
    for cell in getEmptySpots(currentBoard):
        x = cell[0]
        y = cell[1]
        currentBoard[x][y] = player
        bestScore = -1000000
        score = minPlay(currentBoard, -player)
        currentBoard[x][y] = 0
        if score > bestScore:
            bestScore = score
        return bestScore

The other supporting functions are pretty self-explanatory. However, this script does not function as it should. For example, it always seems to begin by picking [0,0] and then proceeding with a relatively constant set of moves, even when better moves are available. Also, given the following state (and states in general where a winning move is available):

enter image description here

where 1 represents the human and -1 represents the computer, the computer picks the move [2][1] as the best move, instead of [2][2] or [1][2] which would both result in a win.

I have gone through a number of questions relating to minimax implementation in different languages and as far as I can tell my code logically makes sense. Thus I'm unsure as to what the problem could be. My full code can be found here.

enter image description here


Solution

  • There are 2 issues. The first is the logic problem in @Flopp 's answer. The second is that isGameOver does not return true when there are no more moves left. Scores of 0 get returned as the initial max or min score.

    here:

    def minPlay(currentBoard, player):
        if isGameOver(currentBoard):
            score = evaluate(currentBoard)
            return score
    

    The relevant (fixed) line is here (it's not beautiful, it's just demonstrating that it will work):

    def isGameOver(currentBoard):
        return checkGameOver(currentBoard, HUMAN) or checkGameOver(currentBoard, COMPUTER) or getEmptySpots(currentBoard) == []
    

    For minimax, it would probably be a good idea to make sure there's an initial bestMove, too.

    def minimax(currentBoard, player):
        if isGameOver(currentBoard):
            score = evaluate(currentBoard)
            return score
        allMoves = getEmptySpots(currentBoard)
        bestMove = allMoves[0]
        for cell in allMoves: