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

Кто решает задачи по комбинаторной геометрии, тот

впоследствии пишет короткие и сильные алгоритмы.

Саватеев.

Мы публикуем методическую разработку для занятий с детьми старше 12-ти лет в кружке программирование на Python. Цель - научить детей строить логические выражения и пользоваться условным оператором if (elif, else), использовать цикл for, вложенный цикл и операторы continue и break, а также, создавать собственные функции и возвращать из них результат. В конце статьи приведён пример использования алгоритма minimax с рекурсией (динамическое программирование).

Возьмём шаблон для игр на поле в клетку. Подробнее о создании этого шаблона на странице Игровое поле 2023.

from tkinter import *               # графическая библиотека
from random import shuffle          # перемешать список suffle(A)
column = 3                          # столбцы
row = 3                             # строки
btn = []
playground = list('ЧТОЕСТЖУ ')      # виртуальное игровое поле

def play(n):                        # функция обработчик нажатия на кнопку
    btn[n].config(text=n)

for i in range(row):
    f = Frame()                     # Фрейм
    f.pack(expand=YES, fill=BOTH)   # Вывод фрейма на экран
    for j in range(column):
        n = i * column + j
        btn += [Button(f)]
        btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
        btn[n].config(width=3, height=2)
        btn[n].config(text=playground[n])
        btn[n].config(command=lambda n=n: play(n))

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

Лист. 1.

Игра Крестики-нолики для 2-х игроков

апва

from tkinter import *               # графическая библиотека
from random import shuffle          # перемешать список suffle(A)
column = 3                          # столбцы
row = 3                             # строки
btn = []
playground = [0] * column * row     # виртуальное игровое поле

def play(n):                        # функция обработчик нажатия на кнопку
    btn[n].config(text="X")

for i in range(row):
    f = Frame()                     # Фрейм
    f.pack(expand=YES, fill=BOTH)   # Вывод фрейма на экран
    for j in range(column):
        n = i * column + j
        btn += [Button(f)]
        btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
        btn[n].config(width=3, height=2)
        btn[n].config(command=lambda n=n: play(n))

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

Лист. 2.

from tkinter import *               # графическая библиотека
from random import shuffle          # перемешать список suffle(A)
column = 3                          # столбцы
row = 3                             # строки
btn = []
playground = [0] * column * row + [1]      # виртуальное игровое поле
                                    # если playground[9] = 1, ходит чел.
                                    # если playground[9] = -1, ходит комп.

def play(n):                        # функция обработчик нажатия на кнопку
    move(n, playground)

def move(n, p_g):                        # Ход в клетку n
    btn[n].config(text = "X" if p_g[9] == 1 else "O")
    p_g[9] *= -1

for i in range(row):
    f = Frame()                     # Фрейм
    f.pack(expand=YES, fill=BOTH)   # Вывод фрейма на экран
    for j in range(column):
        n = i * column + j
        btn += [Button(f)]
        btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
        btn[n].config(width=3, height=2)
        btn[n].config(command=lambda n=n: play(n))

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

Лист. 3.

from tkinter import *               # графическая библиотека
from random import shuffle          # перемешать список suffle(A)
column = 3                          # столбцы
row = 3                             # строки
btn = []
playground = [0] * column * row + [1]      # виртуальное игровое поле
                                    # если playground[9] = 1, ходит чел.
                                    # если playground[9] = -1, ходит комп.

def play(n):                        # функция обработчик нажатия на кнопку
    if playground[n] == 0:
        move(n, playground)

def move(n, p_g):                        # Ход в клетку n
    btn[n].config(text = "X" if p_g[9] == 1 else "O")
    p_g[n] = p_g[9]
    p_g[9] *= -1

for i in range(row):
    f = Frame()                     # Фрейм
    f.pack(expand=YES, fill=BOTH)   # Вывод фрейма на экран
    for j in range(column):
        n = i * column + j
        btn += [Button(f)]
        btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
        btn[n].config(width=3, height=2)
        btn[n].config(command=lambda n=n: play(n))

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

Лист. 4.

from tkinter import *               # графическая библиотека
from random import shuffle          # перемешать список suffle(A)
column = 3                          # столбцы
row = 3                             # строки
btn = []
playground = [0] * column * row + [1]      # виртуальное игровое поле
                                    # если playground[9] = 1, ходит чел.
                                    # если playground[9] = -1, ходит комп.

def won(p_g):                       # вычисляет факт победы
    global standings
    standings = [0] * 8             # турнирная таблица (состояния на линиях)
    for i in range(3):
        for j in range(3):
            standings[i] += p_g[i*3 + j]
            standings[i+3] += p_g[i + j*3]
        standings[6] += p_g[i*4]
        standings[7] += p_g[i*2 + 2]
    if 3 in standings or -3 in standings: return True
    return False

def play(n):                        # функция обработчик нажатия на кнопку
    if playground[n] == 0:
        move(n, playground)

def move(n, p_g):                        # Ход в клетку n
    btn[n].config(text = "X" if p_g[9] == 1 else "O")
    p_g[n] = p_g[9]
    p_g[9] *= -1
    print(won(p_g))
    print(standings)

for i in range(row):
    f = Frame()                     # Фрейм
    f.pack(expand=YES, fill=BOTH)   # Вывод фрейма на экран
    for j in range(column):
        n = i * column + j
        btn += [Button(f)]
        btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
        btn[n].config(width=3, height=2)
        btn[n].config(command=lambda n=n: play(n))

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

Лист. 5.

from tkinter import *               # графическая библиотека
from random import shuffle          # перемешать список suffle(A)
column = 3                          # столбцы
row = 3                             # строки
btn = []
playground = [0] * column * row + [1]      # виртуальное игровое поле
                                    # если playground[9] = 1, ходит чел.
                                    # если playground[9] = -1, ходит комп.

def won(p_g):                       # вычисляет факт победы
    global standings
    standings = [0] * 8             # турнирная таблица (состояния на линиях)
    for i in range(3):
        for j in range(3):
            standings[i] += p_g[i*3 + j]
            standings[i+3] += p_g[i + j*3]
        standings[6] += p_g[i*4]
        standings[7] += p_g[i*2 + 2]
    if 3 in standings or -3 in standings: return True
    return False

def play(n):                        # функция обработчик нажатия на кнопку
    if playground[n] == 0 and not(won(playground)):
        move(n, playground)

def move(n, p_g):                        # Ход в клетку n
    btn[n].config(text = "X" if p_g[9] == 1 else "O")
    p_g[n] = p_g[9]
    p_g[9] *= -1
    if won(p_g):
        tk.title("Победили Х" if p_g[9] == -1 else "Победили 0")
        
tk = Tk()
tk.geometry('300x300')
tk.title("Tic-tac-toe")

for i in range(row):
    f = Frame()                     # Фрейм
    f.pack(expand=YES, fill=BOTH)   # Вывод фрейма на экран
    for j in range(column):
        n = i * column + j
        btn += [Button(f)]
        btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
        btn[n].config(width=3, height=2)
        btn[n].config(command=lambda n=n: play(n))

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

Лист. 6.

Ира с компьютером

Игрок и компьютер ходят по очереди.

from tkinter import *               # графическая библиотека
from random import shuffle          # перемешать список suffle(A)
column = 3                          # столбцы
row = 3                             # строки
btn = []
playground = [0] * column * row + [1]      # виртуальное игровое поле
                                    # если playground[9] = 1, ходит чел.
                                    # если playground[9] = -1, ходит комп.

def won(p_g):                       # вычисляет факт победы
    global standings
    standings = [0] * 8             # турнирная таблица (состояния на линиях)
    for i in range(3):
        for j in range(3):
            standings[i] += p_g[i*3 + j]
            standings[i+3] += p_g[i + j*3]
        standings[6] += p_g[i*4]
        standings[7] += p_g[i*2 + 2]
    if 3 in standings or -3 in standings: return True
    return False

def play(n):                        # функция обработчик нажатия на кнопку
    if playground[n] != 0 or (won(playground)):
        return
    move(n, playground)
    if 0 in playground and not(won(playground)):
        move(best(playground), playground)

def move(n, p_g):                   # Ход в клетку n
    btn[n].config(text = "X" if p_g[9] == 1 else "O")
    p_g[n] = p_g[9]
    p_g[9] *= -1
    if won(p_g):
        tk.title("Победили Х" if p_g[9] == -1 else "Победили 0")

def best(p_g):                      # поиск лучшего хода
    return p_g.index(0)             # ход болвана

tk = Tk()
tk.geometry('300x300')
tk.title("Tic-tac-toe")

for i in range(row):
    f = Frame()                     # Фрейм
    f.pack(expand=YES, fill=BOTH)   # Вывод фрейма на экран
    for j in range(column):
        n = i * column + j
        btn += [Button(f)]
        btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
        btn[n].config(width=3, height=2)
        btn[n].config(command=lambda n=n: play(n))

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

Лист. 7. Игра с болваном.

Возможно, лучший первый ход в ценр?

Лист. 8. Компьютер ходит в центр, если центральная клетка не занята.

#!/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.
# I thank Sergey Polozkov and Timur Sharafutdinov, Yana Romanova, Gleb Shalimov
# for checking the code for hidden errors. 
#
# brownian-motion.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 *
from random import *

btn = [0]*9                                 # Для кнопок
first = True                                # Первым ходит нолик

def startGame():
    global first, btn, playArea, standings
    playArea = [0]*9                        # Виртуальное игровое поле
    standings = [0]*8                       # Суммы линий
    for i in btn: i.config(text=' ', font=('mono', 20, 'bold'), width=5, heigh=3)
    if first:
        n = choice(range(9))
        btn[n].config(text = 'O')
        playArea[n] = -1
    first = not first
    tk.title('Tic Tac Toe')

def won(Ar):                                # Подсчёт сумм линий
    for i in range(3):
        standings[i] = Ar[i*3] + Ar[i*3+1] + Ar[i*3+2]
        standings[i+3] = Ar[i] + Ar[i+3] + Ar[i+6]
    standings[6] = Ar[0] + Ar[4] + Ar[8]
    standings[7] = Ar[2] + Ar[4] + Ar[6]
    if 3 in standings or -3 in standings: return True
    return False

def theBest():
    if playArea[4] == 0: return 4           # Первый ход
    for i in range(9):                      # Если есть возможность победить
        if playArea[i] != 0: continue       # Если клетка не свободна
        A = playArea.copy()                 # Делаем копию виртуального поля
        A[i] = -1                           # Делаем пробный ход
        if won(A): return i                 # Если пробный ход принёс победу
        best = True                         # Предположим что ход i не плохой
        for j in range(9):                  # Есть возможность проиграть?
            if A[j] != 0: continue          # Если клетка не свободна
            B = A.copy()                    # Делаем копию копии виртуального поля
            B[j] = 1                        # Делаем пробный ход
            if won(B) or (standings.count(2)>1 and -2 not in standings):
                best = False                # Xод i оказывается плохой
                break
        if best: сandidate = i              # Если ход i не плохой
    return сandidate                        # Если следующий ход не победный

def test():                                 # Нолик победил или ничья
    if won(playArea) or 0 not in playArea:
        tk.title('O win' if -3 in standings else 'No won')
        return True
    return False

def play(n):                                # Ход крестика, поиск хода нолика
    if test():                              # Если конец игры
        startGame()
        return
    if playArea[n] != 0: return             # Если клетка не свободна
    btn[n].config(text = 'X')               # Ход крестика
    playArea[n] = 1                         # Ход на виртуальном поле
    if test(): return                       # Если конец игры
    i = theBest()                           # Оптимальный ход
    btn[i].config(text = 'O')               # Делаем ход
    playArea[i] = -1
    if test(): return                       # Если конец игры

tk = Tk()                                   # Окно программы
for i in  range(0, 3):
    frm = Frame()
    frm.pack(expand=YES, fill=BOTH)
    for j in  range(0, 3):
        btn[i*3+j] = Button(frm, command = lambda n=i*3+j: play(n))
        btn[i*3+j].pack(side=LEFT, expand=YES, fill=BOTH)
 
startGame()
mainloop()

sgsfg

min-max

xcvf

from tkinter import *               # графическая библиотека
from random import shuffle          # перемешать список suffle(A)
column = 3                          # столбцы
row = 3                             # строки
btn = []
playground = [0] * column * row + [1]      # виртуальное игровое поле
                                    # если playground[9] = 1, ходит чел.
                                    # если playground[9] = -1, ходит комп.

def won(p_g):                       # вычисляет факт победы
    global standings
    standings = [0] * 8             # турнирная таблица (состояния на линиях)
    for i in range(3):
        for j in range(3):
            standings[i] += p_g[i*3 + j]
            standings[i+3] += p_g[i + j*3]
        standings[6] += p_g[i*4]
        standings[7] += p_g[i*2 + 2]
    if 3 in standings or -3 in standings: return True
    return False

def play(n):                        # функция обработчик нажатия на кнопку
    if playground[n] != 0 or (won(playground)):
        return
    move(n, playground)
    if 0 in playground and not(won(playground)):
        move(minimax(playground), playground)

def move(n, p_g):                   # Ход в клетку n
    btn[n].config(text = "X" if p_g[9] == 1 else "O")
    p_g[n] = p_g[9]
    p_g[9] *= -1
    if won(p_g):
        tk.title("Победили Х" if p_g[9] == -1 else "Победили 0")

def minimax(p_g):                   # поиск лучшего хода
    return p_g.index(0)             # ход болвана

tk = Tk()
tk.geometry('300x300')
tk.title("Tic-tac-toe")

for i in range(row):
    f = Frame()                     # Фрейм
    f.pack(expand=YES, fill=BOTH)   # Вывод фрейма на экран
    for j in range(column):
        n = i * column + j
        btn += [Button(f)]
        btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
        btn[n].config(width=3, height=2)
        btn[n].config(command=lambda n=n: play(n))

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

Лист. 50. За основу взят листинг 7.

from tkinter import *               # графическая библиотека
from random import shuffle          # перемешать список suffle(A)
column = 3                          # столбцы
row = 3                             # строки
btn = []
playground = [0] * column * row + [1]      # виртуальное игровое поле
                                    # если playground[9] = 1, ходит чел.
                                    # если playground[9] = -1, ходит комп.

def won(p_g):                       # вычисляет факт победы
    global standings
    standings = [0] * 8             # турнирная таблица (состояния на линиях)
    for i in range(3):
        for j in range(3):
            standings[i] += p_g[i*3 + j]
            standings[i+3] += p_g[i + j*3]
        standings[6] += p_g[i*4]
        standings[7] += p_g[i*2 + 2]
    if 3 in standings or -3 in standings: return True
    return False

def play(n):                        # функция обработчик нажатия на кнопку
    if playground[n] != 0 or (won(playground)):
        return
    move(n, playground)
    if 0 in playground and not(won(playground)):
        move(minimax(playground), playground)

def move(n, p_g):                   # Ход в клетку n
    btn[n].config(text = "X" if p_g[9] == 1 else "O")
    setpg(n, p_g)
    if won(p_g):
        tk.title("Победили Х" if p_g[9] == -1 else "Победили 0")

def setpg(n, p_g):                  # сделать ход на виртуальном поле
    p_g[n] = p_g[9]
    p_g[9] *= -1

def minimax(p_g):                   # поиск лучшего хода
    return p_g.index(0)             # ход болвана

tk = Tk()
tk.geometry('300x300')
tk.title("Tic-tac-toe")

for i in range(row):
    f = Frame()                     # Фрейм
    f.pack(expand=YES, fill=BOTH)   # Вывод фрейма на экран
    for j in range(column):
        n = i * column + j
        btn += [Button(f)]
        btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
        btn[n].config(width=3, height=2)
        btn[n].config(command=lambda n=n: play(n))

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

51

from tkinter import *               # графическая библиотека
from random import shuffle          # перемешать список suffle(A)
column = 3                          # столбцы
row = 3                             # строки
btn = []
playground = [0] * column * row + [1]      # виртуальное игровое поле
                                    # если playground[9] = 1, ходит чел.
                                    # если playground[9] = -1, ходит комп.

def won(p_g):                       # вычисляет факт победы
    global standings
    standings = [0] * 8             # турнирная таблица (состояния на линиях)
    for i in range(3):
        for j in range(3):
            standings[i] += p_g[i*3 + j]
            standings[i+3] += p_g[i + j*3]
        standings[6] += p_g[i*4]
        standings[7] += p_g[i*2 + 2]
    if 3 in standings or -3 in standings: return True
    return False

def play(n):                        # функция обработчик нажатия на кнопку
    if playground[n] != 0 or (won(playground)):
        return
    move(n, playground)
    if 0 in playground and not(won(playground)):
        move(minimax(playground)[1], playground)

def move(n, p_g):                   # Ход в клетку n
    btn[n].config(text = "X" if p_g[9] == 1 else "O")
    setpg(n, p_g)
    if won(p_g):
        tk.title("Победили Х" if p_g[9] == -1 else "Победили 0")

def setpg(n, p_g):                  # сделать ход на виртуальном поле
    p_g[n] = p_g[9]
    p_g[9] *= -1

def minimax(p_g):                   # поиск лучшего хода
    if won(p_g): return [p_g[9], None]    
    elif 0 not in p_g: return [0, None]  
    best = [p_g[9]*2, None]              
    for i in range(9):                  
        if p_g[i] == 0:
            new_pg = p_g.copy()
            setpg(i, new_pg)
            value = minimax(new_pg)[0]
            if p_g[9] == 1 and value <= best[0]:
                best = [value, i]
            elif p_g[9] == -1 and value >= best[0]:
                best = [value, i]
    return best

tk = Tk()
tk.geometry('300x300')
tk.title("Tic-tac-toe")

for i in range(row):
    f = Frame()                     # Фрейм
    f.pack(expand=YES, fill=BOTH)   # Вывод фрейма на экран
    for j in range(column):
        n = i * column + j
        btn += [Button(f)]
        btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
        btn[n].config(width=3, height=2)
        btn[n].config(command=lambda n=n: play(n))

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

53

#!/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.
# I thank Sergey Polozkov and Timur Sharafutdinov
# for checking the code for hidden errors. 
#
# brownian-motion.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 *               # графическая библиотека
from random import shuffle          # перемешать список suffle(A)
column = 3                          # столбцы
row = 3                             # строки
btn = []
playground = [0] * column * row + [1]      # виртуальное игровое поле
                                    # если playground[9] = 1, ходит чел.
                                    # если playground[9] = -1, ходит комп.

def won(p_g):                       # вычисляет факт победы
    global standings
    standings = [0] * 8             # турнирная таблица (состояния на линиях)
    for i in range(3):
        for j in range(3):
            standings[i] += p_g[i*3 + j]
            standings[i+3] += p_g[i + j*3]
        standings[6] += p_g[i*4]
        standings[7] += p_g[i*2 + 2]
    if 3 in standings or -3 in standings: return True
    return False

def play(n):                        # функция обработчик нажатия на кнопку
    if playground[n] != 0 or (won(playground)):
        return
    move(n, playground)
    if 0 in playground and not(won(playground)):
        move(minimax(playground)[1], playground)

def move(n, p_g):                   # Ход в клетку n
    btn[n].config(text = "X" if p_g[9] == 1 else "O")
    setpg(n, p_g)
    if won(p_g):
        tk.title("Победили Х" if p_g[9] == -1 else "Победили 0")

def setpg(n, p_g):                  # сделать ход на виртуальном поле
    p_g[n] = p_g[9]
    p_g[9] *= -1
    return p_g

def minimax(p_g):                   # поиск лучшего хода
    if won(p_g): return [p_g[9], None]    
    elif 0 not in p_g: return [0, None]  
    best = [p_g[9]*2, None]              
    for i in range(9):                  
        if p_g[i] == 0:                  
            value = minimax(setpg(i, p_g.copy()))[0]
            if p_g[9] == 1 and value <= best[0]:
                best = [value, i]
            elif p_g[9] == -1 and value >= best[0]:
                best = [value, i]
    return best

tk = Tk()
tk.geometry('300x300')
tk.title("Tic-tac-toe")

for i in range(row):
    f = Frame()                     # Фрейм
    f.pack(expand=YES, fill=BOTH)   # Вывод фрейма на экран
    for j in range(column):
        n = i * column + j
        btn += [Button(f)]
        btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
        btn[n].config(width=3, height=2)
        btn[n].config(command=lambda n=n: play(n))

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

55

В определении функции setpg() добавлена строка return p_g, что позволило в определении функции minimax() в цикле for объединить 3 строки в одну.

2024 год

ыв

from tkinter import *               # графическая библиотека
from random import shuffle          # перемешать список suffle(A)
column = 3                          # столбцы
row = 3                             # строки
btn = []
playground = [0] * column * row + [1]      # виртуальное игровое поле
                                    # если playground[9] = 1, ходит чел.
                                    # если playground[9] = -1, ходит комп.

def won(p_g):                       # вычисляет факт победы
    global standings
    standings = [0] * 8             # турнирная таблица (состояния на линиях)
    for i in range(3):
        for j in range(3):
            standings[i] += p_g[i*3 + j]
            standings[i+3] += p_g[i + j*3]
        standings[6] += p_g[i*4]
        standings[7] += p_g[i*2 + 2]
    if 3 in standings or -3 in standings: return True
    return False

def play(n):                        # функция обработчик нажатия на кнопку
    if playground[n] != 0 or (won(playground)):
        return
    move(n, playground)
    if 0 in playground and not(won(playground)):
        move(best(playground), playground)

def move(n, p_g):                   # Ход в клетку n
    btn[n].config(text = "X" if p_g[9] == 1 else "O")
    p_g[n] = p_g[9]
    p_g[9] *= -1
    if won(p_g):
        tk.title("Победили Х" if p_g[9] == -1 else "Победили 0")

def best(p_g):                      # поиск лучшего хода
    return p_g.index(0)             # ход болвана

tk = Tk()
tk.geometry('300x300')
tk.title("Tic-tac-toe")

for i in range(row):
    f = Frame()                     # Фрейм
    f.pack(expand=YES, fill=BOTH)   # Вывод фрейма на экран
    for j in range(column):
        n = i * column + j
        btn += [Button(f)]
        btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
        btn[n].config(width=3, height=2)
        btn[n].config(command=lambda n=n: play(n))

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

Лист. 1. Ходилка. Игра с болваном.

from tkinter import *               # графическая библиотека
from random import shuffle          # перемешать список suffle(A)
column = 3                          # столбцы
row = 3                             # строки
btn = []
playground = [0] * column * row + [1]      # виртуальное игровое поле
                                    # если playground[9] = 1, ходит чел.
                                    # если playground[9] = -1, ходит комп.

def won(p_g):                       # вычисляет факт победы
    global standings
    standings = [0] * 8             # турнирная таблица (состояния на линиях)
    for i in range(3):
        for j in range(3):
            standings[i] += p_g[i*3 + j]
            standings[i+3] += p_g[i + j*3]
        standings[6] += p_g[i*4]
        standings[7] += p_g[i*2 + 2]
    if 3 in standings or -3 in standings: return True
    return False

def play(n):                        # функция обработчик нажатия на кнопку
    if playground[n] != 0 or (won(playground)):
        return
    move(n, playground)
    if 0 in playground and not(won(playground)):
        move(best(playground), playground)

def move(n, p_g):                   # Ход в клетку n
    btn[n].config(text = "X" if p_g[9] == 1 else "O")
    p_g[n] = p_g[9]
    p_g[9] *= -1
    if won(p_g):
        tk.title("Победили Х" if p_g[9] == -1 else "Победили 0")

def best(p_g):                      # поиск лучшего хода
    for i in range(9):
        if p_g[i] == 0:
            best = i
    return best

tk = Tk()
tk.geometry('300x300')
tk.title("Tic-tac-toe")

for i in range(row):
    f = Frame()                     # Фрейм
    f.pack(expand=YES, fill=BOTH)   # Вывод фрейма на экран
    for j in range(column):
        n = i * column + j
        btn += [Button(f)]
        btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
        btn[n].config(width=3, height=2)
        btn[n].config(command=lambda n=n: play(n))

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

Лист. 2. Игра с болваном, который перебрав все возможные ходы, выбирает последний ход.

from tkinter import *               # графическая библиотека
from random import shuffle          # перемешать список suffle(A)
column = 3                          # столбцы
row = 3                             # строки
btn = []
playground = [0] * column * row + [1]      # виртуальное игровое поле
                                    # если playground[9] = 1, ходит чел.
                                    # если playground[9] = -1, ходит комп.

def won(p_g):                       # вычисляет факт победы
    global standings
    standings = [0] * 8             # турнирная таблица (состояния на линиях)
    for i in range(3):
        for j in range(3):
            standings[i] += p_g[i*3 + j]
            standings[i+3] += p_g[i + j*3]
        standings[6] += p_g[i*4]
        standings[7] += p_g[i*2 + 2]
    if 3 in standings or -3 in standings: return True
    return False

def play(n):                        # функция обработчик нажатия на кнопку
    if playground[n] != 0 or (won(playground)):
        return
    move(n, playground)
    if 0 in playground and not(won(playground)):
        move(best(playground), playground)

def move(n, p_g):                   # Ход в клетку n
    btn[n].config(text = "X" if p_g[9] == 1 else "O")
    setpg(n, p_g)
    if won(p_g):
        tk.title("Победили Х" if p_g[9] == -1 else "Победили 0")

def setpg(n, p_g):                  # сделать ход на виртуальном поле
    p_g[n] = p_g[9]
    p_g[9] *= -1

def best(p_g):                      # поиск лучшего хода
    for i in range(9):
        if p_g[i] == 0:
            best = i
    return best

tk = Tk()
tk.geometry('300x300')
tk.title("Tic-tac-toe")

for i in range(row):
    f = Frame()                     # Фрейм
    f.pack(expand=YES, fill=BOTH)   # Вывод фрейма на экран
    for j in range(column):
        n = i * column + j
        btn += [Button(f)]
        btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
        btn[n].config(width=3, height=2)
        btn[n].config(command=lambda n=n: play(n))

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

Лист. 3. Функция move разделена на 2 функции move и setpg.

from tkinter import *               # графическая библиотека
from random import shuffle          # перемешать список suffle(A)
column = 3                          # столбцы
row = 3                             # строки
btn = []
playground = [0] * column * row + [1]      # виртуальное игровое поле
                                    # если playground[9] = 1, ходит чел.
                                    # если playground[9] = -1, ходит комп.

def won(p_g):                       # вычисляет факт победы
    global standings
    standings = [0] * 8             # турнирная таблица (состояния на линиях)
    for i in range(3):
        for j in range(3):
            standings[i] += p_g[i*3 + j]
            standings[i+3] += p_g[i + j*3]
        standings[6] += p_g[i*4]
        standings[7] += p_g[i*2 + 2]
    if 3 in standings or -3 in standings: return True
    return False

def play(n):                        # функция обработчик нажатия на кнопку
    if playground[n] != 0 or (won(playground)):
        return
    move(n, playground)
    if 0 in playground and not(won(playground)):
        move(best(playground), playground)

def move(n, p_g):                   # Ход в клетку n
    btn[n].config(text = "X" if p_g[9] == 1 else "O")
    setpg(n, p_g)
    if won(p_g):
        tk.title("Победили Х" if p_g[9] == -1 else "Победили 0")

def setpg(n, p_g):                  # сделать ход на виртуальном поле
    p_g[n] = p_g[9]
    p_g[9] *= -1

def best(p_g):                      # поиск лучшего хода
    for i in range(9):
        if p_g[i] == 0:
            best = i
            new_pg = p_g.copy()
            setpg(i, new_pg)
            if won(new_pg):
                return best
    return best

tk = Tk()
tk.geometry('300x300')
tk.title("Tic-tac-toe")

for i in range(row):
    f = Frame()                     # Фрейм
    f.pack(expand=YES, fill=BOTH)   # Вывод фрейма на экран
    for j in range(column):
        n = i * column + j
        btn += [Button(f)]
        btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
        btn[n].config(width=3, height=2)
        btn[n].config(command=lambda n=n: play(n))

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

Лист. 4. Компьютер делает ход на копии playground и если он находит победный ход, то делает его.

from tkinter import *               # графическая библиотека
from random import shuffle          # перемешать список suffle(A)
column = 3                          # столбцы
row = 3                             # строки
btn = []
playground = [0] * column * row + [1]      # виртуальное игровое поле
                                    # если playground[9] = 1, ходит чел.
                                    # если playground[9] = -1, ходит комп.

def won(p_g):                       # вычисляет факт победы
    global standings
    standings = [0] * 8             # турнирная таблица (состояния на линиях)
    for i in range(3):
        for j in range(3):
            standings[i] += p_g[i*3 + j]
            standings[i+3] += p_g[i + j*3]
        standings[6] += p_g[i*4]
        standings[7] += p_g[i*2 + 2]
    if 3 in standings or -3 in standings: return True
    return False

def play(n):                        # функция обработчик нажатия на кнопку
    if playground[n] != 0 or (won(playground)):
        return
    move(n, playground)
    if 0 in playground and not(won(playground)):
        move(best(playground), playground)

def move(n, p_g):                   # Ход в клетку n
    btn[n].config(text = "X" if p_g[9] == 1 else "O")
    setpg(n, p_g)
    if won(p_g):
        tk.title("Победили Х" if p_g[9] == -1 else "Победили 0")

def setpg(n, p_g):                  # сделать ход на виртуальном поле
    p_g[n] = p_g[9]
    p_g[9] *= -1

def best(p_g):                      # поиск лучшего хода
    best = p_g.index(0)
    for i in range(9):
        if p_g[i] == 0:
            new_pg = p_g.copy()
            setpg(i, new_pg)
            if won(new_pg):
                return i
            elif not virtplay_x(new_pg):
                best = i
    return best

def virtplay_x(p_g):
    for i in range(9):
        if p_g[i] == 0:
            new_pg = p_g.copy()
            setpg(i, new_pg)
            if won(new_pg):
                return True
    return False

tk = Tk()
tk.geometry('300x300')
tk.title("Tic-tac-toe")

for i in range(row):
    f = Frame()                     # Фрейм
    f.pack(expand=YES, fill=BOTH)   # Вывод фрейма на экран
    for j in range(column):
        n = i * column + j
        btn += [Button(f)]
        btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
        btn[n].config(width=3, height=2)
        btn[n].config(command=lambda n=n: play(n))

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

Лист. 5. Компьютер продумывает стратегию на 2 хода, но не видит угрозу вилки.