Загрузка данных


import socket
import threading
import tkinter as tk
from tkinter import messagebox, simpledialog

PORT = 55555


class NetworkTicTacToe:

    def __init__(self):
        self.root = tk.Tk()
        self.root.title("Сетевые Крестики-Нолики")

        self.board = [" " for _ in range(9)]
        self.my_turn = False
        self.my_symbol = ""
        self.opp_symbol = ""
        self.conn = None

        self.buttons = []
        self.create_widgets()
        self.choose_role()

    def create_widgets(self):
        # Создаем сетку кнопок 3х3
        for i in range(9):
            btn = tk.Button(
                self.root,
                text=" ",
                font=("Arial", 24),
                width=5,
                height=2,
                command=lambda idx=i: self.player_click(idx),
            )
            btn.grid(row=i // 3, column=i % 3)
            self.buttons.append(btn)

        # Статусная строка
        self.status_label = tk.Label(
            self.root, text="Выбор режима...", font=("Arial", 12)
        )
        self.status_label.grid(row=3, column=0, columnspan=3, sticky="we")

    def choose_role(self):
        # Всплывающее окно выбора роли при старте
        role_window = tk.Toplevel(self.root)
        role_window.title("Режим игры")
        role_window.geometry("250x120")
        role_window.grab_set()  # Блокирует основное окно

        tk.Label(role_window, text="Выберите режим:", font=("Arial", 12)).pack(
            pady=10
        )

        def start_server():
            role_window.destroy()
            threading.Thread(target=self.init_server, daemon=True).start()

        def start_client():
            role_window.destroy()
            threading.Thread(target=self.init_client, daemon=True).start()

        tk.Button(
            role_window, text="1. Создать сервер (X)", command=start_server
        ).pack(side="left", padx=20, pady=10)
        tk.Button(
            role_window, text="2. Подключиться (O)", command=start_client
        ).pack(side="right", padx=20, pady=10)

    def init_server(self):
        self.my_symbol = "X"
        self.opp_symbol = "O"
        self.my_turn = True  # Сервер ходит первым

        self.status_label.config(text="Ожидание игрока...")
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            s.bind(("0.0.0.0", PORT))
            s.listen(1)
            self.conn, addr = s.accept()
            self.status_label.config(text=f"Игрок подключен! Ваш ход ({self.my_symbol})")
            threading.Thread(target=self.receive_data, daemon=True).start()
        except Exception as e:
            messagebox.showerror("Ошибка", f"Не удалось запустить сервер: {e}")
            self.root.quit()

    def init_client(self):
        self.my_symbol = "O"
        self.opp_symbol = "X"
        self.my_turn = False  # Клиент ждет ход сервера

        # Спрашиваем IP-адрес сервера у пользователя
        ip = simpledialog.askstring(
            "Подключение", "Введите IP-адрес Сервера:", initialvalue="127.0.0.1"
        )
        if not ip:
            self.root.quit()
            return

        self.status_label.config(text="Подключение к серверу...")
        self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            self.conn.connect((ip, PORT))
            self.status_label.config(text=f"Подключено! Ход противника ({self.opp_symbol})")
            threading.Thread(target=self.receive_data, daemon=True).start()
        except Exception as e:
            messagebox.showerror("Ошибка", f"Не удалось подключиться: {e}")
            self.root.quit()

    def player_click(self, idx):
        # Проверяем, наш ли ход и свободна ли ячейка
        if self.my_turn and self.board[idx] == " " and self.conn:
            self.board[idx] = self.my_symbol
            self.buttons[idx].config(text=self.my_symbol)

            # Отправляем индекс сделанного хода по сети
            try:
                self.conn.send(str(idx).encode())
            except:
                messagebox.showerror("Ошибка", "Связь разорвана!")
                return

            self.my_turn = False
            self.status_label.config(text=f"Ход противника ({self.opp_symbol})...")
            self.check_game_status()

    def receive_data(self):
        # Поток для постоянного прослушивания сети
        while True:
            try:
                data = self.conn.recv(1024).decode()
                if not data:
                    break
                idx = int(data)
                # Обновляем поле ходом противника
                self.board[idx] = self.opp_symbol
                self.buttons[idx].config(text=self.opp_symbol)

                self.my_turn = True
                self.status_label.config(text=f"Ваш ход ({self.my_symbol})")
                self.check_game_status()
            except:
                break
        messagebox.showinfo("Инфо", "Противник отключился от игры.")
        self.root.quit()

    def check_game_status(self):
        # Список победных комбинаций
        win_coords = [
            (0, 1, 2),
            (3, 4, 5),
            (6, 7, 8),  # Горизонтали
            (0, 3, 6),
            (1, 4, 7),
            (2, 5, 8),  # Вертикали
            (0, 4, 8),
            (2, 4, 6),  # Диагонали
        ]

        # Проверка победы
        for i, j, k in win_coords:
            if (
                self.board[i] == self.board[j] == self.board[k]
                and self.board[i] != " "
            ):
                winner = self.board[i]
                if winner == self.my_symbol:
                    messagebox.showinfo("Победа!", "Поздравляем! Вы выиграли!")
                else:
                    messagebox.showinfo("Поражение", "Вы проиграли. Повезет в следующий раз!")
                self.root.quit()
                return

        # Проверка ничьей
        if " " not in self.board:
            messagebox.showinfo("Ничья", "Ничья! Свободных клеток не осталось.")
            self.root.quit()

    def run(self):
        self.root.mainloop()


if __name__ == "__main__":
    game = NetworkTicTacToe()
    game.run()