Why does my tic tac toe minimax algorithm utilizing a 1d representation of a tic tac toe board and allowing for play of both sides not work?

huangapple go评论69阅读模式
英文:

Why does my tic tac toe minimax algorithm utilizing a 1d representation of a tic tac toe board and allowing for play of both sides not work?

问题

我编写了一个使用极小化极大算法来玩井字游戏的Python程序。棋盘用一个一维数组表示。玩家可以选择扮演任意一方。程序会递归生成所有合法的棋盘,并在每一轮交替最小化和最大化来检查是否有赢家。但出于某种原因,该算法从未生成过获胜的棋盘。

起初我以为与算法没有正确地最小化和最大化右侧有关,但我检查了后发现代码无明显问题。

import random

# 其他代码...

# 以下代码不要翻译
# Main Code
test()
letter = 'X'
board = [""] * 9
player = random.choice([0, 1])
print("You are playing as", player)
while True:
    printBoard(board)
    winner = getWinner(board)
    if winner is not None:
        if winner == "Tie":
            print("Tie")
        else:
            print("Winner:", winner)
        break
    if player == 0:
        if letter == "O":
            move = findBestMove(board, letter, player)
            print("Computer played move", move)
            board[move] = letter
        else:
            move = int(input("Enter move (0-8): "))
            while board[move] != "":
                move = int(input("Invalid move. Enter move (0-8): "))
            board[move] = letter
    elif player == 1:
        if letter == "X":
            move = findBestMove(board, letter, player)
            print("Computer played move", move)
            board[move] = letter
        else:
            move = int(input("Enter move (0-8): "))
            while board[move] != "":
                move = int(input("Invalid move. Enter move (0-8): "))
            board[move] = letter
    letter = getOtherPlayer(letter)
英文:

I wrote a python program utilizing the minimax algorithm to play tic tac toe. The board is represented by a 1d array. The player is able to play as either side. The program recursively generates all of the legal boards and checks for a winner alternating between minimizing and maximizing every turn. For some reason the algorithm never generates a winning board.

At first I assumed it was related to the algorithm not minimizing and maximizing for the right side but I checked and I don't see any reason the code wouldn't work

import random

def getOtherPlayer(letter):
    if letter == "X":
        return "O"
    else:
        return "X"


def getWinner(board):
    for i in range(3):
        # Check rows
        if board[i*3] == board[i*3+1] == board[i*3+2] != "":
            return board[i*3]
        # Check columns
        if board[i] == board[i+3] == board[i+6] != "":
            return board[i]
    # Check diagonals
    if board[0] == board[4] == board[8] != "":
        return board[0]
    if board[2] == board[4] == board[6] != "":
        return board[2]
    # Check for tie
    if "" not in board:
        return "Tie"
    # No winner yet
    return None

def test():
    board = ['X', '', 'O', 'X', 'O', 'X', 'O', 'X', 'O']
    print(getScore(board, 'X', False))

def getAvailableMoves(board):
    return [i for i in range(len(board)) if board[i] == ""]


def getScore(board, letter, isMaximizing):
    winner = getWinner(board)
    if winner == letter and isMaximizing:
        print('win')
        return 1  # current player wins
    elif winner == letter and isMaximizing is False:
        print('lose')
        return -1
    else:
        print(board)
        print(letter)
        print(isMaximizing)
        print('tie')
        return 0  # tie (no winner)


def minimax(board, letter, player):
    if player == 0:
        if letter == "X":
            bestScore = float('inf')
            isMaximizing = False
        else:
            bestScore = float('-inf')
            isMaximizing = True
    if player == 1:
        if letter == "O":
            bestScore = float('inf')
            isMaximizing = False
        else:
            bestScore = float('-inf')
            isMaximizing = True
    winner = getWinner(board)
    if winner is not None:
        print("terminal")
        return getScore(board, letter, isMaximizing)
    availableMoves = getAvailableMoves(board)
    if isMaximizing:
        for move in availableMoves:
            board[move] = letter
            score = minimax(board, getOtherPlayer(letter), player)
            board[move] = ""
            bestScore = max(score, bestScore)
    else:
        for move in availableMoves:
            board[move] = letter
            print(board)
            score = minimax(board, getOtherPlayer(letter), player)
            board[move] = ""
            bestScore = min(score, bestScore)
    return bestScore




def findBestMove(board, startingLetter, player):
    availableMoves = getAvailableMoves(board)
    bestMove = None
    bestScore = -9999
    for move in availableMoves:
        board[move] = startingLetter
        score = minimax(board, getOtherPlayer(startingLetter), player)
        board[move] = ""
        if score > bestScore:
            bestScore = score
            bestMove = move
    return bestMove


def printBoard(board):
    print()
    for i in range(3):
        print(board[i * 3:i * 3 + 3])

# Main Code
test()
letter = 'X'
board = [""] * 9
player = random.choice([0, 1])
print("You are playing as", player)
while True:
    printBoard(board)
    winner = getWinner(board)
    if winner is not None:
        if winner == "Tie":
            print("Tie")
        else:
            print("Winner:", winner)
        break
    if player == 0:
        if letter == "O":
            move = findBestMove(board, letter, player)
            print("Computer played move", move)
            board[move] = letter
        else:
            move = int(input("Enter move (0-8): "))
            while board[move] != "":
                move = int(input("Invalid move. Enter move (0-8): "))
            board[move] = letter
    elif player == 1:
        if letter == "X":
            move = findBestMove(board, letter, player)
            print("Computer played move", move)
            board[move] = letter
        else:
            move = int(input("Enter move (0-8): "))
            while board[move] != "":
                move = int(input("Invalid move. Enter move (0-8): "))
            board[move] = letter
    letter = getOtherPlayer(letter)

答案1

得分: 3

问题出在你的 getScore 函数中。

只有在赢家是 letter 时才返回非零值,但是当赢家是另一名玩家时却不返回。然而这将是你会遇到的情况:如果有一方获胜,那将是由最后移动的玩家来完成的,而在你调用 getScore 时,letter 实际上是下一个要出手的玩家。因此,即使游戏已经获胜,getScore 也会返回0。

以下是对该函数的建议修正:

def getScore(board, letter, isMaximizing):
    winner = getWinner(board)
    # 当游戏尚未结束,或者是平局时,返回0
    if not winner or winner == '平局':
        return 0
    # 当我们到达这里时,我们知道游戏已经结束。
    # 当 `letter` 赢家并且正在最大化时,或者
    # 当 `letter` 输家并且正在最小化时,返回1
    # 当 `letter` 输家并且正在最大化时,或者
    # 当 `letter` 赢家并且正在最小化时,返回-1
    # 简而言之:如果表达式 `winner == letter` 的布尔值与
    # `isMaximizing` 的布尔值相同,则返回1,否则返回-1
    return 1 if (winner == letter) == isMaximizing else -1

一个小提醒:在分析你的代码时,我发现你使用了一个变量 player,确定了人类玩家是否使用 "X" 和 "O",但该变量是一个数字 (0 或 1)。你可以通过将 "X" 和 "O" 也用于 player 值来避免一些代码重复。

另外,为什么不始终将 "X" 设定为最大化的玩家,无论是人类还是非人类玩家呢?当然,在 findBestMove 函数中需要更加小心一些,但其他的都会变得更简单。

英文:

The problem is in your getScore function.

It only returns a non-zero value when the winner is letter, but not when the winner is the other player. Yet that is the situation you will have: if there is a win, it will have been played by the player that moved last, while in your calls to getScore, letter is actually the player who is next to play. As a consequence, getScore will return 0 even when the game has been won.

Here is a suggested correction of that function:

def getScore(board, letter, isMaximizing):
    winner = getWinner(board)
    # When the game is not over yet, or it is a tie, then return 0
    if not winner or winner == 'Tie':
        return 0
    # When we get here, we know the game has been won.
    # Return 1  when `letter` is the winner and is maximizing, or
    #           when `letter` is the loser  and is minimizing
    # Return -1 when `letter` is the loser  and is maximizing, or
    #           when `letter` is the winner and is minimizing
    # In short: if the expression `winner == letter` has the same boolean value as
    #           `isMaximizing` return 1, else return -1
    return 1 if (winner == letter) == isMaximizing else -1

A small remark: When analysing your code I found it quite confusing that you used a variable player which determines whether the human player plays with "X" and "O", but the variable is a number (0 or 1). You can avoid some code repetition by using "X" and "O" also for the player value.

Also, why not make "X" always the maximizing player, no matter if it is the human or non-human player? Sure, you'd need a bit more care in the findBestMove function, but all the rest would become simpler.

huangapple
  • 本文由 发表于 2023年2月27日 09:15:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/75576062.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定