Крестики-нолики — логическая игра между двумя противниками на квадратном поле 3 на 3 клетки. Напишем программу игры крестики-нолики на Python с библиотекой tkinter и с применением Объектно Ориентированного программирования ООП. 

Создадим поле из 9 клеток с кнопками класса Button для игры крестики-нолики.

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Button(f, width=3, height=2, font=('times', 24, 'italic bold')
               ).pack(expand=YES, fill=BOTH, side=LEFT)

mainloop()                                  # Главный цикл программы

Лист. 1. Программа, генерирующая игровое поле для крестиков-ноликов.

Рис. 1. Игровое поле, созданное программой листинг 1.

Чтобы у нас была возможность менять текст заголовка окна пронграммы, см. Рис. 1, мы должны в нашей программе явно создать главное окно программы с помощью конструктора Tk(). 

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Button(f, width=3, height=2, font=('times', 24, 'italic bold')
               ).pack(expand=YES, fill=BOTH, side=LEFT)

mainloop()                                  # Главный цикл программы

Лист. 2. В программу добавлено создание окна программы с заголовком 'tic-tac-toe'. 

Рис. 2. Окно программы с заголовком 'tic-tac-toe'. 

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    pass                                    # наследник класса Button


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold')
               ).pack(expand=YES, fill=BOTH, side=LEFT)

mainloop()                                  # Главный цикл программы

Лист. 3. В программе создан класс Btn на основе классса Button из библиотеки tkinter. 

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards)
        self.pack(expand=YES, fill=BOTH, side=LEFT)


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 4. В класс Btn добавлен метод (конструктор) __init__ с возможностью передачи аргументов в конструктор суперкласса Button. 

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards, command=self.play)
        self.pack(expand=YES, fill=BOTH, side=LEFT)

    def play(self):                         # Функция игры
        self.config(text='X')               # Ход крестиков


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 5. В класс Btn добавлен метод play с возможностью размещения буквы 'X' на кнопке, по которой кликнул игрок. 

Рис. 3. Игра крестики - нолики, созданная программой листинг 5.

В программе листинг 5, кнопка (объект) по которой кликнул игрок имеет имя self. Имена соседних кнопок в программе листинг 5 не определены. Для того, чтобы сделать ход ноликом, необходимо присвоить имя каждой кнопке на игровом поле. 

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    btn_all = []                            # Все кнопки (экземпляры типа Btn)
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards, command=self.play)
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.num = len(Btn.btn_all)         # Номер кнопки
        Btn.btn_all.append(self)

    def play(self):                         # Функция игры
        self.config(text='X')               # Ход крестиков
        for i in range(9):
            Btn.btn_all[i].config(text=Btn.btn_all[i].num)


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 6. Все кнопки на игровом поле сгруппированы в список Btn.btn_all.

Рис. 4. Игровое поле создано программой листинг 6.

Функция (метод) Btn.play в программе листинг 6 демонстрирует возможностьизменения свойств любой кнопки на игровом поле.

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    btn_all = []                            # Все кнопки (экземпляры типа Btn)
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards, command=self.play)
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.num = len(Btn.btn_all)         # Номер кнопки
        Btn.btn_all.append(self)

    def play(self):                         # Функция игры
        self.config(text='X')               # Ход крестиков
        for i in range(9):
            if Btn.btn_all[i].cget('text') == '':
                Btn.btn_all[i].config(text='O')
                break


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 7. В метод Btn.play добавлен после хода крестика, ход нолика в первую найденную свободную клетку на игровом поле. 

Рис. 5. Игра с компьютером. 

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    btn_all = []                            # Все кнопки (экземпляры типа Btn)
    playground = [0] * (row*column)         # Виртуальное игровое поле
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards, command=self.play)
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.num = len(Btn.btn_all)         # Номер кнопки
        Btn.btn_all.append(self)

    def play(self):                         # Функция игры
        if Btn.playground[self.num] != 0:   # Если клетка не свободна
            return                          # Выход из функции
        self.config(text='X')               # Ход крестиков
        Btn.playground[self.num] = 1        # Ход крестиков на виртуальном поле
        for i in range(9):                  # Поиск клетки для хода О
            if Btn.playground[i] == 0:      # Если клетка свободна
                Btn.btn_all[i].config(text='O')     # Ход ноликов
                Btn.playground[i] = -1      # Ход ноликов на виртуальном поле
                break                       # Прервать поиск клетки


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 8.

Рис. 6.

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    btn_all = []                            # Все кнопки (экземпляры типа Btn)
    playground = [0] * (row*column)         # Виртуальное игровое поле
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards, command=self.play)
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.num = len(Btn.btn_all)         # Номер кнопки
        Btn.btn_all.append(self)

    def play(self):                         # Функция игры
        if Btn.playground[self.num] != 0:   # Если клетка не свободна
            return                          # Выход из функции
        self.config(text='X')               # Ход крестиков
        Btn.playground[self.num] = 1        # Ход крестиков на виртуальном поле
        if self.win(Btn.playground):        # Если победа
            tk.title('Win X')               # Победили крестики
            return
        for i in range(9):                  # Поиск клетки для хода О
            if Btn.playground[i] == 0:      # Если клетка свободна
                Btn.btn_all[i].config(text='O')     # Ход ноликов
                Btn.playground[i] = -1      # Ход ноликов на виртуальном поле
                break                       # Прервать поиск клетки

    def win(self, pg):                      # Если победа, return True
        if pg[0] + pg[4] + pg[8] in (3, -3):
            return True
        return False


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 9.

Рис. 7.

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    btn_all = []                            # Все кнопки (экземпляры типа Btn)
    playground = [0] * (row*column)         # Виртуальное игровое поле
    game_over = False                       # Флаг конец игры
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards, command=self.play)
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.num = len(Btn.btn_all)         # Номер кнопки
        Btn.btn_all.append(self)

    def play(self):                         # Функция игры
        if Btn.game_over: return            # Если конец игры
        if Btn.playground[self.num] != 0:   # Если клетка не свободна
            return                          # Выход из функции
        self.config(text='X')               # Ход крестиков
        Btn.playground[self.num] = 1        # Ход крестиков на виртуальном поле
        if self.win(Btn.playground):        # Если победа
            tk.title('Win X')               # Победили крестики
            Btn.game_over = True
            return
        for i in range(9):                  # Поиск клетки для хода О
            if Btn.playground[i] == 0:      # Если клетка свободна
                Btn.btn_all[i].config(text='O')     # Ход ноликов
                Btn.playground[i] = -1      # Ход ноликов на виртуальном поле
                break                       # Прервать поиск клетки

    def win(self, pg):                      # Если победа, return True
        if pg[0] + pg[4] + pg[8] in (3, -3):
            return True
        return False


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 10.

Рис. 8.

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    btn_all = []                            # Все кнопки (экземпляры типа Btn)
    playground = [0] * (row*column)         # Виртуальное игровое поле
    game_over = False                       # Флаг конец игры
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards, command=self.play)
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.num = len(Btn.btn_all)         # Номер кнопки
        Btn.btn_all.append(self)

    def play(self):                         # Функция игры
        if Btn.game_over: return            # Если конец игры
        if Btn.playground[self.num] != 0:   # Если клетка не свободна
            return                          # Выход из функции
        self.config(text='X')               # Ход крестиков
        Btn.playground[self.num] = 1        # Ход крестиков на виртуальном поле
        if self.win(Btn.playground):        # Если победа
            tk.title('Win X')               # Победили крестики
            Btn.game_over = True
            return
        for i in range(9):                  # Поиск клетки для хода О
            if Btn.playground[i] == 0:      # Если клетка свободна
                Btn.btn_all[i].config(text='O')     # Ход ноликов
                Btn.playground[i] = -1      # Ход ноликов на виртуальном поле
                break                       # Прервать поиск клетки
        if self.win(Btn.playground):        # Если победа
            tk.title('Win O')               # Победили нолики
            Btn.game_over = True

    def win(self, pg):                      # Если победа, return True
        if pg[0] + pg[1] + pg[2] in (3, -3):
            return True
        return False


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 11.

Рис. 9.

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    btn_all = []                            # Все кнопки (экземпляры типа Btn)
    playground = [0] * (row*column)         # Виртуальное игровое поле
    game_over = False                       # Флаг конец игры
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards, command=self.play)
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.num = len(Btn.btn_all)         # Номер кнопки
        Btn.btn_all.append(self)

    def play(self):                         # Функция игры
        if Btn.playground[self.num] != 0:   # Если клетка не свободна
            return                          # Выход из функции
        self.move(self.num, 1)              # Ход крестиков
        for i in range(9):                  # Поиск клетки для хода О
            if Btn.playground[i] == 0:      # Если клетка свободна
                self.move(i, -1)            # Ход ноликов
                break                       # Прервать поиск клетки

    def move(self, n, k):                   # Ход в n k=1 крестики, k=-1 нолики
        if Btn.game_over: return            # Если конец игры
        Btn.playground[n] = k               # Ход на виртуальном поле
        Btn.btn_all[n].config(text = 'X' if k==1 else 'O')              # Ход
        if self.win(Btn.playground):        # Если победа
            Btn.game_over = True
            tk.title('Win X' if k==1 else 'Win O')

    def win(self, pg):                      # Если победа, return True
        if pg[0] + pg[1] + pg[2] in (3, -3):
            return True
        return False


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 12.

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    btn_all = []                            # Все кнопки (экземпляры типа Btn)
    playground = [0] * (row*column)         # Виртуальное игровое поле
    game_over = False                       # Флаг конец игры
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards, command=self.play)
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.num = len(Btn.btn_all)         # Номер кнопки
        Btn.btn_all.append(self)

    def play(self):                         # Функция игры
        if Btn.playground[self.num] != 0:   # Если клетка не свободна
            return                          # Выход из функции
        self.move(self.num, 1)              # Ход крестиков
        for i in range(9):                  # Поиск клетки для хода О
            if Btn.playground[i] == 0:      # Если клетка свободна
                self.move(i, -1)            # Ход ноликов
                break                       # Прервать поиск клетки

    def move(self, n, k):                   # Ход в n k=1 крестики, k=-1 нолики
        if Btn.game_over: return            # Если конец игры
        Btn.playground[n] = k               # Ход на виртуальном поле
        Btn.btn_all[n].config(text = 'X' if k==1 else 'O')              # Ход
        if self.win(Btn.playground):        # Если победа
            Btn.game_over = True
            tk.title('Win X' if k==1 else 'Win O')
        elif not 0 in Btn.playground:       # Если некуда ходить
            Btn.game_over = True
            tk.title('Ничья')

    def win(self, pg):                      # Если победа, return True
        if all([not(i in (3, -3)) for i in [
            pg[0] + pg[1] + pg[2],
            pg[3] + pg[4] + pg[5],
            pg[6] + pg[7] + pg[8],
            pg[0] + pg[3] + pg[6],
            pg[1] + pg[4] + pg[7],
            pg[2] + pg[5] + pg[8],
            pg[0] + pg[4] + pg[8],
            pg[2] + pg[4] + pg[6]]]):
            return False
        return True


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 13.

Рис. 10.

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    btn_all = []                            # Все кнопки (экземпляры типа Btn)
    playground = [0] * (row*column)         # Виртуальное игровое поле
    game_over = False                       # Флаг конец игры
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards, command=self.play)
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.num = len(Btn.btn_all)         # Номер кнопки
        Btn.btn_all.append(self)

    def play(self):                         # Функция игры
        if Btn.playground[self.num] != 0:   # Если клетка не свободна
            return                          # Выход из функции
        self.move(self.num, 1)              # Ход крестиков
        best = (4, 8, 1, 2, 6, 0, 7, 3, 5)  # Порядок перебора ходов
        for i in best:                      # Поиск клетки для хода О
            if Btn.playground[i] == 0:      # Если клетка свободна
                self.move(i, -1)            # Ход ноликов
                break                       # Прервать поиск клетки

    def move(self, n, k):                   # Ход в n k=1 крестики, k=-1 нолики
        if Btn.game_over: return            # Если конец игры
        Btn.playground[n] = k               # Ход на виртуальном поле
        Btn.btn_all[n].config(text = 'X' if k==1 else 'O')              # Ход
        if self.win(Btn.playground):        # Если победа
            Btn.game_over = True
            tk.title('Win X' if k==1 else 'Win O')
        elif not 0 in Btn.playground:       # Если некуда ходить
            Btn.game_over = True
            tk.title('Ничья')

    def win(self, pg):                      # Если победа, return True
        if all([not(i in (3, -3)) for i in [
            pg[0] + pg[1] + pg[2],
            pg[3] + pg[4] + pg[5],
            pg[6] + pg[7] + pg[8],
            pg[0] + pg[3] + pg[6],
            pg[1] + pg[4] + pg[7],
            pg[2] + pg[5] + pg[8],
            pg[0] + pg[4] + pg[8],
            pg[2] + pg[4] + pg[6]]]):
            return False
        return True


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 14.

Рис. 11.

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    btn_all = []                            # Все кнопки (экземпляры типа Btn)
    playground = [0] * (row*column)         # Виртуальное игровое поле
    game_over = False                       # Флаг конец игры
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards, command=self.play)
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.num = len(Btn.btn_all)         # Номер кнопки
        Btn.btn_all.append(self)

    def play(self):                         # Функция игры
        if Btn.playground[self.num] != 0:   # Если клетка не свободна
            return                          # Выход из функции
        self.move(self.num, 1)              # Ход крестиков
        self.move(self.best(), -1)          # Ход ноликов

    def best(self):                         # Поиск лучшего хода
        for i in range(9):                  # Если есть возможность победить
            pg = Btn.playground.copy()
            pg[i] = -1 if pg[i] == 0 else pg[i]
            if self.win(pg): return i       # Лучший ход
        best = (4, 8, 1, 2, 6, 0, 7, 3, 5)  # Порядок перебора ходов
        for i in best:                      # Поиск клетки для хода О
            if Btn.playground[i] == 0:      # Если клетка свободна
                return i

    def move(self, n, k):                   # Ход в n k=1 крестики, k=-1 нолики
        if Btn.game_over: return            # Если конец игры
        Btn.playground[n] = k               # Ход на виртуальном поле
        Btn.btn_all[n].config(text = 'X' if k==1 else 'O')              # Ход
        if self.win(Btn.playground):        # Если победа
            Btn.game_over = True
            tk.title('Win X' if k==1 else 'Win O')
        elif not 0 in Btn.playground:       # Если некуда ходить
            Btn.game_over = True
            tk.title('Ничья')

    def win(self, pg):                      # Если победа, return True
        if all([not(i in (3, -3)) for i in [
            pg[0] + pg[1] + pg[2],
            pg[3] + pg[4] + pg[5],
            pg[6] + pg[7] + pg[8],
            pg[0] + pg[3] + pg[6],
            pg[1] + pg[4] + pg[7],
            pg[2] + pg[5] + pg[8],
            pg[0] + pg[4] + pg[8],
            pg[2] + pg[4] + pg[6]]]):
            return False
        return True


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 15.

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    btn_all = []                            # Все кнопки (экземпляры типа Btn)
    playground = [0] * (row*column)         # Виртуальное игровое поле
    game_over = False                       # Флаг конец игры
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards, command=self.play)
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.num = len(Btn.btn_all)         # Номер кнопки
        Btn.btn_all.append(self)

    def play(self):                         # Функция игры
        if Btn.playground[self.num] != 0:   # Если клетка не свободна
            return                          # Выход из функции
        self.move(self.num, 1)              # Ход крестиков
        self.move(self.best(), -1)          # Ход ноликов

    def best(self):                         # Поиск лучшего хода
        for k in (-1, 1):                   # Если есть возможность:
            for i in range(9):              # победить или проиграть 
                pg = Btn.playground.copy()
                pg[i] = k if pg[i] == 0 else pg[i]
                if self.win(pg): return i       # Лучший ход
        best = (4, 8, 1, 2, 6, 0, 7, 3, 5)  # Порядок перебора ходов
        for i in best:                      # Поиск клетки для хода О
            if Btn.playground[i] == 0:      # Если клетка свободна
                return i

    def move(self, n, k):                   # Ход в n k=1 крестики, k=-1 нолики
        if Btn.game_over: return            # Если конец игры
        Btn.playground[n] = k               # Ход на виртуальном поле
        Btn.btn_all[n].config(text = 'X' if k==1 else 'O')              # Ход
        if self.win(Btn.playground):        # Если победа
            Btn.game_over = True
            tk.title('Win X' if k==1 else 'Win O')
        elif not 0 in Btn.playground:       # Если некуда ходить
            Btn.game_over = True
            tk.title('Ничья')

    def win(self, pg):                      # Если победа, return True
        if all([not(i in (3, -3)) for i in [
            pg[0] + pg[1] + pg[2],
            pg[3] + pg[4] + pg[5],
            pg[6] + pg[7] + pg[8],
            pg[0] + pg[3] + pg[6],
            pg[1] + pg[4] + pg[7],
            pg[2] + pg[5] + pg[8],
            pg[0] + pg[4] + pg[8],
            pg[2] + pg[4] + pg[6]]]):
            return False
        return True


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 16.

Рис.

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    btn_all = []                            # Все кнопки (экземпляры типа Btn)
    playground = [0] * (row*column)         # Виртуальное игровое поле
    game_over = False                       # Флаг конец игры
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards, command=self.play)
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.num = len(Btn.btn_all)         # Номер кнопки
        Btn.btn_all.append(self)

    def play(self):                         # Функция игры
        if Btn.playground[self.num] != 0:   # Если клетка не свободна
            return                          # Выход из функции
        self.move(self.num, 1)              # Ход крестиков
        self.move(self.best(), -1)          # Ход ноликов

    def best(self):                         # Поиск лучшего хода
        for k in (-1, 1):                   # Если есть возможность:
            for i in range(9):              # победить или проиграть 
                pg = Btn.playground.copy()
                pg[i] = k if pg[i] == 0 else pg[i]
                if self.win(pg): return i       # Лучший ход
        best = (4, 8, 1, 2, 6, 0, 7, 3, 5)  # Порядок перебора ходов
        if Btn.playground[2] == 1 and Btn.playground[6] == 1:
            best = (1, 4, 8, 2, 6, 0, 7, 3, 5)
        for i in best:                      # Поиск клетки для хода О
            if Btn.playground[i] == 0:      # Если клетка свободна
                return i

    def move(self, n, k):                   # Ход в n k=1 крестики, k=-1 нолики
        if Btn.game_over: return            # Если конец игры
        Btn.playground[n] = k               # Ход на виртуальном поле
        Btn.btn_all[n].config(text = 'X' if k==1 else 'O')              # Ход
        if self.win(Btn.playground):        # Если победа
            Btn.game_over = True
            tk.title('Win X' if k==1 else 'Win O')
        elif not 0 in Btn.playground:       # Если некуда ходить
            Btn.game_over = True
            tk.title('Ничья')

    def win(self, pg):                      # Если победа, return True
        if all([not(i in (3, -3)) for i in [
            pg[0] + pg[1] + pg[2],
            pg[3] + pg[4] + pg[5],
            pg[6] + pg[7] + pg[8],
            pg[0] + pg[3] + pg[6],
            pg[1] + pg[4] + pg[7],
            pg[2] + pg[5] + pg[8],
            pg[0] + pg[4] + pg[8],
            pg[2] + pg[4] + pg[6]]]):
            return False
        return True


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 17.

Рис.

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    btn_all = []                            # Все кнопки (экземпляры типа Btn)
    playground = [0] * (row*column)         # Виртуальное игровое поле
    game_over = False                       # Флаг конец игры
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards, command=self.play)
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.num = len(Btn.btn_all)         # Номер кнопки
        Btn.btn_all.append(self)

    def play(self):                         # Функция игры
        if Btn.playground[self.num] != 0:   # Если клетка не свободна
            return                          # Выход из функции
        self.move(self.num, 1)              # Ход крестиков
        self.move(self.best(), -1)          # Ход ноликов

    def best(self):                         # Поиск лучшего хода
        for k in (-1, 1):                   # Если есть возможность:
            for i in range(9):              # победить или проиграть 
                pg = Btn.playground.copy()
                pg[i] = k if pg[i] == 0 else pg[i]
                if self.win(pg): return i       # Лучший ход
        best = (4, 8, 1, 2, 6, 0, 7, 3, 5)  # Порядок перебора ходов
        if Btn.playground[2] == 1 and Btn.playground[6] == 1:
            best = (1, 4, 8, 2, 6, 0, 7, 3, 5)
        if Btn.playground.index(1) in (1, 3):
            best = (1, 4, 0, 2, 6, 8, 7, 3, 5)
        for i in best:                      # Поиск клетки для хода О
            if Btn.playground[i] == 0:      # Если клетка свободна
                return i

    def move(self, n, k):                   # Ход в n k=1 крестики, k=-1 нолики
        if Btn.game_over: return            # Если конец игры
        Btn.playground[n] = k               # Ход на виртуальном поле
        Btn.btn_all[n].config(text = 'X' if k==1 else 'O')              # Ход
        if self.win(Btn.playground):        # Если победа
            Btn.game_over = True
            tk.title('Win X' if k==1 else 'Win O')
        elif not 0 in Btn.playground:       # Если некуда ходить
            Btn.game_over = True
            tk.title('Ничья')

    def win(self, pg):                      # Если победа, return True
        if all([not(i in (3, -3)) for i in [
            pg[0] + pg[1] + pg[2],
            pg[3] + pg[4] + pg[5],
            pg[6] + pg[7] + pg[8],
            pg[0] + pg[3] + pg[6],
            pg[1] + pg[4] + pg[7],
            pg[2] + pg[5] + pg[8],
            pg[0] + pg[4] + pg[8],
            pg[2] + pg[4] + pg[6]]]):
            return False
        return True


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 18.

Рис.

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    btn_all = []                            # Все кнопки (экземпляры типа Btn)
    playground = [0] * (row*column)         # Виртуальное игровое поле
    game_over = False                       # Флаг конец игры
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards, command=self.play)
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.num = len(Btn.btn_all)         # Номер кнопки
        Btn.btn_all.append(self)

    def play(self):                         # Функция игры
        if Btn.playground[self.num] != 0:   # Если клетка не свободна
            return                          # Выход из функции
        self.move(self.num, 1)              # Ход крестиков
        self.move(self.best(), -1)          # Ход ноликов

    def best(self):                         # Поиск лучшего хода
        for k in (-1, 1):                   # Если есть возможность:
            for i in range(9):              # победить или проиграть 
                pg = Btn.playground.copy()
                pg[i] = k if pg[i] == 0 else pg[i]
                if self.win(pg): return i       # Лучший ход
        best = (4, 8, 1, 2, 6, 0, 7, 3, 5)  # Порядок перебора ходов
        if Btn.playground[2] == 1 and Btn.playground[6] == 1:
            best = (1, 4, 8, 2, 6, 0, 7, 3, 5)
        if Btn.playground.index(1) in (1, 3):
            best = (1, 4, 0, 2, 6, 8, 7, 3, 5)
        if Btn.playground[3] == 1 and Btn.playground[7] == 1:
            best = (6, 4, 0, 2, 1, 8, 7, 3, 5)
        for i in best:                      # Поиск клетки для хода О
            if Btn.playground[i] == 0:      # Если клетка свободна
                return i

    def move(self, n, k):                   # Ход в n k=1 крестики, k=-1 нолики
        if Btn.game_over: return            # Если конец игры
        Btn.playground[n] = k               # Ход на виртуальном поле
        Btn.btn_all[n].config(text = 'X' if k==1 else 'O')              # Ход
        if self.win(Btn.playground):        # Если победа
            Btn.game_over = True
            tk.title('Win X' if k==1 else 'Win O')
        elif not 0 in Btn.playground:       # Если некуда ходить
            Btn.game_over = True
            tk.title('Ничья')

    def win(self, pg):                      # Если победа, return True
        if all([not(i in (3, -3)) for i in [
            pg[0] + pg[1] + pg[2],
            pg[3] + pg[4] + pg[5],
            pg[6] + pg[7] + pg[8],
            pg[0] + pg[3] + pg[6],
            pg[1] + pg[4] + pg[7],
            pg[2] + pg[5] + pg[8],
            pg[0] + pg[4] + pg[8],
            pg[2] + pg[4] + pg[6]]]):
            return False
        return True


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 19.

Рис. 

#!/usr/bin/env python3
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- 
#
# t-t-t.py (tic tac toe)
# Copyright (C) 2021 Aleksandr Diorditsa, see <https://adior.ru>
# I want to thank all my students for the inspiration they give me.
#
# t-t-t.py is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# t-t-t.py is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    btn_all = []                            # Все кнопки (экземпляры типа Btn)
    playground = [0] * (row*column)         # Виртуальное игровое поле
    game_over = False                       # Флаг конец игры
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards, command=self.play)
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.num = len(Btn.btn_all)         # Номер кнопки
        Btn.btn_all.append(self)

    def play(self):                         # Функция игры
        if Btn.playground[self.num] != 0:   # Если клетка не свободна
            return                          # Выход из функции
        self.move(self.num, 1)              # Ход крестиков
        self.move(self.best(), -1)          # Ход ноликов

    def best(self):                         # Поиск лучшего хода
        for k in (-1, 1):                   # Если есть возможность:
            for i in range(9):              # победить или проиграть 
                pg = Btn.playground.copy()
                pg[i] = k if pg[i] == 0 else pg[i]
                if self.win(pg): return i       # Лучший ход
        best = (4, 8, 1, 2, 6, 0, 7, 3, 5)  # Порядок перебора ходов
        if Btn.playground[2] == 1 and Btn.playground[6] == 1:
            best = (1, 4, 8, 2, 6, 0, 7, 3, 5)
        if Btn.playground.index(1) in (1, 3):
            best = (1, 4, 0, 2, 6, 8, 7, 3, 5)
        if Btn.playground[3] == 1 and Btn.playground[7] == 1:
            best = (6, 4, 0, 2, 1, 8, 7, 3, 5)
        if Btn.playground[4] == 1 and Btn.playground[0] == 1:
            best = (2, 6, 8, 0, 1, 7, 3, 5)
        for i in best:                      # Поиск клетки для хода О
            if Btn.playground[i] == 0:      # Если клетка свободна
                return i

    def move(self, n, k):                   # Ход в n k=1 крестики, k=-1 нолики
        if Btn.game_over: return            # Если конец игры
        Btn.playground[n] = k               # Ход на виртуальном поле
        Btn.btn_all[n].config(text = 'X' if k==1 else 'O')              # Ход
        if self.win(Btn.playground):        # Если победа
            Btn.game_over = True
            tk.title('Win X' if k==1 else 'Win O')
        elif not 0 in Btn.playground:       # Если некуда ходить
            Btn.game_over = True
            tk.title('Ничья')

    def win(self, pg):                      # Если победа, return True
        if all([not(i in (3, -3)) for i in [
            pg[0] + pg[1] + pg[2],
            pg[3] + pg[4] + pg[5],
            pg[6] + pg[7] + pg[8],
            pg[0] + pg[3] + pg[6],
            pg[1] + pg[4] + pg[7],
            pg[2] + pg[5] + pg[8],
            pg[0] + pg[4] + pg[8],
            pg[2] + pg[4] + pg[6]]]):
            return False
        return True


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 20.

#!/usr/bin/env python3
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- 
#
# t-t-t.py (tic tac toe)
# Copyright (C) 2021 Aleksandr Diorditsa, see <https://adior.ru>
# I want to thank all my students for the inspiration they give me.
#
# t-t-t.py is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# t-t-t.py is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.

from tkinter import *                       # Графическая библиотека

column = 3                                  # Количество колонок
row = 3                                     # Количество строк


class Btn(Button):                          # Класс Btn
    btn_all = []                            # Все кнопки (экземпляры типа Btn)
    playground = [0] * (row*column)         # Виртуальное игровое поле
    game_over = False                       # Флаг конец игры
    def __init__(self, *args, **kwards):
        super().__init__(*args, **kwards, command=self.play)
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.num = len(Btn.btn_all)         # Номер кнопки
        Btn.btn_all.append(self)

    def play(self):                         # Функция игры
        if Btn.playground[self.num] != 0:   # Если клетка не свободна
            return                          # Выход из функции
        self.move(self.num, 1)              # Ход крестиков
        self.move(self.best(), -1)          # Ход ноликов

    def best(self):                         # Поиск лучшего хода
        for k in (-1, 1):                   # Если есть возможность:
            for i in range(9):              # победить или проиграть 
                pg = Btn.playground.copy()
                pg[i] = k if pg[i] == 0 else pg[i]
                if self.win(pg): return i       # Лучший ход
        best = (4, 8, 1, 2, 6, 0, 7, 3, 5)  # Порядок перебора ходов
        if Btn.playground[2] == 1 and Btn.playground[6] == 1:
            best = (1, 4, 8, 2, 6, 0, 7, 3, 5)
        if Btn.playground.index(1) in (1, 3):
            best = (1, 4, 0, 2, 6, 8, 7, 3, 5)
        if Btn.playground[3] == 1 and Btn.playground[7] == 1:
            best = (6, 4, 0, 2, 1, 8, 7, 3, 5)
        if Btn.playground[4] == 1 and Btn.playground[0] == 1:
            best = (2, 6, 8, 0, 1, 7, 3, 5, 4)
        for i in best:                      # Поиск клетки для хода О
            if Btn.playground[i] == 0:      # Если клетка свободна
                return i

    def move(self, n, k):                   # Ход в n k=1 крестики, k=-1 нолики
        if Btn.game_over: return            # Если конец игры
        Btn.playground[n] = k               # Ход на виртуальном поле
        Btn.btn_all[n].config(text = 'X' if k==1 else 'O')              # Ход
        if self.win(Btn.playground):        # Если победа
            Btn.game_over = True
            tk.title('Win X' if k==1 else 'Win O')
        elif not 0 in Btn.playground:       # Если некуда ходить
            Btn.game_over = True
            tk.title('Ничья')

    def win(self, pg):                      # Если победа, return True
        if any([i in (3, -3) for i in
                [pg[j] + pg[j+1] + pg[j+2] for j in range(0,9,3)]+
                [pg[j] + pg[j+3] + pg[j+6] for j in range(3)]+
                [pg[0] + pg[4] + pg[8], pg[2] + pg[4] + pg[6]]]):
            return True
        return False


tk = Tk()                                   # Окно программы
tk.title('tic-tac-toe')

for i in range(row):                        # Формирование игрового поля
    f = Frame()                             # Фреймы (строки)
    f.pack(expand=YES, fill=BOTH)
    for j in range(column):                 # Колонки (клетки)
        Btn(f, width=3, height=2, font=('times', 24, 'italic bold'))

mainloop()                                  # Главный цикл программы

Лист. 21.