python - 井字游戏的极小极大算法通过用相同的符号填充每个空 space 来更新棋盘

python - minimax algorithm for tic tac toe updating the board by filling every empty space with the same symbol

我一直在尝试通过编写小游戏(例如井字游戏)来提高对 python 的理解。我从头开始创建了游戏副本,但随后开始寻找为游戏创建合理 AI 的方法。我决定使用 minimax 来实现这个目标。我找到了该算法的教程并尝试为游戏塑造它。

然而,当代码为 运行 时,在 ai 的第一个回合。棋盘上剩下的每个空 space 都用相同的符号填充,迫使获胜,使游戏无法玩。

由于我对算法不熟悉且缺乏经验,所以我从 youtube 教程中获取代码并对其进行编辑,使其适用于游戏。

这是代码

import random
import time
import sys
from sys import maxsize




#converts numbers from the board to X O and "" for printing out the board 
def numToSymbol(board):
    visual = board
    for i in range(0, 9):
        if visual[i] == -1:
            visual[i]= "X"
        elif visual[i] == 1:
            visual[i] = "O"
        elif visual[i] == 0:
            visual[i] = " "
    return visual


def symbolToNum(visual):
    board = visual
    for i in range(0, 9):
        if board[i] == "X":
            board[i] = -1
        elif board[i] == "O":
            board[i] = 1
        elif board[i] == " ":
            board[i] = 0
    return board


# prints the board, first coverting numbers throught numtoSymbol
def drawBoard():
    visual = numToSymbol(board)
    print(
        "|"+ str(visual[0]) + "|" + str(visual[1]) + "|" + str(visual[2]) + "| \n"
        "-------\n"
        "|"+ str(visual[3]) + "|" + str(visual[4]) + "|" + str(visual[5]) + "| \n"
        "-------\n"
        "|"+ str(visual[6]) + "|" + str(visual[7]) + "|" + str(visual[8]) + "| \n"
        "-------")
    symbolToNum(visual)


# checks the possible options for 3 in a row and if any are all held by one side returns true
def checkWin(board, winConditions, ai):
    if ai == True:
        win = 3
    if ai == False:
        win = -3
    for row in winConditions:
        checkA = board[row[0]]
        checkB = board[row[1]]
        checkC = board[row[2]]
        if checkA + checkB + checkC == int(win):
           return True

    return False

def checkDraw(board):
    emptyTile = 9
    for tile in board:
        if tile != 0:
            emptyTile -= 1
    if emptyTile == 0:
        completion(2)

def availableMoves(board, bonus):
    availableSlots = []
    for position in range(0,9):
        if board[position] == 0:
            availableSlots.append(position + bonus)
    return availableSlots



def playerTurn(board, remainingTurns):
    board = board
    checkDraw(board)
    visual =drawBoard()
    print("pick one of the available slots \n")
    availableSlots = availableMoves(board, 1)
    print(availableSlots)
    choice = int(input('Select a tile from those shown above:\n')) -1
    while  True:    
        if 0 <= choice <= 8:
            if board[choice] == 0:
                board[choice] = -1
                break
            else:
                choice = int(input('Select a tile from those shown above which has not already been selected:\n')) -1
        else:
            choice = int(input('Select a tile from those shown above:\n')) -1
    visual = drawBoard()
    if checkWin(board, winConditions, False) == True:
        completion(0)
    else:
        aiTurn(board, remainingTurns - 1)


class Node(object):

    def __init__(self,  remainingTurns, playerNum, board, value = 0):
        self.depth = remainingTurns
        self.playerNum = playerNum
        self.board = board
        self.value = value
        self.moves = availableMoves(self.board, 0)
        self.children = []
        self.createChildren()

    def availableMoves(self, board):
        self.availableSlots = []
        for position in range(0,9):
            if board[position] == 0:
                availableSlots.append(position)
        return self.availableSlots

    def winCheck(self, state, playerNum):
        self.winConditions = [[0, 1, 2],[3, 4, 5],[6, 7, 8],[0, 3, 6],[1, 4, 7],[2, 5, 8],[0, 4, 8],[2, 4, 6]]
        self.win = 3
        for row in self.winConditions:
            self.checkA = self.state[row[0]]
            self.checkB = self.state[row[1]]
            self.checkC = self.state[row[2]]
            if self.checkA + self.checkB + self.checkC == int(self.win * self.playerNum):
               return True
        return False

    def createChildren(self):
        if self.depth > 0:
            for i in self.moves:
                self.state = self.board
                self.state[i] = self.playerNum
                print(board)
                self.children.append(Node(self.depth - 1, self.playerNum * -1, self.state, self.realVal(self.state, self.playerNum) ))


    def realVal(self, value, playerNum):
        value = self.winCheck(self.state, self.playerNum)
        if value == True:
            return maxsize * self.playerNum
        else:
            return 0




def minimax(node, depth, playerNum):
    if depth == 0 or abs(node.value) == maxsize:
        return node.value

    bestValue = maxsize * -playerNum

    for i in range(len(node.children)):
        child = node.children[i]
        val = minimax(child, depth - 1, -playerNum)
        if (abs(maxsize * playerNum - val) < abs(maxsize * playerNum - bestValue)):
            bestValue = val
    return bestValue


def aiTurn(board, remainingTurns):
    checkDraw(board)
    print('Waiting for ai')
    time.sleep(2)
    board = board
    state = board
    currentPlayer = 1
    tree = Node(remainingTurns, currentPlayer, state)
    bestChoice = -100
    bestValue = maxsize * -1
    for i in range(len(tree.children)):
        n_child = tree.children[i]
        i_val = minimax(n_child, remainingTurns, -currentPlayer,)
        if (abs(currentPlayer * maxsize - i_val) <= abs(currentPlayer * maxsize - bestValue)):
            bestValue = i_val
            bestChoice = i
    print(bestChoice)
    print(board)
    #choice = minimax(tree, remainingTurns, 1)
    board[bestChoice] = 1
    print(board)
    if checkWin(board, winConditions, True) == True:
        completion(1)
    else:
        playerTurn(board, remainingTurns - 1)



def completion(state):
    if state == 1:
        print('The computer has won this game.')
        choice = str(input('press y if you would like to play another game; Press n if you would not \n'))
        if choice == 'y' or choice == 'Y':
            playGame()
        else:
            sys.exit()
    if state == 0:
        print('you win!')
        choice = str(input('press y if you would like to play another game; Press n if you would not \n'))
        if choice == 'y' or choice == 'Y':
            playGame()
        else:
            sys.exit()
    if state == 2:
        print('you drew')
        choice = str(input('press y if you would like to play another game; Press n if you would not \n'))
        if choice == 'y' or choice == 'Y':
            playGame()
        else:
            sys.exit()        




def playGame():
    global board
    board = [0,0,0,0,0,0,0,0,0]

    global winConditions
    winConditions = [
                [0, 1, 2],
                [3, 4, 5],
                [6, 7, 8],
                [0, 3, 6],
                [1, 4, 7],
                [2, 5, 8],
                [0, 4, 8],
                [2, 4, 6]
                ]


    global remainingTurns
    remainingTurns = 9
    playerFirst = random.choice([True, False])

    if playerFirst == True:
        playerTurn(board, remainingTurns)
    else:
        aiTurn(board, remainingTurns)


if __name__ == '__main__':
    playGame() 

这是上面代码的输出

[1, 0, 0, 0, 0, 0, 0, 0, 0]
[1, -1, 0, 0, 0, 0, 0, 0, 0]
[1, -1, 1, 0, 0, 0, 0, 0, 0]
[1, -1, 1, -1, 0, 0, 0, 0, 0]
[1, -1, 1, -1, 1, 0, 0, 0, 0]
[1, -1, 1, -1, 1, -1, 0, 0, 0]
[1, -1, 1, -1, 1, -1, 1, 0, 0]
[1, -1, 1, -1, 1, -1, 1, -1, 0]
[1, -1, 1, -1, 1, -1, 1, -1, 1]
[1, -1, 1, -1, 1, -1, 1, -1, -1]
[1, -1, 1, -1, 1, -1, 1, 1, -1]
[1, -1, 1, -1, 1, -1, 1, 1, 1]
[1, -1, 1, -1, 1, -1, -1, 1, 1]
[1, -1, 1, -1, 1, -1, -1, -1, 1]
[1, -1, 1, -1, 1, -1, -1, -1, -1
[1, -1, 1, -1, 1, 1, -1, -1, -1]
[1, -1, 1, -1, 1, 1, 1, -1, -1]
[1, -1, 1, -1, 1, 1, 1, 1, -1]
[1, -1, 1, -1, 1, 1, 1, 1, 1]
[1, -1, 1, -1, -1, 1, 1, 1, 1]
[1, -1, 1, -1, -1, -1, 1, 1, 1]
[1, -1, 1, -1, -1, -1, -1, 1, 1]
[1, -1, 1, -1, -1, -1, -1, -1, 1
[1, -1, 1, -1, -1, -1, -1, -1, -
[1, -1, 1, 1, -1, -1, -1, -1, -1
[1, -1, 1, 1, 1, -1, -1, -1, -1]
[1, -1, 1, 1, 1, 1, -1, -1, -1]
[1, -1, 1, 1, 1, 1, 1, -1, -1]
[1, -1, 1, 1, 1, 1, 1, 1, -1]
[1, -1, 1, 1, 1, 1, 1, 1, 1]
[1, -1, -1, 1, 1, 1, 1, 1, 1]
[1, -1, -1, -1, 1, 1, 1, 1, 1]
[1, -1, -1, -1, -1, 1, 1, 1, 1]
[1, -1, -1, -1, -1, -1, 1, 1, 1]
[1, -1, -1, -1, -1, -1, -1, 1, 1
[1, -1, -1, -1, -1, -1, -1, -1,
[1, -1, -1, -1, -1, -1, -1, -1,
[1, 1, -1, -1, -1, -1, -1, -1, -
[1, 1, 1, -1, -1, -1, -1, -1, -1
[1, 1, 1, 1, -1, -1, -1, -1, -1]
[1, 1, 1, 1, 1, -1, -1, -1, -1]
[1, 1, 1, 1, 1, 1, -1, -1, -1]
[1, 1, 1, 1, 1, 1, 1, -1, -1]
[1, 1, 1, 1, 1, 1, 1, 1, -1]
[1, 1, 1, 1, 1, 1, 1, 1, 1]
8
[1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1]
The computer has won this game.

有什么建议吗?

问题出在你初始化你的节点时,你只是复制 board:

的引用
self.board = board

而您应该复制这些值

self.board = board[::]

否则您所有的节点对象都引用同一个板:全局板。