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


import time
import keyboard
from analysis import ChessAnalyzer
import crop


class ChessLogic:
    def __init__(self):
        self.analyzer = ChessAnalyzer()
        
        # Состояние доски 8x8 в формате FEN
        self.board_state = []
        self.last_crop_matrix = None
        self.is_analyzed = False
        
        self.active_color = 'w'  # 'w' - белые, 'b' - черные
        
        # Храним последнюю сходившую пешку для проверки взятия на проходе
        # Формат: {'pawn_pos': (row, col), 'target_pos': (row, col)}
        self.en_passant_target = None

    def convert_coords_to_notation(self, row, col):
        files = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
        ranks = ['8', '7', '6', '5', '4', '3', '2', '1']
        return f"{files[col]}{ranks[row]}"

    def is_white_piece(self, piece):
        return piece.isupper() if piece else False

    def is_black_piece(self, piece):
        return piece.islower() if piece else False

    def start_initial_analysis(self):
        """Единоразовый запуск по 'S'"""
        print("\n[!] Нажата 'S'. Первичный анализ доски...")
        self.board_state = self.analyzer.analyze_initial_board()
        
        if self.board_state:
            crop_matrix, _ = crop.find_chessboard_strictly_1to1()
            self.last_crop_matrix = crop_matrix
            self.is_analyzed = True
            print("[✓] Начальная позиция успешно зафиксирована!")
            self.print_board()
            print(f"Начальный FEN: {self.get_fen_string()}\n")
        else:
            print("[!] Ошибка: не удалось проанализировать доску.")

    def get_fen_string(self):
        if not self.board_state:
            return ""

        fen_rows = []
        for row in self.board_state:
            empty_count = 0
            row_fen = ""
            for cell in row:
                if cell == "":
                    empty_count += 1
                else:
                    if empty_count > 0:
                        row_fen += str(empty_count)
                        empty_count = 0
                    row_fen += cell
            if empty_count > 0:
                row_fen += str(empty_count)
            fen_rows.append(row_fen)

        position_fen = "/".join(fen_rows)
        
        # Обозначение целевой клетки для взятия на проходе в FEN
        ep_str = "-"
        if self.en_passant_target:
            ep_str = self.convert_coords_to_notation(*self.en_passant_target['target_pos'])

        return f"{position_fen} {self.active_color} KQkq {ep_str} 0 1"

    def print_board(self):
        if not self.board_state:
            return
        print("\n    a   b   c   d   e   f   g   h")
        print("  +-------------------------------+")
        for i, row in enumerate(self.board_state):
            rank = 8 - i
            row_str = " | ".join([p if p != "" else "." for p in row])
            print(f"{rank} | {row_str} | {rank}")
            print("  +-------------------------------+")
        print("    a   b   c   d   e   f   g   h\n")

    def make_move(self, from_row, from_col, to_row, to_col, is_capture=False, is_en_passant=False, captured_pawn_pos=None):
        """Исполнение хода в памяти и переключение очередности"""
        moved_piece = self.board_state[from_row][from_col]
        captured_piece = self.board_state[to_row][to_col] if is_capture and not is_en_passant else ""

        from_not = self.convert_coords_to_notation(from_row, from_col)
        to_not = self.convert_coords_to_notation(to_row, to_col)

        # 1. Если это взятие на проходе
        if is_en_passant and captured_pawn_pos:
            cp_row, cp_col = captured_pawn_pos
            captured_piece = self.board_state[cp_row][cp_col]
            self.board_state[cp_row][cp_col] = ""  # Сбиваем пешку с её позиции
            print(f"[⚡ EN PASSANT]: Пешка {moved_piece} с {from_not} сбила на проходе {captured_piece} на {to_not}!")
        
        # 2. Обычное взятие
        elif is_capture:
            print(f"[⚔ CAPTURE]: {moved_piece} с {from_not} срубила {captured_piece} на {to_not}!")
        
        # 3. Тихий ход
        else:
            print(f"[♟ MOVE]: {moved_piece} с {from_not} на {to_not}")

        # Переставляем фигуру
        self.board_state[to_row][to_col] = moved_piece
        self.board_state[from_row][from_col] = ""

        # Проверяем, сделала ли пешка ход на 2 клетки (фиксация поля для взятия на проходе)
        if moved_piece.upper() == 'P' and abs(from_row - to_row) == 2:
            middle_row = (from_row + to_row) // 2
            self.en_passant_target = {
                'pawn_pos': (to_row, to_col),
                'target_pos': (middle_row, to_col)
            }
        else:
            self.en_passant_target = None  # Взятие на проходе возможно только следующим ходом!

        # Смена очередности хода
        self.active_color = 'b' if self.active_color == 'w' else 'w'

        self.print_board()
        print(f"Новый FEN: {self.get_fen_string()}\n")

    def track_board_changes(self):
        """Отслеживание изменений от crop.py"""
        if not self.is_analyzed or self.last_crop_matrix is None:
            return

        new_crop_matrix, _ = crop.find_chessboard_strictly_1to1()

        if new_crop_matrix is None:
            return

        from_cells = []  # True -> False (освободились)
        to_cells = []    # False -> True (занялись)

        for r in range(8):
            for c in range(8):
                old_val = self.last_crop_matrix[r][c]
                new_val = new_crop_matrix[r][c]

                if old_val and not new_val:
                    from_cells.append((r, c))
                elif not old_val and new_val:
                    to_cells.append((r, c))

        # --- СЦЕНАРИЙ 1: Обычный ход (1 ушла, 1 пришла) ---
        if len(from_cells) == 1 and len(to_cells) == 1:
            f_row, f_col = from_cells[0]
            t_row, t_col = to_cells[0]
            
            self.make_move(f_row, f_col, t_row, t_col)
            self.last_crop_matrix = new_crop_matrix

        # --- СЦЕНАРИЙ 2: Обычное взятие (1 ушла, 0 пришло) ---
        elif len(from_cells) == 1 and len(to_cells) == 0:
            f_row, f_col = from_cells[0]
            moved_piece = self.board_state[f_row][f_col]

            if not moved_piece:
                return

            # Ищем вокруг ходившей фигуры вражескую клетку, на которой стояла жертва
            possible_targets = []
            for r in range(8):
                for c in range(8):
                    target_piece = self.board_state[r][c]
                    if target_piece != "":
                        # Фигура должна быть чужого цвета
                        is_enemy = (self.is_white_piece(moved_piece) and self.is_black_piece(target_piece)) or \
                                   (self.is_black_piece(moved_piece) and self.is_white_piece(target_piece))
                        
                        if is_enemy:
                            possible_targets.append((r, c))

            # По логике ходов берем наиболее подходящую соседнюю клетку
            # (Для пешки — по диагонали, для остальных — по траектории)
            best_target = None
            min_dist = float('inf')
            
            for (tr, tc) in possible_targets:
                # Дистанция между клетками
                dist = (tr - f_row)**2 + (tc - f_col)**2
                
                # Пешка бьет только по диагонали на 1 клетку вперед
                if moved_piece.upper() == 'P':
                    direction = -1 if self.is_white_piece(moved_piece) else 1
                    if tr - f_row == direction and abs(tc - f_col) == 1:
                        best_target = (tr, tc)
                        break
                else:
                    if dist < min_dist:
                        min_dist = dist
                        best_target = (tr, tc)

            if best_target:
                t_row, t_col = best_target
                self.make_move(f_row, f_col, t_row, t_col, is_capture=True)
                self.last_crop_matrix = new_crop_matrix

        # --- СЦЕНАРИЙ 3: Взятие на проходе (2 ушло, 1 пришла) ---
        elif len(from_cells) == 2 and len(to_cells) == 1:
            t_row, t_col = to_cells[0]
            
            # Определяем, какая из двух освободившихся клеток была нашей атакующей пешкой,
            # а какая — сбитой вражеской пешкой
            pawn_from = None
            captured_pawn = None

            for (r, c) in from_cells:
                piece = self.board_state[r][c]
                if piece.upper() == 'P':
                    # Наша пешка ходит по диагонали
                    if abs(c - t_col) == 1:
                        pawn_from = (r, c)
                    else:
                        captured_pawn = (r, c)

            if pawn_from and captured_pawn:
                f_row, f_col = pawn_from
                self.make_move(f_row, f_col, t_row, t_col, is_capture=True, is_en_passant=True, captured_pawn_pos=captured_pawn)
                self.last_crop_matrix = new_crop_matrix

    def run(self):
        print("=========================================")
        print(" Шахматный бот запущен.")
        print(" Нажмите 'S' для первоначального анализа.")
        print("=========================================")

        keyboard.add_hotkey('s', self.start_initial_analysis)

        try:
            while True:
                if self.is_analyzed:
                    self.track_board_changes()
                time.sleep(0.15)

        except KeyboardInterrupt:
            print("\nПрограмма завершена.")


if __name__ == '__main__':
    logic = ChessLogic()
    logic.run()