TicTacToe 和 Minimax

TicTacToe and Minimax

我是一名年轻的程序员,正在学习 python 并努力实现 AI(使用 minimax)来玩 TicTacToe。我开始在网上看教程,但是教程在 JavaScript 上,因此无法解决我的问题。我也看过这个问题 ( Python minimax for tictactoe ),但它没有任何答案,而且实现方式与我的有很大不同。

编辑:您将在下面找到的代码是其中一个答案 (@water_ghosts) 建议的编辑。

编辑 #2:我删除了 possiblePositions,因为 AI 应该从 possiblePositions 中选择一个自由字段而不是一个位置(这在实现 minimax 时不会让它变得那么聪明 :))

现在代码完全没有给出任何错误并且可以正常运行,但有一件小事:AI 总是选择下一个可用字段。例如,在我远离获胜的情况下,它不会阻止我的获胜选项,而是选择下一个空闲位置。

如果您想知道 elements dict 在那里做什么:我只是想确保程序选择了最佳索引...

这是我的代码:

class TicTacToe:
    def __init__(self):

        self.board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]

        self.playerSymbol = ""
        self.playerPosition = []

        self.aiSymbol = ""
        self.aiPosition = []

        self.score = 0

        self.winner = None

        self.scoreBoard = {
            self.playerSymbol: -1,
            self.aiSymbol: 1,
            "tie": 0
        }

        self.turn = 0

        self.optimalMove = int()

    def drawBoard(self):
        print(self.board[0] + " | " + self.board[1] + " | " + self.board[2])
        print("___" + "___" + "___")
        print(self.board[3] + " | " + self.board[4] + " | " + self.board[5])
        print("___" + "___" + "___")
        print(self.board[6] + " | " + self.board[7] + " | " + self.board[8])

    def choice(self):

        answer = input("What do you want to play as? (type x or o) ")

        if answer.upper() == "X":
            self.playerSymbol = "X"
            self.aiSymbol = "O"
        else:
            self.playerSymbol = "O"
            self.aiSymbol = "X"

    def won(self):

        winningPositions = [{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, {0, 4, 8}, {2, 4, 6}, {0, 3, 6}, {1, 4, 7}, {2, 5, 8}]

        for position in winningPositions:
            if position.issubset(self.playerPosition):
                self.winner = self.playerSymbol
                print("Player Wins :)")
                return True
            elif position.issubset(self.aiPosition):
                self.winner = self.aiSymbol
                print("AI wins :(")
                return True
        if self.board.count(" ") == 0:
            self.winner = "tie"
            print("Guess it's a draw")
            return True

        return False

    def findOptimalPosition(self):

        bestScore = float("-Infinity")
        elements = {}  # desperate times call for desperate measures

        for i in range(9):
            if self.board[i] == " ":
                self.board[i] = self.aiSymbol  # AI quasi made the move here
                if self.minimax(True) > bestScore:
                    bestScore = self.score
                    elements[i] = bestScore
                self.board[i] = " "
        return max(elements, key=lambda k: elements[k])

    def minimax(self, isMaximizing):

        if self.winner is not None:
            return self.scoreBoard[self.winner]

        if isMaximizing:
            bestScore = float("-Infinity")
            for i in range(9):
                if self.board[i] == " ":
                    self.board[i] = self.aiSymbol
                    bestScore = max(self.minimax(False), bestScore)
                    self.board[i] = " "
            return bestScore
        else:
            bestScore = float("Infinity")
            for i in range(9):
                if self.board[i] == " ":
                    self.board[i] = self.playerSymbol
                    bestScore = min(self.minimax(True), bestScore)
                    self.board[i] = " "
            return bestScore

    def play(self):

        self.choice()

        while not self.won():
            if self.turn % 2 == 0:
                pos = int(input("Where would you like to play? (0-8) "))
                self.playerPosition.append(pos)
                self.board[pos] = self.playerSymbol
                self.turn += 1
                self.drawBoard()
            else:
                aiTurn = self.findOptimalPosition()
                self.aiPosition.append(aiTurn)
                self.board[aiTurn] = self.aiSymbol
                self.turn += 1
                print("\n")
                print("\n")
                self.drawBoard()
        else:
            print("Thanks for playing :)")


tictactoe = TicTacToe()
tictactoe.play()


我来自 java 背景,对此不习惯 :( 任何帮助将不胜感激

我乐于接受改进我的代码和解决此问题的建议和方法。 在此先感谢并保持健康, 克里斯蒂

改变这部分,你的实现将 return optimalMove 即使它没有进入 if statement,并且 optimalMove 不会在那个时候分配, 所以把 return 放在里面。

    if score > sampleScore:
        sampleScore = score
        optimalMove = i
        return optimalMove
play() 中的

optimalMove = 0findOptimalField() 中的 optimalMove = i 声明了两个不同的变量,每个变量都是声明它的函数的局部变量。

如果您希望多个函数访问同一个变量,您可以使用 global 关键字,但这通常被认为是一种不好的做法。它会使代码难以推理(例如 var = x 是创建一个新的局部变量还是覆盖全局变量的值?)并且它不会阻止您在变量声明之前意外使用它。

由于您来自 Java 背景,您可以将其转变为 class 以获得更符合您预期的行为,从而消除对全局变量的需求:

class TicTacToe:
    def __init__(self):
        self.board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]

        self.playerSymbol = ""
        self.playerPosition = []

        self.aiSymbol = ""
        self.aiPosition = []

        self.score = 0

        self.playerSymbol = None
        self.aiSymbol = None
        ...

    def drawBoard(self):
        print(self.board[0] + " | " + self.board[1] + " | " + self.board[2])
        ...

    def choice(self):
        answer = input("What do you want to play as? (type x or o) ")

        if answer.upper() == "X":
            self.playerSymbol = "X"
            self.aiSymbol = "O"
        ...

每个方法现在都采用一个显式 self 参数来引用当前实例,您可以使用它来访问属于 class 实例而不是特定方法的任何变量。如果您不在变量前包含 self. ,该变量对于声明它的方法仍然是局部的。在这种情况下,drawBoard() 方法将无法访问 choice() 中定义的 answer 变量。

您可以在 class 的任何方法中创建新的 self. 变量,但最佳做法是在 __init__ 构造函数方法中初始化所有变量,使用 None 作为还没有值的变量的占位符。

我将此作为答案发布,以防万一将来有人遇到同样的问题:)

我遇到的主要问题(除了我糟糕的编程风格之外)是我忘记更新列表 playerPosition 和 aiPosition 的内容。 您可以查看工作代码中的其余更改:

class TicTacToe:
    def __init__(self):

        self.board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]

        self.playerSymbol = ""
        self.playerPosition = []

        self.aiSymbol = ""
        self.aiPosition = []

        self.winner = None

        self.scoreBoard = None

        self.turn = 0

        self.optimalMove = int()

    def drawBoard(self):
        print(self.board[0] + " | " + self.board[1] + " | " + self.board[2])
        print("___" + "___" + "___")
        print(self.board[3] + " | " + self.board[4] + " | " + self.board[5])
        print("___" + "___" + "___")
        print(self.board[6] + " | " + self.board[7] + " | " + self.board[8])

    def choice(self):

        answer = input("What do you want to play as? (type x or o) ")

        if answer.upper() == "X":
            self.playerSymbol = "X"
            self.aiSymbol = "O"
        else:
            self.playerSymbol = "O"
            self.aiSymbol = "X"

        self.scoreBoard = {
            self.playerSymbol: -1,
            self.aiSymbol: 1,
            "tie": 0
        }

    def availableMoves(self):

        moves = []
        for i in range(0, len(self.board)):
            if self.board[i] == " ":
                moves.append(i)
        return moves

    def won_print(self):
        self.won()
        if self.winner == self.aiSymbol:
            print("AI wins :(")
            exit(0)
        elif self.winner == self.playerSymbol:
            print("Player Wins :)")
            exit(0)
        elif self.winner == "tie":
            print("Guess it's a draw")
            exit(0)

    def won(self):

        winningPositions = [{0, 1, 2}, {3, 4, 5}, {6, 7, 8},
                            {0, 4, 8}, {2, 4, 6}, {0, 3, 6},
                            {1, 4, 7}, {2, 5, 8}]

        for position in winningPositions:
            if position.issubset(self.playerPosition):
                self.winner = self.playerSymbol
                return True
            elif position.issubset(self.aiPosition):
                self.winner = self.aiSymbol
                return True
        if self.board.count(" ") == 0:
            self.winner = "tie"
            return True

        self.winner = None
        return False

    def set_i_ai(self, i):
        self.aiPosition.append(i)
        self.board[i] = self.aiSymbol

    def set_clear_for_ai(self, i):
        self.aiPosition.remove(i)
        self.board[i] = " "

    def set_i_player(self, i):
        self.playerPosition.append(i)
        self.board[i] = self.playerSymbol

    def set_clear_for_player(self, i):
        self.playerPosition.remove(i)
        self.board[i] = " "

    def findOptimalPosition(self):

        bestScore = float("-Infinity")
        elements = {}  # desperate times call for desperate measures

        for i in self.availableMoves():
            self.set_i_ai(i)
            score = self.minimax(False)
            if score > bestScore:
                bestScore = score
                elements[i] = bestScore
            self.set_clear_for_ai(i)
        if bestScore == 1:
            print("you messed up larry")
        elif bestScore == 0:
            print("hm")
        else:
            print("whoops i made a prog. error")
        return max(elements, key=lambda k: elements[k])

    def minimax(self, isMaximizing):

        if self.won():
            return self.scoreBoard[self.winner]

        if isMaximizing:
            bestScore = float("-Infinity")
            for i in self.availableMoves():
                self.set_i_ai(i)
                bestScore = max(self.minimax(False), bestScore)
                self.set_clear_for_ai(i)
            return bestScore
        else:
            bestScore = float("Infinity")
            for i in self.availableMoves():
                self.set_i_player(i)
                bestScore = min(self.minimax(True), bestScore)
                self.set_clear_for_player(i)
            return bestScore

    def play(self):

        self.choice()

        while not self.won_print():
            if self.turn % 2 == 0:
                pos = int(input("Where would you like to play? (0-8) "))
                self.playerPosition.append(pos)
                self.board[pos] = self.playerSymbol
                self.turn += 1
                self.drawBoard()
            else:
                aiTurn = self.findOptimalPosition()
                self.aiPosition.append(aiTurn)
                self.board[aiTurn] = self.aiSymbol
                self.turn += 1
                print("\n")
                print("\n")
                self.drawBoard()
        else:
            print("Thanks for playing :)")


if __name__ == '__main__':
    tictactoe = TicTacToe()
    tictactoe.play()

但如前所述,代码可能有效,但在逻辑和结构方面存在很多问题,因此请不要直接复制粘贴 :))