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


import time
import random
import pyautogui
import crop

# Отключаем системную задержку pyautogui для полного контроля скорости
pyautogui.PAUSE = 0.01
pyautogui.FAILSAFE = False


class ChessMover:
    def __init__(self):
        self.auto_move_enabled = False
        self.humanly_mode = True   # По умолчанию включен плавно-человечный режим
        self.move_duration = 0.35  # Скорость перемещения (в секундах)

    def toggle_auto_move(self):
        """Включение / Выключение автоматических ходов (Клавиша L)"""
        self.auto_move_enabled = not self.auto_move_enabled
        status = "ВКЛЮЧЁН" if self.auto_move_enabled else "ВЫКЛЮЧЕН"
        print(f"\n[AUTO-MOVE]: Автоход -> {status}")

    def toggle_humanly_mode(self):
        """Переключение между плавным зажатием и кликами (Клавиша H)"""
        self.humanly_mode = not self.humanly_mode
        mode_str = "HUMANLY (Плавный перенос)" if self.humanly_mode else "CLICK (Клик-Клик)"
        print(f"\n[MOVE MODE]: Режим движения -> {mode_str}")

    def speed_up(self):
        """Увеличить скорость движения мыши (Клавиша '+')"""
        self.move_duration = max(0.05, self.move_duration - 0.05)
        print(f"\n[SPEED +]: Время перемещения уменьшено до {self.move_duration:.2f} сек")

    def speed_down(self):
        """Уменьшить скорость движения мыши (Клавиша '-')"""
        self.move_duration = min(2.0, self.move_duration + 0.05)
        print(f"\n[SPEED -]: Время перемещения увеличено до {self.move_duration:.2f} сек")

    def get_absolute_board_rect(self):
        """Вычисляет абсолютные координаты доски на всём экране на основе crop.py"""
        if crop._tracker.last_board_rect is None:
            return None
        
        bx, by, size = crop._tracker.last_board_rect
        mon = crop._tracker.monitor
        abs_x = mon["left"] + bx
        abs_y = mon["top"] + by
        return (abs_x, abs_y, size)

    def square_to_screen_coords(self, square_str, board_rect, my_color='w'):
        """Преобразует нотацию шахматной клетки (например, 'e2') в координаты экрана (X, Y)"""
        if not board_rect or len(square_str) < 2:
            return None

        abs_x, abs_y, board_size = board_rect
        cell_size = board_size / 8.0

        file_char = square_str[0].lower()
        rank_char = square_str[1]

        file_idx = ord(file_char) - ord('a')  # 0..7 (столбцы a-h)
        rank_idx = int(rank_char)             # 1..8 (горизонтали)

        # Ориентация доски в зависимости от цвета игрока
        if my_color == 'w':
            col = file_idx
            row = 8 - rank_idx
        else:
            col = 7 - file_idx
            row = rank_idx - 1

        # Случайное микро-отклонение от центра клетки (человеческий фактор)
        jitter_x = random.uniform(-cell_size * 0.12, cell_size * 0.12)
        jitter_y = random.uniform(-cell_size * 0.12, cell_size * 0.12)

        center_x = int(abs_x + (col + 0.5) * cell_size + jitter_x)
        center_y = int(abs_y + (row + 0.5) * cell_size + jitter_y)

        return center_x, center_y

    def execute_move(self, uci_move, my_color='w'):
        """Выполняет передвижение фигуры на экране по UCI-строке (например, 'e2e4')"""
        if not self.auto_move_enabled:
            return

        board_rect = self.get_absolute_board_rect()
        if not board_rect:
            print("[!] Ошибка физического хода: Координаты доски не найдены в crop!")
            return

        from_sq = uci_move[:2]
        to_sq = uci_move[2:4]

        start_pos = self.square_to_screen_coords(from_sq, board_rect, my_color)
        end_pos = self.square_to_screen_coords(to_sq, board_rect, my_color)

        if not start_pos or not end_pos:
            print(f"[!] Не удалось рассчитать координаты для хода: {uci_move}")
            return

        start_x, start_y = start_pos
        end_x, end_y = end_pos

        mode_desc = "Humanly (Drag)" if self.humanly_mode else "Click"
        print(f"[PHYSICAL MOVE]: Выполняем {uci_move} | {mode_desc} | Скорость: {self.move_duration:.2f}s")

        if self.humanly_mode:
            # --- 3 РЕЖИМ: HUMANLY (Подвод, зажатие, плавное перемещение, отпускание) ---
            pyautogui.moveTo(start_x, start_y, duration=self.move_duration * 0.4, tween=pyautogui.easeOutQuad)
            pyautogui.mouseDown()
            time.sleep(random.uniform(0.02, 0.05))
            
            # Плавная траектория движения к клетке назначения
            pyautogui.moveTo(end_x, end_y, duration=self.move_duration, tween=pyautogui.easeInOutQuad)
            time.sleep(random.uniform(0.02, 0.05))
            pyautogui.mouseUp()
        else:
            # --- РЕЖИМ КЛИКОВ (Обычное нажатие на фигуру и на клетку) ---
            pyautogui.moveTo(start_x, start_y, duration=self.move_duration * 0.3)
            pyautogui.click()
            time.sleep(random.uniform(0.04, 0.08))
            pyautogui.moveTo(end_x, end_y, duration=self.move_duration * 0.3)
            pyautogui.click()

        # При превращении пешки (e7e8q) делается дополнительный клик
        if len(uci_move) == 5:
            time.sleep(0.1)
            pyautogui.click()