Крестики-нолики с графическим интерфейсом на Python

Статьи

Введение

В данной статье напишем код игры крестики-нолики с графическим интерфейсом на Python с использованием библиотеки tkinter.

Т.к. tkinter входит в стандартную библиотеку Python — устанавливать его не придётся.

Создание корневого окна

Для написания кода нам понадобится импортировать tkinter и messagebox из tkinter.

import tkinter as tk
from tkinter import messagebox

Теперь создадим корневое окно и сразу вызовем метод mainloop() (главный цикл обработки событий).

import tkinter as tk
from tkinter import messagebox

root = tk.Tk()
root.mainloop()

Создание класса TicTacToe и инициализация атрибутов

Перейдём к созданию класса, в котором будет написана вся логика игры. Создадим класс TicTacToe и инициализируем все необходимые атрибуты:

import tkinter as tk
from tkinter import messagebox


class TicTacToe:
    def __init__(self, master):
        self.master = master  # Определение корневого окна
        self.master.title("Крестики-нолики")  # Установка заголовка окна

        self.current_player = "X"  # Установка начального игрока
        self.board = [" " for _ in range(9)]  # Создание пустого игрового поля

        self.buttons = []  # Создание списка для хранения кнопок игрового поля


root = tk.Tk()
root.mainloop()

Также при помощи цикла создадим игровое поле в виде кнопок:

import tkinter as tk
from tkinter import messagebox


class TicTacToe:
    def __init__(self, master):
        self.master = master  # Определение корневого окна
        self.master.title("Крестики-нолики")  # Установка заголовка окна

        self.current_player = "X"  # Установка начального игрока
        self.board = [" " for _ in range(9)]  # Создание пустого игрового поля

        self.buttons = []  # Создание списка для хранения кнопок игрового поля
        for i in range(3):
            row = []  # Создание списка для хранения кнопок в строке
            for j in range(3):
                # Создание кнопки и привязка метода on_button_click к событию нажатия
                button = tk.Button(self.master, text=" ", font=("Arial", 20), width=5, height=2,
                                   command=lambda i=i, j=j: self.on_button_click(i, j))
                button.grid(row=i, column=j, sticky="nsew")  # Размещение кнопки на сетке
                row.append(button)  # Добавление кнопки в строку
            self.buttons.append(row)  # Добавление строки кнопок в общий список

        self.reset_button = tk.Button(self.master, text="Новая игра", command=self.reset_game)  # Создание кнопки для сброса игры
        self.reset_button.grid(row=3, column=0, columnspan=3, sticky="nsew")  # Размещение кнопки на сетке


root = tk.Tk()
root.mainloop()

Создание метода on_button_click()

Мы можем заметить, что в цикле во время создания кнопки идёт привязка к методу on_button_click(). Данный метод будет предназначен для того, чтобы заполнить игровую клетку определённым символом (крестиком или ноликом). Также при достижении победы одним из игроков данный метод отобразит всплывающее окно, оповещающее о победе одного из игроков или ничьей. Создадим данный метод:

import tkinter as tk
from tkinter import messagebox


class TicTacToe:
    def __init__(self, master):
        self.master = master  # Определение корневого окна
        self.master.title("Крестики-нолики")  # Установка заголовка окна

        self.current_player = "X"  # Установка начального игрока
        self.board = [" " for _ in range(9)]  # Создание пустого игрового поля

        self.buttons = []  # Создание списка для хранения кнопок игрового поля
        for i in range(3):
            row = []  # Создание списка для хранения кнопок в строке
            for j in range(3):
                # Создание кнопки и привязка метода on_button_click к событию нажатия
                button = tk.Button(self.master, text=" ", font=("Arial", 20), width=5, height=2,
                                   command=lambda i=i, j=j: self.on_button_click(i, j))
                button.grid(row=i, column=j, sticky="nsew")  # Размещение кнопки на сетке
                row.append(button)  # Добавление кнопки в строку
            self.buttons.append(row)  # Добавление строки кнопок в общий список

        self.reset_button = tk.Button(self.master, text="Новая игра", command=self.reset_game)  # Создание кнопки для сброса игры
        self.reset_button.grid(row=3, column=0, columnspan=3, sticky="nsew")  # Размещение кнопки на сетке

    def on_button_click(self, i, j):
        if self.board[i * 3 + j] == " ":  # Проверка, что клетка пуста
            self.board[i * 3 + j] = self.current_player  # Установка символа текущего игрока в клетку
            self.buttons[i][j].config(text=self.current_player)  # Обновление текста на кнопке
            if self.check_winner(i, j):  # Проверка на победу
                messagebox.showinfo("Победа", f"Игрок {self.current_player} выиграл!")  # Отображение сообщения о победе
                self.reset_game()  # Сброс игры
            elif " " not in self.board:  # Проверка на ничью
                messagebox.showinfo("Ничья", "Ничья!")  # Отображение сообщения о ничьей
                self.reset_game()  # Сброс игры
            else:
                self.current_player = "O" if self.current_player == "X" else "X"  # Смена игрока


root = tk.Tk()
root.mainloop()

Создание метода check_winner()

Теперь мы можем заметить, что в методе on_button_click() присутствует вызов метода check_winner(). Данный метод, который мы сейчас напишем предназначен для проверки, победил ли на данный момент кто-то из игроков, или нет.

import tkinter as tk
from tkinter import messagebox


class TicTacToe:
    def __init__(self, master):
        self.master = master  # Определение корневого окна
        self.master.title("Крестики-нолики")  # Установка заголовка окна

        self.current_player = "X"  # Установка начального игрока
        self.board = [" " for _ in range(9)]  # Создание пустого игрового поля

        self.buttons = []  # Создание списка для хранения кнопок игрового поля
        for i in range(3):
            row = []  # Создание списка для хранения кнопок в строке
            for j in range(3):
                # Создание кнопки и привязка метода on_button_click к событию нажатия
                button = tk.Button(self.master, text=" ", font=("Arial", 20), width=5, height=2,
                                   command=lambda i=i, j=j: self.on_button_click(i, j))
                button.grid(row=i, column=j, sticky="nsew")  # Размещение кнопки на сетке
                row.append(button)  # Добавление кнопки в строку
            self.buttons.append(row)  # Добавление строки кнопок в общий список

        self.reset_button = tk.Button(self.master, text="Новая игра", command=self.reset_game)  # Создание кнопки для сброса игры
        self.reset_button.grid(row=3, column=0, columnspan=3, sticky="nsew")  # Размещение кнопки на сетке

    def on_button_click(self, i, j):
        if self.board[i * 3 + j] == " ":  # Проверка, что клетка пуста
            self.board[i * 3 + j] = self.current_player  # Установка символа текущего игрока в клетку
            self.buttons[i][j].config(text=self.current_player)  # Обновление текста на кнопке
            if self.check_winner(i, j):  # Проверка на победу
                messagebox.showinfo("Победа", f"Игрок {self.current_player} выиграл!")  # Отображение сообщения о победе
                self.reset_game()  # Сброс игры
            elif " " not in self.board:  # Проверка на ничью
                messagebox.showinfo("Ничья", "Ничья!")  # Отображение сообщения о ничьей
                self.reset_game()  # Сброс игры
            else:
                self.current_player = "O" if self.current_player == "X" else "X"  # Смена игрока

    def check_winner(self, i, j):
        row = all(self.board[i*3 + col] == self.current_player for col in range(3))  # Проверка по горизонтали
        col = all(self.board[row*3 + j] == self.current_player for row in range(3))  # Проверка по вертикали
        diag1 = all(self.board[i*3 + i] == self.current_player for i in range(3))  # Проверка по диагонали
        diag2 = all(self.board[i*3 + 2-i] == self.current_player for i in range(3))  # Проверка по диагонали
        return any([row, col, diag1, diag2])  # Возвращает True, если есть победа


root = tk.Tk()
root.mainloop()

Создание метода check_winner()

Последний метод который мы создадим — это check_winner(), который также неоднократно вызывается и в других методах. Он предназначен для начала игры с нуля. Т.е. благодаря нему поле будет очищаться, также будет установлен начальный игрок в виде крестика.

import tkinter as tk
from tkinter import messagebox


class TicTacToe:
    def __init__(self, master):
        self.master = master  # Определение корневого окна
        self.master.title("Крестики-нолики")  # Установка заголовка окна

        self.current_player = "X"  # Установка начального игрока
        self.board = [" " for _ in range(9)]  # Создание пустого игрового поля

        self.buttons = []  # Создание списка для хранения кнопок игрового поля
        for i in range(3):
            row = []  # Создание списка для хранения кнопок в строке
            for j in range(3):
                # Создание кнопки и привязка метода on_button_click к событию нажатия
                button = tk.Button(self.master, text=" ", font=("Arial", 20), width=5, height=2,
                                   command=lambda i=i, j=j: self.on_button_click(i, j))
                button.grid(row=i, column=j, sticky="nsew")  # Размещение кнопки на сетке
                row.append(button)  # Добавление кнопки в строку
            self.buttons.append(row)  # Добавление строки кнопок в общий список

        self.reset_button = tk.Button(self.master, text="Новая игра", command=self.reset_game)  # Создание кнопки для сброса игры
        self.reset_button.grid(row=3, column=0, columnspan=3, sticky="nsew")  # Размещение кнопки на сетке

    def on_button_click(self, i, j):
        if self.board[i * 3 + j] == " ":  # Проверка, что клетка пуста
            self.board[i * 3 + j] = self.current_player  # Установка символа текущего игрока в клетку
            self.buttons[i][j].config(text=self.current_player)  # Обновление текста на кнопке
            if self.check_winner(i, j):  # Проверка на победу
                messagebox.showinfo("Победа", f"Игрок {self.current_player} выиграл!")  # Отображение сообщения о победе
                self.reset_game()  # Сброс игры
            elif " " not in self.board:  # Проверка на ничью
                messagebox.showinfo("Ничья", "Ничья!")  # Отображение сообщения о ничьей
                self.reset_game()  # Сброс игры
            else:
                self.current_player = "O" if self.current_player == "X" else "X"  # Смена игрока

    def check_winner(self, i, j):
        row = all(self.board[i*3 + col] == self.current_player for col in range(3))  # Проверка по горизонтали
        col = all(self.board[row*3 + j] == self.current_player for row in range(3))  # Проверка по вертикали
        diag1 = all(self.board[i*3 + i] == self.current_player for i in range(3))  # Проверка по диагонали
        diag2 = all(self.board[i*3 + 2-i] == self.current_player for i in range(3))  # Проверка по диагонали
        return any([row, col, diag1, diag2])  # Возвращает True, если есть победа

    def reset_game(self):
        self.current_player = "X"  # Установка начального игрока
        self.board = [" " for _ in range(9)]  # Очистка игрового поля
        for i in range(3):
            for j in range(3):
                self.buttons[i][j].config(text=" ")  # Очистка текста на кнопках


root = tk.Tk()
root.mainloop()

Проверка работы кода

Осталось создать экземпляр нашего класса TicTacToe и передать в него root.

import tkinter as tk
from tkinter import messagebox


class TicTacToe:
    def __init__(self, master):
        self.master = master  # Определение корневого окна
        self.master.title("Крестики-нолики")  # Установка заголовка окна

        self.current_player = "X"  # Установка начального игрока
        self.board = [" " for _ in range(9)]  # Создание пустого игрового поля

        self.buttons = []  # Создание списка для хранения кнопок игрового поля
        for i in range(3):
            row = []  # Создание списка для хранения кнопок в строке
            for j in range(3):
                # Создание кнопки и привязка метода on_button_click к событию нажатия
                button = tk.Button(self.master, text=" ", font=("Arial", 20), width=5, height=2,
                                   command=lambda i=i, j=j: self.on_button_click(i, j))
                button.grid(row=i, column=j, sticky="nsew")  # Размещение кнопки на сетке
                row.append(button)  # Добавление кнопки в строку
            self.buttons.append(row)  # Добавление строки кнопок в общий список

        self.reset_button = tk.Button(self.master, text="Новая игра", command=self.reset_game)  # Создание кнопки для сброса игры
        self.reset_button.grid(row=3, column=0, columnspan=3, sticky="nsew")  # Размещение кнопки на сетке

    def on_button_click(self, i, j):
        if self.board[i * 3 + j] == " ":  # Проверка, что клетка пуста
            self.board[i * 3 + j] = self.current_player  # Установка символа текущего игрока в клетку
            self.buttons[i][j].config(text=self.current_player)  # Обновление текста на кнопке
            if self.check_winner(i, j):  # Проверка на победу
                messagebox.showinfo("Победа", f"Игрок {self.current_player} выиграл!")  # Отображение сообщения о победе
                self.reset_game()  # Сброс игры
            elif " " not in self.board:  # Проверка на ничью
                messagebox.showinfo("Ничья", "Ничья!")  # Отображение сообщения о ничьей
                self.reset_game()  # Сброс игры
            else:
                self.current_player = "O" if self.current_player == "X" else "X"  # Смена игрока

    def check_winner(self, i, j):
        row = all(self.board[i*3 + col] == self.current_player for col in range(3))  # Проверка по горизонтали
        col = all(self.board[row*3 + j] == self.current_player for row in range(3))  # Проверка по вертикали
        diag1 = all(self.board[i*3 + i] == self.current_player for i in range(3))  # Проверка по диагонали
        diag2 = all(self.board[i*3 + 2-i] == self.current_player for i in range(3))  # Проверка по диагонали
        return any([row, col, diag1, diag2])  # Возвращает True, если есть победа

    def reset_game(self):
        self.current_player = "X"  # Установка начального игрока
        self.board = [" " for _ in range(9)]  # Очистка игрового поля
        for i in range(3):
            for j in range(3):
                self.buttons[i][j].config(text=" ")  # Очистка текста на кнопках


root = tk.Tk()
game = TicTacToe(root)
root.mainloop()

Пример игры:

Крестики-нолики с графическим интерфейсом на Python
Крестики-нолики с графическим интерфейсом на Python

Заключение

В ходе статьи мы с Вами написали код игры «Крестики-нолики» с графическим интерфейсом на Python используя библиотеку tkinter. Надеюсь Вам понравилась статья, желаю удачи и успехов! 🙂

Admin
Admin
IT Start