Игра в "Крестики-Нолики"

Всем доброго времени суток!

Делаю игру "Крестики-Нолики" с возможностью играть в нескольких режимах:

  1. Игрок1 - Игрок2
  2. Игрок1 - Компьютер2
  3. Компьютер1 - Игрок2
  4. Компьютер1 - Компьютер2

Пока получилось реализовать 2) Игрок1 - Компьютер2 и 3) Компьютер1 - Игрок2. Вот мой код:

from tkinter import *
import random


class Square:
    """ задает квадраты(клетки) """

    def __init__(self, master, root, num, row, column):
        self.master = master
        self.square_num = num
        self.msg = StringVar()
        self.lbl = Label(root, textvariable=self.msg, font="FreeSerifBold 75", bg="white")
        self.lbl.grid(row=row, column=column, sticky=NSEW, padx=5, pady=5)

    def unbind(self):
        self.lbl.unbind("<Button-1>")

    def bind(self):
        self.lbl.bind("<Button-1>", self.choice)

    def set_msg(self, txt):
        self.msg.set(txt)

    def choice(self, event):
        self.master.turn_user(self.square_num)

    def cfg(self, color):
        self.lbl.configure(bg=color)


class Application(Frame):
    def __init__(self, master):
        super(Application, self).__init__(master)
        self.master = master
        self.master.config(bg="#D9D9D9")
        # константы
        self.wins = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6),
                     (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6))
        self.board_start = ['_'] * 9
        self.free_squares_start = [x for x in range(9)]
        self.choice_pc1 = [0, 2, 6, 8]
        self.choice_pc2 = [1, 3, 5, 7]
        # статистика игры
        self.drawn_game = 0
        self.user1_win = 0
        self.user2_win = 0
        self.pc1_win = 0
        self.pc2_win = 0
        self.create_widgets()

    def create_widgets(self):
        # блок статистики
        stat_frame = Frame(self.master)
        stat_frame.grid(row=0, column=0, padx=5, pady=5, sticky=N)
        # статистика
        statistics = LabelFrame(stat_frame, text=" Статистика ")
        statistics.grid()
        self.stat_msg = StringVar()
        stat_lbl = Label(statistics, textvariable=self.stat_msg, justify=LEFT, width=20)
        stat_lbl.grid(padx=0)
        self.stat_msg.set(
            'Сыграно игр: %s\nИз них:\n  побед игрока 1: %s\n  побед игрока 2: %s\n  побед компьютера 1: %s\n  побед компьютера 2: %s\n  ничьих: %s' %
            (self.user1_win + self.user2_win + self.pc1_win + self.pc2_win + self.drawn_game,
             self.user1_win, self.user2_win, self.pc1_win, self.pc2_win, self.drawn_game)
        )

        # блок игрока 1
        gamer1_frame = Frame(self.master)
        gamer1_frame.grid(row=1, column=0, padx=5, pady=5, sticky=W)
        step_choice = LabelFrame(gamer1_frame, text=" Игрок 1 ")
        step_choice.grid(ipadx=26)
        self.gamer1_step = IntVar()
        self.gamer1_step.set(0)
        gamer1_step0 = Radiobutton(step_choice, text='Игрок', variable=self.gamer1_step, value=0)
        gamer1_step1 = Radiobutton(step_choice, text='Компьютер', variable=self.gamer1_step, value=1)
        gamer1_step0.grid(ipady=2, sticky=W)
        gamer1_step1.grid(ipady=3, sticky=W)

        # блок игрока 2
        gamer2_frame = Frame(self.master)
        gamer2_frame.grid(row=1, column=1, padx=5, pady=5, sticky=W)
        step_choice = LabelFrame(gamer2_frame, text=" Игрок 2 ")
        step_choice.grid(ipadx=26)
        self.gamer2_step = IntVar()
        self.gamer2_step.set(1)
        gamer2_step0 = Radiobutton(step_choice, text='Игрок', variable=self.gamer2_step, value=0)
        gamer2_step1 = Radiobutton(step_choice, text='Компьютер', variable=self.gamer2_step, value=1)
        gamer2_step0.grid(ipady=2, sticky=W)
        gamer2_step1.grid(ipady=3, sticky=W)

        # блок выбора хода
        step_frame = Frame(self.master)
        step_frame.grid(row=0, column=1, padx=10, pady=5, sticky=N)
        step_choice = LabelFrame(step_frame, text=" Ходит первым ")
        step_choice.grid(ipadx=20, ipady=25)
        self.first_step = IntVar()
        self.first_step.set(0)
        first_step0 = Radiobutton(step_choice, text='Игрок 1', variable=self.first_step, value=0)
        first_step1 = Radiobutton(step_choice, text='Игрок 2', variable=self.first_step, value=1)
        first_step0.grid(ipady=3, sticky=W)
        first_step1.grid(ipady=2, sticky=W)

        # блок выбора знака
        sign_frame = Frame(self.master)
        sign_frame.grid(row=0, column=2, padx=5, pady=5)
        # выбор знака
        sign_choice = LabelFrame(sign_frame, text=" Выбор знака ")
        sign_choice.grid(ipadx=20)
        self.gamer1_choice = StringVar()
        self.gamer1_choice.set('0')
        gamer1_sign0 = Radiobutton(sign_choice, text='Игрок 1 "0"', variable=self.gamer1_choice, value='0')
        gamer1_sign1 = Radiobutton(sign_choice, text='Игрок 1 "X"', variable=self.gamer1_choice, value='X')
        gamer1_sign0.grid()
        gamer1_sign1.grid()

        # кнопка старт
        self.btn = Button(sign_frame, text="Старт")
        self.btn.bind("<Button-1>", self.start)
        self.btn.grid(pady=6, ipady=10, sticky=NSEW)

        # игровое поле
        self.squares = {}
        num = 0
        for row in range(2, 5):
            for column in range(0, 3):
                self.squares[num] = Square(self, self.master, num, row, column)
                num += 1
        self.widgets = [gamer1_sign0, gamer1_sign1, gamer1_step0, gamer1_step1, gamer2_step0, gamer2_step1, first_step0,
                        first_step1, self.btn]

    def start(self, *args):
        self.choice_pc = None
        self.flag_end = False
        self.free_squares = self.free_squares_start[:]
        self.board = self.board_start[:]
        for square_num in range(9):
            square = self.squares[square_num]
            square.bind()
            square.set_msg('')
            square.cfg('white')
        gs1 = self.gamer1_step.get()
        gs2 = self.gamer2_step.get()
        if self.gamer1_choice.get() == '0':
            if gs1 == 0 and gs2 == 1:
                self.user_sign, self.pc_sign = '0', 'X'
            elif gs1 == 1 and gs2 == 0:
                self.pc_sign, self.user_sign = '0', 'X'
            elif gs1 == 0 and gs2 == 0:  # for user1 vs user2
                self.user1_sign, self.user2_sign = '0', 'X'
            elif gs1 == 1 and gs2 == 1:  # for pc1 vs pc2
                self.pc1_sign, self.pc2_sign = '0', 'X'
        elif self.gamer1_choice.get() == 'X':
            if gs1 == 0 and gs2 == 1:
                self.user_sign, self.pc_sign = 'X', '0'
            elif gs1 == 1 and gs2 == 0:
                self.pc_sign, self.user_sign = 'X', '0'
            elif gs1 == 0 and gs2 == 0:  # for user1 vs user2
                self.user1_sign, self.user2_sign = 'X', '0'
            elif gs1 == 1 and gs2 == 1:  # for pc1 vs pc2
                self.pc1_sign, self.pc2_sign = 'X', '0'
        fs = self.first_step.get()
        if fs == 0:
            if gs1 == 1:
                self.turn_pc()
        elif fs == 1:
            if gs2 == 1:
                self.turn_pc()
        self.widget_state()
        self.btn.unbind("<Button-1>")

    def widget_state(self, wst=DISABLED):
        for widget in self.widgets:
            widget.config(state=wst)

    def unbind_squares(self):
        for square_num in self.free_squares:
            self.squares[square_num].unbind()

    def greet_winner(self, sign):
        """ поздравления """
        if sign == self.user_sign and self.gamer1_step.get() == 0:
            self.user1_win += 1
        if sign == self.pc_sign and self.gamer1_step.get() == 1:
            self.pc1_win += 1
        if sign == self.user_sign and self.gamer2_step.get() == 0:
            self.user2_win += 1
        if sign == self.pc_sign and self.gamer2_step.get() == 1:
            self.pc2_win += 1
        self.flag_end = True

    def show_win(self, win, sign):
        if sign == self.user_sign:
            color = 'green'
        else:
            color = 'red'
        for square_num in win:
            square = self.squares[square_num]
            square.cfg(color)

    def game_over(self):
        """ окончание игры """
        self.stat_msg.set(
            'Сыграно игр: %s\nИз них:\n  побед игрока 1: %s\n  побед игрока 2: %s\n  побед компьютера 1: %s\n  побед компьютера 2: %s\n  ничьих: %s' %
            (self.user1_win + self.user2_win + self.pc1_win + self.pc2_win + self.drawn_game,
             self.user1_win, self.user2_win, self.pc1_win, self.pc2_win, self.drawn_game)
        )
        self.widget_state(NORMAL)
        self.btn.bind("<Button-1>", self.start)

    def test_win(self, sign):
        for a, b, c in self.wins:
            if self.board[a] == sign and self.board[b] == sign and self.board[c] == sign:
                self.show_win((a, b, c), sign)
                self.unbind_squares()
                self.greet_winner(sign)
                self.game_over()
                return
        if not self.free_squares:
            self.flag_end = True
            self.drawn_game += 1
            for square_num in range(9):
                square = self.squares[square_num]
                square.cfg('yellow')
            self.game_over()

    def is_win(self, sign, iboard):
        for a, b, c in self.wins:
            if iboard[a] == sign and iboard[b] == sign and iboard[c] == sign:
                return True
        return False

    def find_best_turn(self, sign):
        for iturn in self.free_squares:
            iboard = self.board[:]
            iboard[iturn] = sign
            res = self.is_win(sign, iboard)
            if res:
                return iturn
        return None

    def default_choice(self):
        if not self.choice_pc:
            self.choice_pc = [4]
            random.shuffle(self.choice_pc1)
            random.shuffle(self.choice_pc2)
            self.choice_pc.extend(self.choice_pc1)
            self.choice_pc.extend(self.choice_pc2)
        for iturn in self.choice_pc:
            if iturn in self.free_squares:
                return iturn

    def turn_pc(self):
        """ ход пк """
        square_num = self.find_best_turn(self.pc_sign)
        if square_num is None:
            square_num = self.find_best_turn(self.user_sign)
            if square_num is None:
                square_num = self.default_choice()
        self.turn(square_num, self.pc_sign)

    def change_board(self, square_num, sign):
        """ заполнение клетки знаком """
        self.board[square_num] = sign
        self.free_squares.remove(square_num)

    def turn(self, square_num, sign):
        square = self.squares[square_num]
        square.unbind()
        square.set_msg(sign)
        self.change_board(square_num, sign)
        self.test_win(sign)

    def turn_user(self, square_num):
        """ ход игрока """
        self.turn(square_num, self.user_sign)
        if not self.flag_end:
            self.turn_pc()


def main():
    root = Tk()
    root.title('Крестики - Нолики')
    root.resizable(FALSE, FALSE)
    app = Application(root)
    root.mainloop()


if __name__ == '__main__':
    main()

Буду благодарен за любую помощь в реализации 1) Игрок1 - Игрок2 и 4) Компьютер1 - Компьютер2 Заранее спасибо


Ответы (2 шт):

Автор решения: Mikhail

Вот мой вариант кода:

from tkinter import *
import random


class Square:
    """ задает квадраты(клетки) """
    def __init__(self, master, root, num, row, column):
        self.master = master
        self.square_num = num
        self.msg = StringVar()
        self.lbl = Label(root, textvariable=self.msg, font="FreeSerifBold 75", bg="white")
        self.lbl.grid(row=row, column=column, sticky=NSEW, padx=5, pady=5)

    def unbind(self):
        self.lbl.unbind("<Button-1>")

    def bind(self):
        self.lbl.bind("<Button-1>", self.choice)

    def bind_comp(self):
        self.lbl.bind("<Button-1>", self.choice_comp)

    def set_msg(self, txt):
        self.msg.set(txt)

    def choice(self, event):
        self.master.turn_user(self.square_num)

    def choice_comp(self, event):
        self.master.turn_pc()

    def cfg(self, color):
        self.lbl.configure(bg=color)


class Application(Frame):
    def __init__(self, master):
        super(Application, self).__init__(master)
        self.master = master
        self.master.config(bg="#D9D9D9")
        # константы
        self.wins = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6),
                     (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6))
        self.board_start = ['_'] * 9
        self.free_squares_start = [x for x in range(9)]
        self.choice_pc1 = [0, 2, 6, 8]
        self.choice_pc2 = [1, 3, 5, 7]
        # статистика игры
        self.drawn_game = 0
        self.user1_win = 0
        self.user2_win = 0
        self.pc1_win = 0
        self.pc2_win = 0
        self.create_widgets()

    def create_widgets(self):
        # блок статистики
        stat_frame = Frame(self.master)
        stat_frame.grid(row=0, column=0, padx=5, pady=5, sticky=N)
        # статистика
        statistics = LabelFrame(stat_frame, text=" Статистика ")
        statistics.grid()
        self.stat_msg = StringVar()
        stat_lbl = Label(statistics, textvariable=self.stat_msg, justify=LEFT, width=20)
        stat_lbl.grid(padx=0)
        self.stat_msg.set(
            'Сыграно игр: %s\nИз них:\n  побед игрока 1: %s\n  побед игрока 2: %s\n  побед компьютера 1: %s\n  побед компьютера 2: %s\n  ничьих: %s' %
            (self.user1_win + self.user2_win + self.pc1_win + self.pc2_win + self.drawn_game,
             self.user1_win, self.user2_win, self.pc1_win, self.pc2_win, self.drawn_game)
        )
        # блок игрока 1
        gamer1_frame = Frame(self.master)
        gamer1_frame.grid(row=1, column=0, padx=5, pady=5, sticky=W)
        step_choice = LabelFrame(gamer1_frame, text=" Игрок 1 ")
        step_choice.grid(ipadx=26)
        self.gamer1_step = IntVar()
        self.gamer1_step.set(0)
        gamer1_step0 = Radiobutton(step_choice, text='Игрок', variable=self.gamer1_step, value=0)
        gamer1_step1 = Radiobutton(step_choice, text='Компьютер', variable=self.gamer1_step, value=1)
        gamer1_step0.grid(ipady=2, sticky=W)
        gamer1_step1.grid(ipady=3, sticky=W)
        # блок игрока 2
        gamer2_frame = Frame(self.master)
        gamer2_frame.grid(row=1, column=1, padx=5, pady=5, sticky=W)
        step_choice = LabelFrame(gamer2_frame, text=" Игрок 2 ")
        step_choice.grid(ipadx=26)
        self.gamer2_step = IntVar()
        self.gamer2_step.set(1)
        gamer2_step0 = Radiobutton(step_choice, text='Игрок', variable=self.gamer2_step, value=0)
        gamer2_step1 = Radiobutton(step_choice, text='Компьютер', variable=self.gamer2_step, value=1)
        gamer2_step0.grid(ipady=2, sticky=W)
        gamer2_step1.grid(ipady=3, sticky=W)
        # блок выбора хода
        step_frame = Frame(self.master)
        step_frame.grid(row=0, column=1, padx=10, pady=5, sticky=N)
        step_choice = LabelFrame(step_frame, text=" Ходит первым ")
        step_choice.grid(ipadx=20, ipady=25)
        self.first_step = IntVar()
        self.first_step.set(0)
        first_step0 = Radiobutton(step_choice, text='Игрок 1', variable=self.first_step, value=0)
        first_step1 = Radiobutton(step_choice, text='Игрок 2', variable=self.first_step, value=1)
        first_step0.grid(ipady=3, sticky=W)
        first_step1.grid(ipady=2, sticky=W)
        # блок выбора знака
        sign_frame = Frame(self.master)
        sign_frame.grid(row=0, column=2, padx=5, pady=5)
        # выбор знака
        sign_choice = LabelFrame(sign_frame, text=" Выбор знака ")
        sign_choice.grid(ipadx=20)
        self.gamer1_choice = StringVar()
        self.gamer1_choice.set('0')
        gamer1_sign0 = Radiobutton(sign_choice, text='Игрок 1 "0"', variable=self.gamer1_choice, value='0')
        gamer1_sign1 = Radiobutton(sign_choice, text='Игрок 1 "X"', variable=self.gamer1_choice, value='X')
        gamer1_sign0.grid()
        gamer1_sign1.grid()
        # кнопка старт
        self.btn = Button(sign_frame, text="Старт")
        self.btn.bind("<Button-1>", self.start)
        self.btn.grid(pady=6, ipady=10, sticky=NSEW)
        # игровое поле
        self.squares = {}
        num = 0
        for row in range(2, 5):
            for column in range(0, 3):
                self.squares[num] = Square(self, self.master, num, row, column)
                num += 1
        self.widgets = [gamer1_sign0, gamer1_sign1, gamer1_step0, gamer1_step1, gamer2_step0, gamer2_step1, first_step0,
                        first_step1, self.btn]

    def start(self, *args):
        self.choice_pc = None
        self.flag_end = False
        self.flag_choice_user = False
        self.flag_choice_pc = False
        self.free_squares = self.free_squares_start[:]
        self.board = self.board_start[:]
        for square_num in range(9):
            square = self.squares[square_num]
            if self.gamer1_step.get() == 1 and self.gamer2_step.get() == 1:
                square.bind_comp()
            else:
                square.bind()
            square.set_msg('')
            square.cfg('white')
        gs1 = self.gamer1_step.get()
        gs2 = self.gamer2_step.get()
        if self.gamer1_choice.get() == '0':
            if gs1 == 0 and gs2 == 1:
                self.user_sign, self.pc_sign = '0', 'X'
            elif gs1 == 1 and gs2 == 0:
                self.pc_sign, self.user_sign = '0', 'X'
            elif gs1 == 0 and gs2 == 0:
                self.user1_sign, self.user2_sign = '0', 'X'
            elif gs1 == 1 and gs2 == 1:
                self.pc1_sign, self.pc2_sign = '0', 'X'
        else:
            if gs1 == 0 and gs2 == 1:
                self.user_sign, self.pc_sign = 'X', '0'
            elif gs1 == 1 and gs2 == 0:
                self.pc_sign, self.user_sign = 'X', '0'
            elif gs1 == 0 and gs2 == 0:
                self.user1_sign, self.user2_sign = 'X', '0'
            elif gs1 == 1 and gs2 == 1:
                self.pc1_sign, self.pc2_sign = 'X', '0'
        fs = self.first_step.get()
        if fs == 0:
            if gs1 == 1:
                self.turn_pc()
        elif fs == 1:
            if gs2 == 1:
                self.flag_choice_pc = True
                self.turn_pc()
        self.widget_state()
        self.btn.unbind("<Button-1>")

    def widget_state(self, wst=DISABLED):
        for widget in self.widgets:
            widget.config(state=wst)

    def unbind_squares(self):
        for square_num in self.free_squares:
            self.squares[square_num].unbind()

    def greet_winner(self, sign):
        """ поздравления """
        if self.gamer1_step.get() == 0 and self.gamer2_step.get() == 0:
            if sign == self.user1_sign and self.gamer1_step.get() == 0:
                self.user1_win += 1
            if sign == self.user2_sign and self.gamer2_step.get() == 0:
                self.user2_win += 1
        elif self.gamer1_step.get() == 1 and self.gamer2_step.get() == 1:
            if sign == self.pc1_sign and self.gamer1_step.get() == 1:
                self.pc1_win += 1
            elif sign == self.pc2_sign and self.gamer2_step.get() == 1:
                self.pc2_win += 1
        else:
            if sign == self.user_sign and self.gamer1_step.get() == 0:
                self.user1_win += 1
            if sign == self.pc_sign and self.gamer1_step.get() == 1:
                self.pc1_win += 1
            if sign == self.user_sign and self.gamer2_step.get() == 0:
                self.user2_win += 1
            if sign == self.pc_sign and self.gamer2_step.get() == 1:
                self.pc2_win += 1
        self.flag_end = True

    def show_win(self, win, sign):
        if self.gamer1_step.get() == 0 and self.gamer2_step.get() == 0:
            if sign == self.user1_sign:
                color = 'green'
            else:
                color = 'red'
        elif self.gamer1_step.get() == 1 and self.gamer2_step.get() == 1:
            if sign == self.pc1_sign:
                color = 'green'
            else:
                color = 'red'
        else:
            if sign == self.user_sign:
                color = 'green'
            else:
                color = 'red'
        for square_num in win:
            square = self.squares[square_num]
            square.cfg(color)

    def game_over(self):
        """ окончание игры """
        self.stat_msg.set(
            'Сыграно игр: %s\nИз них:\n  побед игрока 1: %s\n  побед игрока 2: %s\n  побед компьютера 1: %s\n  побед компьютера 2: %s\n  ничьих: %s' %
            (self.user1_win + self.user2_win + self.pc1_win + self.pc2_win + self.drawn_game,
             self.user1_win, self.user2_win, self.pc1_win, self.pc2_win, self.drawn_game)
        )
        self.widget_state(NORMAL)
        self.btn.bind("<Button-1>", self.start)

    def test_win(self, sign):
        for a, b, c in self.wins:
            if self.board[a] == sign and self.board[b] == sign and self.board[c] == sign:
                self.show_win((a, b, c), sign)
                self.unbind_squares()
                self.greet_winner(sign)
                self.game_over()
                return
        if not self.free_squares:
            self.flag_end = True
            self.drawn_game += 1
            for square_num in range(9):
                square = self.squares[square_num]
                square.cfg('yellow')
            self.game_over()

    def is_win(self, sign, iboard):
        for a, b, c in self.wins:
            if iboard[a] == sign and iboard[b] == sign and iboard[c] == sign:
                return True
        return False

    def find_best_turn(self, sign):
        for iturn in self.free_squares:
            iboard = self.board[:]
            iboard[iturn] = sign
            res = self.is_win(sign, iboard)
            if res:
                return iturn
        return None

    def default_choice(self):
        if not self.choice_pc:
            self.choice_pc = [4]
            random.shuffle(self.choice_pc1)
            random.shuffle(self.choice_pc2)
            self.choice_pc.extend(self.choice_pc1)
            self.choice_pc.extend(self.choice_pc2)
        for iturn in self.choice_pc:
            if iturn in self.free_squares:
                return iturn

    def turn_pc(self):
        """ ход пк """
        if self.gamer1_step.get() == 1 and self.gamer2_step.get() == 1:
            if not self.flag_choice_pc:
                square_num = self.find_best_turn(self.pc1_sign)
                if square_num is None:
                    square_num = self.find_best_turn(self.pc2_sign)
                    if square_num is None:
                        square_num = self.default_choice()
                self.turn(square_num, self.pc1_sign)
                self.flag_choice_pc = True
            else:
                square_num = self.find_best_turn(self.pc2_sign)
                if square_num is None:
                    square_num = self.find_best_turn(self.pc1_sign)
                    if square_num is None:
                        square_num = self.default_choice()
                self.turn(square_num, self.pc2_sign)
                self.flag_choice_pc = False
        else:
            square_num = self.find_best_turn(self.pc_sign)
            if square_num is None:
                square_num = self.find_best_turn(self.user_sign)
                if square_num is None:
                    square_num = self.default_choice()
            self.turn(square_num, self.pc_sign)

    def change_board(self, square_num, sign):
        """ заполнение клетки знаком """
        self.board[square_num] = sign
        self.free_squares.remove(square_num)
        if self.gamer1_step.get() == 0 and self.gamer2_step.get() == 0:
            self.flag_choice_user = True

    def turn(self, square_num, sign):
        square = self.squares[square_num]
        square.unbind()
        square.set_msg(sign)
        self.change_board(square_num, sign)
        self.test_win(sign)

    def turn_user(self, square_num):
        """ ход игрока """
        if self.gamer1_step.get() == 0 and self.gamer2_step.get() == 0:
            if self.first_step.get() == 0:
                if not self.flag_choice_user:
                    self.turn(square_num, self.user1_sign)
                else:
                    self.turn(square_num, self.user2_sign)
                    self.flag_choice_user = False
            elif self.first_step.get() == 1:
                if not self.flag_choice_user:
                    self.turn(square_num, self.user2_sign)
                else:
                    self.turn(square_num, self.user1_sign)
                    self.flag_choice_user = False
        elif (self.gamer1_step.get() == 0 and self.gamer2_step.get() == 1) or (
                self.gamer1_step.get() == 1 and self.gamer2_step.get() == 0):
            self.turn(square_num, self.user_sign)
            if not self.flag_end:
                self.turn_pc()


def main():
    root = Tk()
    root.title('Крестики - Нолики')
    root.resizable(FALSE, FALSE)
    app = Application(root)
    root.mainloop()


if __name__ == '__main__':
    main()
→ Ссылка
Автор решения: Mikhail

Вариант кода с режимом PC vs PC

from tkinter import *
import random
import time


class Square:
    """ задает квадраты(клетки) """

    def __init__(self, master, root, num, row, column):
        self.master = master
        self.square_num = num
        self.msg = StringVar()
        self.lbl = Label(root, textvariable=self.msg, font="FreeSerifBold 75", bg="white")
        self.lbl.grid(row=row, column=column, sticky=NSEW, padx=5, pady=5)

    def unbind(self):
        self.lbl.unbind("<Button-1>")

    def bind(self):
        self.lbl.bind("<Button-1>", self.choice)

    def bind_comp(self):
        self.lbl.after(1000, self.choice_comp)

    def set_msg(self, txt):
        self.msg.set(txt)

    def choice(self, event):
        self.master.turn_user(self.square_num)

    def choice_comp(self):
        self.master.turn_pc()

    def cfg(self, color):
        self.lbl.configure(bg=color)


class Application(Frame):
    def __init__(self, master):
        super(Application, self).__init__(master)
        self.master = master
        self.master.config(bg="#D9D9D9")
        # константы
        self.wins = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6),
                     (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6))
        self.board_start = ['_'] * 9
        self.free_squares_start = [x for x in range(9)]
        self.choice_pc1 = [0, 2, 6, 8]
        self.choice_pc2 = [1, 3, 5, 7]
        # статистика игры
        self.drawn_game = 0
        self.user1_win = 0
        self.user2_win = 0
        self.pc1_win = 0
        self.pc2_win = 0
        self.create_widgets()

    def create_widgets(self):
        # блок статистики
        stat_frame = Frame(self.master)
        stat_frame.grid(row=0, column=0, padx=5, pady=5, sticky=N)
        # статистика
        statistics = LabelFrame(stat_frame, text=" Статистика ")
        statistics.grid()
        self.stat_msg = StringVar()
        stat_lbl = Label(statistics, textvariable=self.stat_msg, justify=LEFT, width=20)
        stat_lbl.grid(padx=0)
        self.stat_msg.set(
            'Сыграно игр: %s\nИз них:\n  побед игрока 1: %s\n  побед игрока 2: %s\n  побед компьютера 1: %s\n  побед компьютера 2: %s\n  ничьих: %s' %
            (self.user1_win + self.user2_win + self.pc1_win + self.pc2_win + self.drawn_game,
             self.user1_win, self.user2_win, self.pc1_win, self.pc2_win, self.drawn_game)
        )
        # блок игрока 1
        gamer1_frame = Frame(self.master)
        gamer1_frame.grid(row=1, column=0, padx=5, pady=5, sticky=W)
        step_choice = LabelFrame(gamer1_frame, text=" Игрок 1 ")
        step_choice.grid(ipadx=26)
        self.gamer1_step = IntVar()
        self.gamer1_step.set(0)
        gamer1_step0 = Radiobutton(step_choice, text='Игрок', variable=self.gamer1_step, value=0)
        gamer1_step1 = Radiobutton(step_choice, text='Компьютер', variable=self.gamer1_step, value=1)
        gamer1_step0.grid(ipady=2, sticky=W)
        gamer1_step1.grid(ipady=3, sticky=W)
        # блок игрока 2
        gamer2_frame = Frame(self.master)
        gamer2_frame.grid(row=1, column=1, padx=5, pady=5, sticky=W)
        step_choice = LabelFrame(gamer2_frame, text=" Игрок 2 ")
        step_choice.grid(ipadx=26)
        self.gamer2_step = IntVar()
        self.gamer2_step.set(1)
        gamer2_step0 = Radiobutton(step_choice, text='Игрок', variable=self.gamer2_step, value=0)
        gamer2_step1 = Radiobutton(step_choice, text='Компьютер', variable=self.gamer2_step, value=1)
        gamer2_step0.grid(ipady=2, sticky=W)
        gamer2_step1.grid(ipady=3, sticky=W)
        # блок выбора хода
        step_frame = Frame(self.master)
        step_frame.grid(row=0, column=1, padx=10, pady=5, sticky=N)
        step_choice = LabelFrame(step_frame, text=" Ходит первым ")
        step_choice.grid(ipadx=20, ipady=25)
        self.first_step = IntVar()
        self.first_step.set(0)
        first_step0 = Radiobutton(step_choice, text='Игрок 1', variable=self.first_step, value=0)
        first_step1 = Radiobutton(step_choice, text='Игрок 2', variable=self.first_step, value=1)
        first_step0.grid(ipady=3, sticky=W)
        first_step1.grid(ipady=2, sticky=W)
        # блок выбора знака
        sign_frame = Frame(self.master)
        sign_frame.grid(row=0, column=2, padx=5, pady=5)
        # выбор знака
        sign_choice = LabelFrame(sign_frame, text=" Выбор знака ")
        sign_choice.grid(ipadx=20)
        self.gamer1_choice = StringVar()
        self.gamer1_choice.set('0')
        gamer1_sign0 = Radiobutton(sign_choice, text='Игрок 1 "0"', variable=self.gamer1_choice, value='0')
        gamer1_sign1 = Radiobutton(sign_choice, text='Игрок 1 "X"', variable=self.gamer1_choice, value='X')
        gamer1_sign0.grid()
        gamer1_sign1.grid()
        # кнопка старт
        self.btn = Button(sign_frame, text="Старт")
        self.btn.bind("<Button-1>", self.start)
        self.btn.grid(pady=6, ipady=10, sticky=NSEW)
        # игровое поле
        self.squares = {}
        num = 0
        for row in range(2, 5):
            for column in range(0, 3):
                self.squares[num] = Square(self, self.master, num, row, column)
                num += 1
        self.widgets = [gamer1_sign0, gamer1_sign1, gamer1_step0, gamer1_step1, gamer2_step0, gamer2_step1, first_step0,
                        first_step1, self.btn]

    def start(self, *args):
        self.choice_pc = None
        self.flag_end = False
        self.flag_choice_user = False
        self.flag_choice_pc = False
        self.free_squares = self.free_squares_start[:]
        self.board = self.board_start[:]
        for square_num in range(9):
            square = self.squares[square_num]
            if self.gamer1_step.get() == 1 and self.gamer2_step.get() == 1:
                square.bind_comp()
            else:
                square.bind()
            square.set_msg('')
            square.cfg('white')
        gs1 = self.gamer1_step.get()
        gs2 = self.gamer2_step.get()
        if self.gamer1_choice.get() == '0':
            if gs1 == 0 and gs2 == 1:
                self.user_sign, self.pc_sign = '0', 'X'
            elif gs1 == 1 and gs2 == 0:
                self.pc_sign, self.user_sign = '0', 'X'
            elif gs1 == 0 and gs2 == 0:
                self.user1_sign, self.user2_sign = '0', 'X'
            elif gs1 == 1 and gs2 == 1:
                self.pc1_sign, self.pc2_sign = '0', 'X'
        else:
            if gs1 == 0 and gs2 == 1:
                self.user_sign, self.pc_sign = 'X', '0'
            elif gs1 == 1 and gs2 == 0:
                self.pc_sign, self.user_sign = 'X', '0'
            elif gs1 == 0 and gs2 == 0:
                self.user1_sign, self.user2_sign = 'X', '0'
            elif gs1 == 1 and gs2 == 1:
                self.pc1_sign, self.pc2_sign = 'X', '0'
        fs = self.first_step.get()
        if fs == 0:
            if gs1 == 1:
                self.turn_pc()
        elif fs == 1:
            if gs2 == 1:
                self.flag_choice_pc = True
                self.turn_pc()
        self.widget_state()
        self.btn.unbind("<Button-1>")

    def widget_state(self, wst=DISABLED):
        for widget in self.widgets:
            widget.config(state=wst)

    def unbind_squares(self):
        for square_num in self.free_squares:
            self.squares[square_num].unbind()

    def greet_winner(self, sign):
        """ поздравления """
        if self.gamer1_step.get() == 0 and self.gamer2_step.get() == 0:
            if sign == self.user1_sign and self.gamer1_step.get() == 0:
                self.user1_win += 1
            if sign == self.user2_sign and self.gamer2_step.get() == 0:
                self.user2_win += 1
        elif self.gamer1_step.get() == 1 and self.gamer2_step.get() == 1:
            if sign == self.pc1_sign and self.gamer1_step.get() == 1:
                self.pc1_win += 1
            elif sign == self.pc2_sign and self.gamer2_step.get() == 1:
                self.pc2_win += 1
        else:
            if sign == self.user_sign and self.gamer1_step.get() == 0:
                self.user1_win += 1
            if sign == self.pc_sign and self.gamer1_step.get() == 1:
                self.pc1_win += 1
            if sign == self.user_sign and self.gamer2_step.get() == 0:
                self.user2_win += 1
            if sign == self.pc_sign and self.gamer2_step.get() == 1:
                self.pc2_win += 1
        self.flag_end = True

    def show_win(self, win, sign):
        if self.gamer1_step.get() == 0 and self.gamer2_step.get() == 0:
            if sign == self.user1_sign:
                color = 'green'
            else:
                color = 'red'
        elif self.gamer1_step.get() == 1 and self.gamer2_step.get() == 1:
            if sign == self.pc1_sign:
                color = 'green'
            else:
                color = 'red'
        else:
            if sign == self.user_sign:
                color = 'green'
            else:
                color = 'red'
        for square_num in win:
            square = self.squares[square_num]
            square.cfg(color)

    def game_over(self):
        """ окончание игры """
        self.stat_msg.set(
            'Сыграно игр: %s\nИз них:\n  побед игрока 1: %s\n  побед игрока 2: %s\n  побед компьютера 1: %s\n  побед компьютера 2: %s\n  ничьих: %s' %
            (self.user1_win + self.user2_win + self.pc1_win + self.pc2_win + self.drawn_game,
             self.user1_win, self.user2_win, self.pc1_win, self.pc2_win, self.drawn_game)
        )
        self.widget_state(NORMAL)
        self.btn.bind("<Button-1>", self.start)

    def test_win(self, sign):
        for a, b, c in self.wins:
            if self.board[a] == sign and self.board[b] == sign and self.board[c] == sign:
                self.show_win((a, b, c), sign)
                self.unbind_squares()
                self.greet_winner(sign)
                self.game_over()
                return
        if not self.free_squares:
            self.flag_end = True
            self.drawn_game += 1
            for square_num in range(9):
                square = self.squares[square_num]
                square.cfg('yellow')
            self.game_over()

    def is_win(self, sign, iboard):
        for a, b, c in self.wins:
            if iboard[a] == sign and iboard[b] == sign and iboard[c] == sign:
                return True
        return False

    def find_best_turn(self, sign):
        for iturn in self.free_squares:
            iboard = self.board[:]
            iboard[iturn] = sign
            res = self.is_win(sign, iboard)
            if res:
                return iturn
        return None

    def default_choice(self):
        if not self.choice_pc:
            self.choice_pc = [4]
            random.shuffle(self.choice_pc1)
            random.shuffle(self.choice_pc2)
            self.choice_pc.extend(self.choice_pc1)
            self.choice_pc.extend(self.choice_pc2)
        for iturn in self.choice_pc:
            if iturn in self.free_squares:
                return iturn

    def turn_pc(self):
        """ ход пк """
        if self.gamer1_step.get() == 1 and self.gamer2_step.get() == 1:
            if not self.flag_choice_pc:
                square_num = self.find_best_turn(self.pc1_sign)
                if square_num is None:
                    square_num = self.find_best_turn(self.pc2_sign)
                    if square_num is None:
                        square_num = self.default_choice()
                self.turn(square_num, self.pc1_sign)
                self.flag_choice_pc = True
            else:
                square_num = self.find_best_turn(self.pc2_sign)
                if square_num is None:
                    square_num = self.find_best_turn(self.pc1_sign)
                    if square_num is None:
                        square_num = self.default_choice()
                self.turn(square_num, self.pc2_sign)
                self.flag_choice_pc = False
        else:
            square_num = self.find_best_turn(self.pc_sign)
            if square_num is None:
                square_num = self.find_best_turn(self.user_sign)
                if square_num is None:
                    square_num = self.default_choice()
            self.turn(square_num, self.pc_sign)

    def change_board(self, square_num, sign):
        """ заполнение клетки знаком """
        self.board[square_num] = sign
        self.free_squares.remove(square_num)
        if self.gamer1_step.get() == 0 and self.gamer2_step.get() == 0:
            self.flag_choice_user = True

    def turn(self, square_num, sign):
        if square_num is not None:
            square = self.squares[square_num]
            square.unbind()
            square.set_msg(sign)
            self.change_board(square_num, sign)
            self.test_win(sign)

    def turn_user(self, square_num):
        """ ход игрока """
        if self.gamer1_step.get() == 0 and self.gamer2_step.get() == 0:
            if self.first_step.get() == 0:
                if not self.flag_choice_user:
                    self.turn(square_num, self.user1_sign)
                else:
                    self.turn(square_num, self.user2_sign)
                    self.flag_choice_user = False
            elif self.first_step.get() == 1:
                if not self.flag_choice_user:
                    self.turn(square_num, self.user2_sign)
                else:
                    self.turn(square_num, self.user1_sign)
                    self.flag_choice_user = False
        elif (self.gamer1_step.get() == 0 and self.gamer2_step.get() == 1) or (
                self.gamer1_step.get() == 1 and self.gamer2_step.get() == 0):
            self.turn(square_num, self.user_sign)
            if not self.flag_end:
                self.turn_pc()


def main():
    root = Tk()
    root.title('Крестики - Нолики')
    root.resizable(FALSE, FALSE)
    app = Application(root)
    root.mainloop()


if __name__ == '__main__':
    main()
→ Ссылка