Tic Tac Toe 🎮 с помощью Python tkinter — часть 1

В этом уроке мы создадим базовую игру Tic Tac Toe 🎮 с помощью Python tkinter. Если вы новичок в tkinter, обратитесь к этому краткому курсу: https://dev.to/jothinkumar/getting-started-with-tkinter-crash-course-5a7m.

Веб-версия: https://dev.to/jothinkumar/tic-tac-toe-with-html-css-and-js-part-1-4ja2

Графический интерфейс 👀

Давайте продолжим и создадим графический интерфейс для игры.

Шаг 1: Создайте окно tkinter

import tkinter as tk

root = tk.Tk()
root.resizable(False, False)
root.title("Tic Tac Toe")
root.mainloop()
Войдите в полноэкранный режим Выйти из полноэкранного режима

Шаг 2: Добавьте игровую область и ярлык с текстом «Tic Tac Toe».

import tkinter as tk  

root = tk.Tk()  
root.resizable(False, False)  
root.title("Tic Tac Toe")  

tk.Label(root, text="Tic Tac Toe", font=('Ariel', 25)).pack()  

play_area = tk.Frame(root, width=300, height=300, bg='white')  
XO_points = []  
class XOPoint:  
    def __init__(self, x, y):  
        self.x = x  
        self.y = y  
        self.value = None  
        self.button = tk.Button(play_area, text="", width=10, height=5)  
        self.button.grid(row=x, column=y)  

    def reset(self):  
        self.button.configure(text="", bg='white')  
        self.value = None  
for x in range(1, 4):
    for y in range(1, 4):
        XOPoint(x, y) 
play_area.pack(pady=10, padx=10)  

root.mainloop()
Войти в полноэкранный режим Выйти из полноэкранного режима


Шаг 3: Сделайте графический интерфейс функциональным

Давайте сделаем графический интерфейс функциональным, изменяя текст и цвет кнопок при нажатии. Для этого нам нужно внести некоторые изменения в класс «XOPoint» в коде.

class XOPoint:  
    def __init__(self, x, y):
        self.x = x  
        self.y = y  
        self.value = None  
        self.button = tk.Button(play_area, text="", width=10, height=5, command=self.set)
        self.button.grid(row=x, column=y)

    def set(self):
        global current_chr
        if not self.value:
            self.button.configure(text=current_chr, bg='snow', fg='black')
            self.value = current_chr 
            if current_chr == "X":
                current_chr = "O"
            elif current_chr == "O":
                current_chr = "X"

    def reset(self):  
        self.button.configure(text="", bg='white')  
        self.value = None
Вход в полноэкранный режим Выход из полноэкранного режима

Определять выигрыш и рисовать 🤔.

Теперь давайте реализуем логику для определения win/draw.

Шаг 4: Реализация логики для определения выигрыша

Нам нужно проверять после каждого хода, выиграл ли X или O партию. Существует 8 возможных способов, которыми можно выиграть в Tic Tac Toe:


Давайте модифицируем код для определения победы в игре.

import tkinter as tk  

root = tk.Tk()  
root.resizable(False, False)  
root.title("Tic Tac Toe")  

tk.Label(root, text="Tic Tac Toe", font=('Ariel', 25)).pack()
current_chr = "X"

play_area = tk.Frame(root, width=300, height=300, bg='white')  
X_points = []
O_points = []
class XOPoint:  
    def __init__(self, x, y):
        self.x = x  
        self.y = y  
        self.value = None  
        self.button = tk.Button(play_area, text="", width=10, height=5, command=self.set)
        self.button.grid(row=x, column=y)

    def set(self):
        global current_chr
        if not self.value:
            self.button.configure(text=current_chr, bg='snow', fg='black')
            self.value = current_chr 
            if current_chr == "X":
                X_points.append(self)
                current_chr = "O"
            elif current_chr == "O":
                O_points.append(self)
                current_chr = "X"
        check_win()

    def reset(self):  
        self.button.configure(text="", bg='white')  
        self.value = None
for x in range(1, 4):
    for y in range(1, 4):
        XOPoint(x, y)
class WinningPossibility:
    def __init__(self, x1, y1, x2, y2, x3, y3):
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2
        self.x3 = x3
        self.y3 = y3
    def check(self, for_chr):
        p1_satisfied = False
        p2_satisfied = False
        p3_satisfied = False
        if for_chr == 'X':
            for point in X_points:
                if point.x == self.x1 and point.y == self.y1:
                    p1_satisfied = True
                elif point.x == self.x2 and point.y == self.y2:
                    p2_satisfied = True
                elif point.x == self.x3 and point.y == self.y3:
                    p3_satisfied = True
        elif for_chr == 'O':
            for point in O_points:
                if point.x == self.x1 and point.y == self.y1:
                    p1_satisfied = True
                elif point.x == self.x2 and point.y == self.y2:
                    p2_satisfied = True
                elif point.x == self.x3 and point.y == self.y3:
                    p3_satisfied = True
        return all([p1_satisfied, p2_satisfied, p3_satisfied])
winning_possibilities = [
    WinningPossibility(1, 1, 1, 2, 1, 3),
    WinningPossibility(2, 1, 2, 2, 2, 3),
    WinningPossibility(3, 1, 3, 2, 3, 3),
    WinningPossibility(1, 1, 2, 1, 3, 1),
    WinningPossibility(1, 2, 2, 2, 3, 2),
    WinningPossibility(1, 3, 2, 3, 3, 3),
    WinningPossibility(1, 1, 2, 2, 3, 3),
    WinningPossibility(3, 1, 2, 2, 1, 3)
]
def check_win():
    for possibility in winning_possibilities:
        if possibility.check('X'):
            print("X won!")
            return
        elif possibility.check('O'):
            print("O won!")
            return
play_area.pack(pady=10, padx=10)  

root.mainloop()
Вход в полноэкранный режим Выйти из полноэкранного режима

Шаг 5: Определить ничью

Если все 9 квадратов были заполнены, но никто не выиграл, то это ничья! Определить ничью очень просто. Просто добавьте следующий код в функцию «check_win».

if len(X_points) + len(O_points) == 9:
        print("Draw!")
Вход в полноэкранный режим Выход из полноэкранного режима

Усовершенствования

Теперь, когда все работает хорошо, мы можем улучшить его дальше.

Шаг 6: Метка состояния

Tic Tac Toe — это игра с графическим интерфейсом, а отображение результатов игры в графическом интерфейсе лучше, чем вывод их в консоль. Поэтому давайте создадим tkinter Label, через который мы сможем отображать результат. Добавьте следующий код:

status_label = tk.Label(root, text="", font=('Ariel', 15), bg='green', fg='snow')
status_label.pack(fill=tk.X)
Войти в полноэкранный режим Выйти из полноэкранного режима

Также необходимо внести некоторые изменения в функцию «check_win»:

def check_win():
    for possibility in winning_possibilities:
        if possibility.check('X'):
            status_label.configure(text="X won!")
            return
        elif possibility.check('O'):
            status_label.configure(text="O won!")
            return
    if len(X_points) + len(O_points) == 9:
        status_label.configure(text="Draw!")
Вход в полноэкранный режим Выйти из полноэкранного режима


Шаг 7: Отображение того, чей сейчас ход

Метка статуса не используется до тех пор, пока кто-то не выиграет игру. Давайте используем ее для отображения того, чья сейчас очередь — Х или О! Для этого нужно внести некоторые изменения в функцию «set» в классе «XOPoint».

def set(self):
        global current_chr
        if not self.value:
            self.button.configure(text=current_chr, bg='snow', fg='black')
            self.value = current_chr
            if current_chr == "X":
                X_points.append(self)
                current_chr = "O"
                status_label.configure(text="O's turn")
            elif current_chr == "O":
                O_points.append(self)
                current_chr = "X"
                status_label.configure(text="X's turn")
        check_win()
Вход в полноэкранный режим Выход из полноэкранного режима

Также необходимо изменить свойство text метки состояния:

status_label = tk.Label(root, text="X's turn", font=('Ariel', 15), bg='green', fg='snow')
Вход в полноэкранный режим Выйти из полноэкранного режима


Шаг 8: Создание кнопки «Воспроизвести еще раз

После завершения игры ее необходимо отключить и отобразить кнопку для повторного воспроизведения игры. Добавим следующий код, чтобы отключить игру после ее завершения:

XO_points = []
def disable_game():
    for point in XO_points:
        point.button.configure(state=tk.DISABLED)
Вход в полноэкранный режим Выйти из полноэкранного режима

Нам нужно внести изменения в функцию «check_win» и в циклы for, которые создают объекты «XOPoint»:

for x in range(1, 4):
    for y in range(1, 4):
        XO_points.append(XOPoint(x, y))

def check_win():
    for possibility in winning_possibilities:
        if possibility.check('X'):
            status_label.configure(text="X won!")
            disable_game()
            return
        elif possibility.check('O'):
            status_label.configure(text="O won!")
            disable_game()
            return
    if len(X_points) + len(O_points) == 9:
        status_label.configure(text="Draw!")
        disable_game()
Войти в полноэкранный режим Выход из полноэкранного режима


Добавьте следующий код для отображения кнопки Play again после завершения игры:

def play_again():
    global current_chr
    current_chr = 'X'
    for point in XO_points:
        point.button.configure(state=tk.NORMAL)
        point.reset()
    status_label.configure(text="X's turn")
    play_again_button.pack_forget()
play_again_button = tk.Button(root, text='Play again', font=('Ariel', 15), command=play_again)
Войти в полноэкранный режим Выйти из полноэкранного режима

Также необходимо внести изменения в функцию «disable_game» и функцию «reset» в классе «XOPoint»:

def disable_game():
    for point in XO_points:
        point.button.configure(state=tk.DISABLED)
    play_again_button.pack()

def reset(self):
        self.button.configure(text="", bg='lightgray')
        if self.value == "X":
            X_points.remove(self)
        elif self.value == "O":
            O_points.remove(self)
        self.value = None
Вход в полноэкранный режим Выход из полноэкранного режима

Резюме

В этом руководстве были выполнены следующие шаги:

  1. Создайте окно tkinter.
  2. Добавьте игровую область и ярлык с текстом «Tic Tac Toe».
  3. Сделать графический интерфейс функциональным.
  4. Реализовать логику для определения выигрыша.
  5. Определить ничью.
  6. Ярлык состояния.
  7. Отображение того, чей сейчас ход.
  8. Создать кнопку «Играть снова».

Финальный код:

import tkinter as tk

root = tk.Tk()
root.resizable(False, False)
root.title("Tic Tac Toe")

tk.Label(root, text="Tic Tac Toe", font=('Ariel', 25)).pack()
status_label = tk.Label(root, text="X's turn", font=('Ariel', 15), bg='green', fg='snow')
status_label.pack(fill=tk.X)
def play_again():
    global current_chr
    current_chr = 'X'
    for point in XO_points:
        point.button.configure(state=tk.NORMAL)
        point.reset()
    status_label.configure(text="X's turn")
    play_again_button.pack_forget()
play_again_button = tk.Button(root, text='Play again', font=('Ariel', 15), command=play_again)

current_chr = "X"

play_area = tk.Frame(root, width=300, height=300, bg='white')
XO_points = []
X_points = []
O_points = []
class XOPoint:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.value = None
        self.button = tk.Button(play_area, text="", width=10, height=5, command=self.set)
        self.button.grid(row=x, column=y)

    def set(self):
        global current_chr
        if not self.value:
            self.button.configure(text=current_chr, bg='snow', fg='black')
            self.value = current_chr
            if current_chr == "X":
                X_points.append(self)
                current_chr = "O"
                status_label.configure(text="O's turn")
            elif current_chr == "O":
                O_points.append(self)
                current_chr = "X"
                status_label.configure(text="X's turn")
        check_win()

    def reset(self):
        self.button.configure(text="", bg='lightgray')
        if self.value == "X":
            X_points.remove(self)
        elif self.value == "O":
            O_points.remove(self)
        self.value = None
for x in range(1, 4):
    for y in range(1, 4):
        XO_points.append(XOPoint(x, y))
class WinningPossibility:
    def __init__(self, x1, y1, x2, y2, x3, y3):
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2
        self.x3 = x3
        self.y3 = y3
    def check(self, for_chr):
        p1_satisfied = False
        p2_satisfied = False
        p3_satisfied = False
        if for_chr == 'X':
            for point in X_points:
                if point.x == self.x1 and point.y == self.y1:
                    p1_satisfied = True
                elif point.x == self.x2 and point.y == self.y2:
                    p2_satisfied = True
                elif point.x == self.x3 and point.y == self.y3:
                    p3_satisfied = True
        elif for_chr == 'O':
            for point in O_points:
                if point.x == self.x1 and point.y == self.y1:
                    p1_satisfied = True
                elif point.x == self.x2 and point.y == self.y2:
                    p2_satisfied = True
                elif point.x == self.x3 and point.y == self.y3:
                    p3_satisfied = True
        return all([p1_satisfied, p2_satisfied, p3_satisfied])
winning_possibilities = [
    WinningPossibility(1, 1, 1, 2, 1, 3),
    WinningPossibility(2, 1, 2, 2, 2, 3),
    WinningPossibility(3, 1, 3, 2, 3, 3),
    WinningPossibility(1, 1, 2, 1, 3, 1),
    WinningPossibility(1, 2, 2, 2, 3, 2),
    WinningPossibility(1, 3, 2, 3, 3, 3),
    WinningPossibility(1, 1, 2, 2, 3, 3),
    WinningPossibility(3, 1, 2, 2, 1, 3)
]
def disable_game():
    for point in XO_points:
        point.button.configure(state=tk.DISABLED)
    play_again_button.pack()
def check_win():
    for possibility in winning_possibilities:
        if possibility.check('X'):
            status_label.configure(text="X won!")
            disable_game()
            return
        elif possibility.check('O'):
            status_label.configure(text="O won!")
            disable_game()
            return
    if len(X_points) + len(O_points) == 9:
        status_label.configure(text="Draw!")
        disable_game()
play_area.pack(pady=10, padx=10)

root.mainloop()
Вход в полноэкранный режим Выход из полноэкранного режима

Ссылка на репозиторий GitHub: https://github.com/Jothin-kumar/tic-tac-toe.

Если вы нашли эту статью полезной, поставьте лайк ⭐ и следуйте за мной, чтобы получать все мои последние материалы.

Часть 2 этого руководства: https://dev.to/jothinkumar/tic-tac-toe-with-python-tkinter-part-2-9mp

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *