TicTacToe и минимакс - PullRequest
       98

TicTacToe и минимакс

0 голосов
/ 27 апреля 2020

Я молодой программист, который изучает python и пытается реализовать ИИ (используя минимакс) для игры в TicTacToe. Я начал смотреть учебник онлайн, но он был на JavaScript и поэтому не мог решить мою проблему. Я также взглянул на этот вопрос (Python минимакс для tictactoe ), но он не дал никаких ответов, и реализация значительно отличалась от моей.

РЕДАКТИРОВАТЬ: код, который вы найдете ниже, является правкой, предложенной одним из ответов (@water_ghosts).

РЕДАКТИРОВАТЬ # 2: я удалил возможные позиции, так как ИИ должен выбрать свободное поле, а не место из возможных позиций (это не сделало бы его умным при реализации минимакса :))

Теперь код не выдает никаких ошибок и функционирует должным образом, но есть одна небольшая вещь: ИИ всегда выбирает следующее доступное поле. Например, в ситуациях, когда я отхожу от выигрыша, вместо того, чтобы заблокировать свой вариант выигрыша, он выбирает следующее свободное место.

Если вам интересно, что там делает этот элемент 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 фона и не привык к этому :( Любая помощь будет принята с благодарностью

Я открыт для предложений и способов улучшить мой код и исправить эту проблему. Спасибо в развивайся и будь здоров, Кристи

Ответы [ 2 ]

0 голосов
/ 27 апреля 2020

optimalMove = 0 в play() и optimalMove = i в findOptimalField() объявляют две различные переменные, каждая из которых является локальной для функции, которая ее объявляет.

Если вы хотите, чтобы несколько функций имели доступ к одной и той же переменной, вы можете использовать ключевое слово global, но это обычно считается плохой практикой. Это может затруднить рассуждение о коде (например, var = x создает новую локальную переменную или перезаписывает значение глобальной переменной?) И не мешает вам случайно использовать переменную до ее объявления.

Поскольку вы пришли из Java фона, вы можете превратить его в класс, чтобы получить поведение, более похожее на то, что вы ожидаете, устраняя необходимость в глобальных значениях:

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 аргумент, который ссылается на текущий экземпляр, и вы можете использовать его для доступа к любым переменным, которые принадлежат экземпляру класса, а не к конкретному методу. Если вы не включите self. перед переменной, эта переменная все равно будет локальной для метода, который ее объявляет. В этом случае метод drawBoard() не сможет получить доступ к переменной answer, определенной в choice().

Вы можете создавать новые self. переменные в любом из методов класса, но лучше всего инициализировать их все в методе конструктора __init__, используя None в качестве заполнителя для переменных, которые пока не имеет значения.

0 голосов
/ 27 апреля 2020

Измените эту часть, ваша реализация вернет optimalMove, даже если она не go внутри if statement, и optimalMove не будет назначено в этой точке, поэтому поместите return внутри.

    if score > sampleScore:
        sampleScore = score
        optimalMove = i
        return optimalMove
...