Игра Python Tic-Tac-Toe с использованием функции tkinter Draw - PullRequest
0 голосов
/ 16 декабря 2018

Я писал игру Tic-Tac-Toe с минимаксным алгоритмом.Я уже добавил topWindow, когда проигрыватель проигрывает, но я не могу найти способ заставить программу показать сообщение Draw.Что я могу сделать в этом случае?

from tkinter import *
from tkinter.font import Font
from copy import deepcopy

class Board:
    def __init__(self, other=None):
        self.player = 'X'
        self.opponent = 'O'
        self.empty = ''
        self.size = 3
        self.fields = {}
        for y in range(self.size):
            for x in range(self.size):
                self.fields[x, y] = self.empty
        # copy constructor
        if other:
            self.__dict__ = deepcopy(other.__dict__)

    def move(self, x, y):
        board = Board(self)
        board.fields[x, y] = board.player
        (board.player, board.opponent) = (board.opponent, board.player)
        return board

    def __minimax(self, player):
        if self.won():
            if player:
                return (-1, None)
            else:
                return (+1, None)
        elif self.tied():
            return (0, None)
        elif player:
            best = (-2, None)
            for x, y in self.fields:
                if self.fields[x, y] == self.empty:
                    value = self.move(x, y).__minimax(not player)[0]
                    if value > best[0]:
                        best = (value, (x, y))
            return best
        else:
            best = (+2, None)
            for x, y in self.fields:
                if self.fields[x, y] == self.empty:
                    value = self.move(x, y).__minimax(not player)[0]
                    if value < best[0]:
                        best = (value, (x, y))
            return best

    def best(self):
        return self.__minimax(True)[1]

    def tied(self):
        for (x, y) in self.fields:
            if self.fields[x, y] == self.empty:
                return False
        return True

    def won(self):
        # horizontal
        for y in range(self.size):
            winning = []
            for x in range(self.size):
                if self.fields[x, y] == self.opponent:
                    winning.append((x, y))
            if len(winning) == self.size:
                return winning
        # vertical
        for x in range(self.size):
            winning = []
            for y in range(self.size):
                if self.fields[x, y] == self.opponent:
                    winning.append((x, y))
            if len(winning) == self.size:
                return winning
        # diagonal
        winning = []
        for y in range(self.size):
            x = y
            if self.fields[x, y] == self.opponent:
                winning.append((x, y))
        if len(winning) == self.size:
            return winning
        # other diagonal
        winning = []
        for y in range(self.size):
            x = self.size - 1 - y
            if self.fields[x, y] == self.opponent:
                winning.append((x, y))
        if len(winning) == self.size:
            return winning
        # default
        return None

    def __str__(self):
        string = ''
        for y in range(self.size):
            for x in range(self.size):
                string += self.fields[x, y]
            string += "\n"
        return string

class GUI:
    def __init__(self):
        self.app = Tk()
        self.app.title('Tic-Tac-Toe')
        self.app.resizable(width=False, height=False)
        self.board = Board()
        self.font = Font(family="Helvetica", size=32)
        self.buttons = {}
        for x, y in self.board.fields:
            handler = lambda x=x, y=y: self.move(x, y)
            button = Button(self.app, command=handler, font=self.font, width=2, height=1)
            button.grid(row=y, column=x)
            self.buttons[x, y] = button
        handler = lambda: self.reset()
        button = Button(self.app, text='Restart', command=handler)
        button.grid(row=self.board.size + 1, column=0, columnspan=self.board.size, sticky="WE")
        self.update()

    def reset(self):
        self.board = Board()
        self.update()
        self.top.destroy()

    def move(self, x, y):
        self.app.config(cursor="watch")
        self.app.update()
        self.board = self.board.move(x, y)
        self.update()
        move = self.board.best()
        if move:
            self.board = self.board.move(*move)
            self.update()
        self.app.config(cursor="")

    def topMessage(self):
        """Congratulation message pops up"""
        self.top = Toplevel()
        self.top.title("Congratulations!")
        self.top.minsize(300, 150)
        # top.geometry("200x150")
        self.topText = Label(self.top, text="O is the winner!", font=("Helvetica", 20), pady=20)
        self.topButton = Button(self.top, text="Restart", width=10, height=2, command=lambda: self.reset())
        self.topText.pack(fill=X)
        self.topButton.pack()

    def update(self):
        for (x, y) in self.board.fields:
            text = self.board.fields[x, y]
            self.buttons[x, y]['text'] = text
            self.buttons[x, y]['disabledforeground'] = 'black'
            if text == self.board.empty:
                self.buttons[x, y]['state'] = 'normal'
            else:
                self.buttons[x, y]['state'] = 'disabled'
        winning = self.board.won()
        if winning:
            for x, y in winning:
                self.buttons[x, y]['disabledforeground'] = 'red'
            for x, y in self.buttons:
                self.buttons[x, y]['state'] = 'disabled'
            self.topMessage()
        for (x, y) in self.board.fields:
            self.buttons[x, y].update()

    def mainloop(self):
        self.app.mainloop()

if __name__ == '__main__':
    GUI().mainloop()

................................................................................................................................................................................................................................................................................................................................

1 Ответ

0 голосов
/ 19 декабря 2018

Я не знаком по minimax algorithm.Но согласно вашему коду, когда переменная move равна None (результат self.board.best()) в GUI.move(), игра заканчивается.Однако ваш код не возвращает результат игры по окончании игры.

Предложите изменить Board.best(), чтобы вернуть и результат игры:

def best(self):
    return self.__minimax(True) # return the game result as well, not just the move

А также изменить GUI.move() чтобы обслужить указанное выше изменение и показать сообщение tie или lose по окончании игры:

def move(self, x, y):
    self.app.config(cursor="watch")
    self.app.update()
    self.board = self.board.move(x, y)
    self.update()
    result, move = self.board.best() # get the result of game as well
    if move:
        self.board = self.board.move(*move)
        self.update()
    else:
        # tie or lose game
        self.topMessage('tie' if result == 0 else 'lose')
    self.app.config(cursor="")

Наконец, измените topMessage(), чтобы принять еще один аргумент, результат игры:

def topMessage(self, result='win'):
    """Congratulation message pops up"""
    self.top = Toplevel()
    self.top.title("Congratulations!")
    self.top.minsize(300, 150)
    # top.geometry("200x150")
    if result == 'win':
        message = "O is the winner!"
    elif result == 'lose':
        message = "X is the winner!"
    else:
        message = "No winner!"
    self.topText = Label(self.top, text=message, font=("Helvetica", 20), pady=20)
    self.topButton = Button(self.top, text="Restart", width=10, height=2, command=lambda: self.reset())
    self.topText.pack(fill=X)
    self.topButton.pack()

Обратите внимание, что в GUI.reset() есть проблема: когда во время игры нажимается кнопка restart, переменная self.top еще не определена, и возникает исключение.Поэтому вы можете уничтожить self.top только тогда, когда оно определено:

def reset(self):
    self.board = Board()
    self.update()
    if hasattr(self, 'top'):
        self.top.destroy()
...