Загрузка данных
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()