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

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

from tkinter import *

for i in range(9):
    Button().pack()

mainloop()

Лист. 1.

Библиотека tkinter

В программе листинг 1, в первой строке мы импортируем все функции из библиотеки tkinter. Графическая библиотека tkinter входит в стандартный дистрибутив Python версии 3 и старше и служит для создания графического интерфейса программ на Pyton. В версии 2.7 имеется библиотека Tkinter с тем же функционалом.

Цикл for, функция range() 

Ключевое слово for создаёт цикл, в котором переменная i последовательно принимает значения из диапазона range(9).

Функция range() создаёт диапазон значений. Параметрами функции range() могут быть границы диапазона и приращение (правая граница не включается в диапазон). 

Цикл for создаётся с помощью ключевых слов for и in, определение цикла заканчивается двоеточием :. После двоеточия начинается тело цикла. Каждый оператор тела  цикла, если он пишется с новой строки, должен иметь отступ слева. Для выделения блока операторов в python, (например, тела цикла) рекомендуется добавлять перед каждым оператором блока 4 пробела. 

Цикл for i in range(9): выполняется 9 раз, при этом переменная i принимает значения от 0 до 9 с шагом 1. 0 включительно 9 исключительно. Так как мы указли только один параметр диапазона range(), диапазон создан с левой границей 0 и шагом 1 по умолчанию. 9 - правая граница диапазона. 

Конструктор Button() метод pack() 

В цикле for выполняется конструктор Button() с методом pack(). 

Конструктор Button() создаёт объект кнопка. Метод pack() отображает объект кнопку в окне программы. Окно программы, у нас создаётся библиотекой tkinter по умолчанию, как только мы создаём в программе визуальный объект (кнопку, например). Функция Button() выполняется в цикле 9 раз и наша программа, листинг 1, создаёт 9 кнопок в окне нашего приложения, см. рис. 1.

Рис. 1.

from tkinter import *

for i in range(9):
    Button().pack(expand=YES, fill=BOTH)

mainloop()

Лист. 2

В программе листинг 2 в методе pack() мы добавили два параметра. Параметр expand со значением YES применяется для того, чтобы визуальный объект мог занять всё свободное пространство в контейнере. В нашем случае, кнопка (визуальный объект) размещается в окне программы (контейнер).

Свойство fill со значением BOTH позволяет визуальному объекту растягиваться в обоих направлениях, по X и по Y до размера контейнера.

Рис. 2.

from tkinter import *

for i in range(9):
    Button().pack(expand=YES, fill=BOTH, side=LEFT)

mainloop()

Лист. 3.

В программе листинг 3 параметр side со значением LEFT прижимает визуальный объект влево при размещении в контейнере.

Рис. 3.

from tkinter import *

for i in range(3):
    for j in range(3):
        Button().pack(expand=YES, fill=BOTH, side=LEFT)

mainloop()

Лист. 4.

Вложенные циклы

В программе листинг 4 мы, вместо одного цикла из 9 повторений сделали два вложенных цикла из 3х3 повторений. Программа всё так же создаёт 9 кнопок с теми же параметрами размещения, поэтому, внешне в окне программы ни чего не меняется.

Рис. 4.

from tkinter import *

frm = []

for i in range(3):
    frm.append(Frame())
    frm[i].pack(expand=YES, fill=BOTH)
    for j in range(3):
        Button(frm[i]).pack(expand=YES, fill=BOTH, side=LEFT)

mainloop()

Лист. 5.

Списки, метод append(). Констуктор Frame() 

В программе листинг 5 мы создали пустой список с именем frm. В цикле из трёх повторений в список frm методом append() добавляется 3 фрейма. Фрейм — это контейнер для других визуальных объектов. По умолчанию фрейм невидим и прозрачен. Создаёт фреймы конструктор Frame(). В нашей программе мы размещаем невидимые фреймы на экране растянутыми по горизонтали методом pack() с соответствующими параметрами.

В итоге, мы имеем список frm состоящий из трёх фреймов. К верхнему фрейму можно обратиться по имени frm[0], к среднему — по имени frm[1], к нижнему — frm[3].

Во внутреннем цикле for методом Button() с параметром frm[i] создаются кнопки. Первый параметр метода Button() указывает на родительский контейнер в котором должна быть размещены кнопка. Так как переменная i первые три повторения внутреннего цикла имеет значение 0, то первые три кнопки размещаются в верхнем фрейме. Ещё три кнопки размещаются в среднем фрейме и ещё три — в нижнем.

Рис. 5

В дальнейшем  у нас возникнет необходимость обращаться к кнопкам, как к объектам, по имени. Поэтому, в программе листинг 6 мы создали пустой список btn в который в цикле будет добавлено 9 кнопок. 

from tkinter import *

frm = []; btn = []

for i in range(3):
    frm.append(Frame())
    frm[i].pack(expand=YES, fill=BOTH)
    for j in range(3):
        btn.append(Button(frm[i]))
        btn[i*3+j].pack(expand=YES, fill=BOTH, side=LEFT)

mainloop()

Лист. 6

В программе листинг 6 в цикле из 9 повторений в список btn методом append() добавляется 9 объектов класса Button (кнопоки). В списке btn все кнопки имеют индекс (номер) от 0 до 8 включительно. Чтобы разместить эти кнопки на экране методом pack(), к каждой кнопке мы должны обратиться по индексу. Индекс очередной кнопки мы вычисляем, используя параметры внешнего и внутреннего цикла, переменные i и j.

В остальном, программа листинг 6 не отличается от программы листинг 5.

Рис. 6

from tkinter import *

frm = []; btn = []

def play(n):
    btn[n].config(text=str(n))

for i in range(3):
    frm.append(Frame())
    frm[i].pack(expand=YES, fill=BOTH)
    for j in range(3):
        btn.append(Button(frm[i]))
        btn[i*3+j].config(command=lambda n=i*3+j:play(n))
        btn[i*3+j].pack(expand=YES, fill=BOTH, side=LEFT)

mainloop()

Лист. 7.

Функция пользователя 

В программе листинг 7 с помощью ключевого слова  def мы создали функцию play(). Новые функции, созданные программистом в программе называют функциями пользователя. Новые функции, обычно, создают в начале программы после определения глобальных переменных, до их первого упоминания в коде программы.

В теле функции play() мы прописали метод config() применяемый к кнопке по которой кликнул игрок. Параметр text метода config() выводит на кнопку надпись. Функция str() превращает число в строку. Параметр n, передаваемый в функцию play() это номер кнопки по которой кликнул игрок.

Обработка событий

Функция play() будет зарегистрирована как обработчик события "клик мышью по кнопке". Обработчик события по умолчанию для кнопки можно зарегистрировать методом config(). Параметр command метода config() регистрирует обработчик события. Так как значением параметра command должно быть имя функции без скобок, мы для передачи параметра в функцию play в случае возникновения события, воспользовались lambda функцией. 

Функция lambda() 

Первый параметр lambda функции n вычисляется в процессе создания объекта кнопка, а второй параметр, указанный в lambda функции после двоеточия, передаётся для регистрации в качестве обработчика события. А в lambda функции мы указываем функцию play() с круглыми скобками и передаваемым параметром n. Причём, параметр n вячисляется на этапе создания кнопки и сохраняется в свойстве command как значение. То есть, функция play, как обработчик события будет вызвана с параметром номер кнопки, который был вычислен на этапе создания кнопки. 

Рис. 7.

from tkinter import *

frm = []; btn = []; who = True

def play(n):
    global who
    btn[n].config(text= 'X' if who else 'O')
    who = not(who)

for i in range(3):
    frm.append(Frame())
    frm[i].pack(expand=YES, fill=BOTH)
    for j in range(3):
        btn.append(Button(frm[i]))
        btn[i*3+j].config(command=lambda n=i*3+j:play(n))
        btn[i*3+j].pack(expand=YES, fill=BOTH, side=LEFT)

mainloop()

Лист. 8.

Рис.

from tkinter import *

frm = []; btn = []; who = True

def play(n):
    global who
    btn[n].config(text= 'X' if who else 'O')
    who = not(who)

for i in range(3):
    frm.append(Frame())
    frm[i].pack(expand=YES, fill=BOTH)
    for j in range(3):
        btn.append(Button(frm[i], text=' ', font=('mono', 20, 'bold'), width=3, height=2))
        btn[i*3+j].config(command=lambda n=i*3+j:play(n))
        btn[i*3+j].pack(expand=YES, fill=BOTH, side=LEFT, padx=1, pady=1)

mainloop()

Лист. 9.

Рис.

Рис.

from tkinter import *

frm = []; btn = []; who = True

def play(n):
    global who
    btn[n].config(text= 'X' if who else 'O', state=DISABLED)
    who = not(who)

for i in range(3):
    frm.append(Frame())
    frm[i].pack(expand=YES, fill=BOTH)
    for j in range(3):
        btn.append(Button(frm[i], text=' ', font=('mono', 20, 'bold'), width=3, height=2))
        btn[i*3+j].config(command=lambda n=i*3+j:play(n))
        btn[i*3+j].pack(expand=YES, fill=BOTH, side=LEFT, padx=1, pady=1)

mainloop()

Лист. 10.

Рис.

Часть 2. Думалка. 

from tkinter import *

frm = []; btn = []; who = True
playArea = []

def play(n):
    global who
    btn[n].config(text= 'X' if who else 'O', state=DISABLED)
    playArea[n] = 1 if who else -1
    who = not(who)
    print(playArea)

for i in range(3):
    frm.append(Frame())
    frm[i].pack(expand=YES, fill=BOTH)
    for j in range(3):
        btn.append(Button(frm[i], text=' ', font=('mono', 20, 'bold'), width=3, height=2))
        btn[i*3+j].config(command=lambda n=i*3+j:play(n))
        btn[i*3+j].pack(expand=YES, fill=BOTH, side=LEFT, padx=1, pady=1)
        playArea.append(0)

mainloop()

Лист.

Рис.

[1, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0, 0, 0, -1]
[1, 0, 0, 0, 1, 0, 0, 0, -1]
[1, 0, 0, 0, 1, 0, -1, 0, -1]
[1, 0, 0, 0, 1, 0, -1, 1, -1]
[1, -1, 0, 0, 1, 0, -1, 1, -1]
[1, -1, 0, 0, 1, 1, -1, 1, -1]
[1, -1, 0, -1, 1, 1, -1, 1, -1]
[1, -1, 1, -1, 1, 1, -1, 1, -1]

Лист.

from tkinter import *

frm = []; btn = []; who = True
playArea = []
standings = []                          # Турнирная таблица (положение)

def play(n):
    global who
    btn[n].config(text= 'X' if who else 'O', state=DISABLED)
    playArea[n] = 1 if who else -1
    standings[0] = playArea[0] + playArea[1] + playArea[2]
    standings[1] = playArea[3] + playArea[4] + playArea[5]
    standings[2] = playArea[6] + playArea[7] + playArea[8]
    standings[3] = playArea[0] + playArea[3] + playArea[6]
    standings[4] = playArea[1] + playArea[4] + playArea[7]
    standings[5] = playArea[2] + playArea[5] + playArea[8]
    standings[6] = playArea[0] + playArea[4] + playArea[8]
    standings[7] = playArea[2] + playArea[4] + playArea[6]
    print(n, standings[0:8])
    who = not(who)

for i in range(3):
    frm.append(Frame())
    frm[i].pack(expand=YES, fill=BOTH)
    for j in range(3):
        btn.append(Button(frm[i], text=' ', font=('mono', 20, 'bold'), width=3, height=2))
        btn[i*3+j].config(command=lambda n=i*3+j:play(n))
        btn[i*3+j].pack(expand=YES, fill=BOTH, side=LEFT, padx=1, pady=1)
        playArea.append(0)
        standings.append(0)

mainloop()

Лист.

Рис.

0 [1, 0, 0, 1, 0, 0, 1, 0]
1 [0, 0, 0, 1, -1, 0, 1, 0]
2 [1, 0, 0, 1, -1, 1, 1, 1]
5 [1, -1, 0, 1, -1, 0, 1, 1]
4 [1, 0, 0, 1, 0, 0, 2, 2]
8 [1, 0, -1, 1, 0, -1, 1, 2]
7 [1, 0, 0, 1, 1, -1, 1, 2]
6 [1, 0, -1, 0, 1, -1, 1, 1]
3 [1, 1, -1, 1, 1, -1, 1, 1]

Лист. 

from tkinter import *

frm = []; btn = []; who = True
playArea = []
standings = []                          # Турнирная таблица (положение)

def play(n):
    global who
    btn[n].config(text= 'X' if who else 'O', state=DISABLED)
    playArea[n] = 1 if who else -1
    standings[0] = playArea[0] + playArea[1] + playArea[2]
    standings[1] = playArea[3] + playArea[4] + playArea[5]
    standings[2] = playArea[6] + playArea[7] + playArea[8]
    standings[3] = playArea[0] + playArea[3] + playArea[6]
    standings[4] = playArea[1] + playArea[4] + playArea[7]
    standings[5] = playArea[2] + playArea[5] + playArea[8]
    standings[6] = playArea[0] + playArea[4] + playArea[8]
    standings[7] = playArea[2] + playArea[4] + playArea[6]
    print(n, standings[0:8])
    for i in range(8):
        if standings[i] == 3:
            print('X win')
        elif standings[i] == -3:
            print('O win')
    who = not(who)

for i in range(3):
    frm.append(Frame())
    frm[i].pack(expand=YES, fill=BOTH)
    for j in range(3):
        btn.append(Button(frm[i], text=' ', font=('mono', 20, 'bold'), width=3, height=2))
        btn[i*3+j].config(command=lambda n=i*3+j:play(n))
        btn[i*3+j].pack(expand=YES, fill=BOTH, side=LEFT, padx=1, pady=1)
        playArea.append(0)
        standings.append(0)

mainloop()

Лист.

Рис.

0 [1, 0, 0, 1, 0, 0, 1, 0]
1 [0, 0, 0, 1, -1, 0, 1, 0]
4 [0, 1, 0, 1, 0, 0, 2, 1]
2 [-1, 1, 0, 1, 0, -1, 2, 0]
8 [-1, 1, 1, 1, 0, 0, 3, 0]
X win

Лист.

Рис.

Лист.

Рис.

Лист.

Рис.

Лист.

Рис.

Лист.

Рис.

Лист.

Рис.