Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- import time
- import threading
- import sys
- from math import inf
- # Globální konstanty – pohyby jezdce (relativní souřadnice)
- knight_moves = [(2, 1), (2, -1), (-2, 1), (-2, -1),
- (1, 2), (1, -2), (-1, 2), (-1, -2)]
- MATE = 10000 # Velikost hodnoty pro mat
- # Globální proměnné pro sledování času
- running = False
- start_time = 0
- ###############################################################################
- # Funkce pro výpis času v samostatném vlákně
- ###############################################################################
- def time_reporter():
- """Funkce pro výpis aktuálního času každou sekundu na stejném řádku."""
- global running, start_time
- while running:
- elapsed = time.time() - start_time
- hrs = int(elapsed // 3600)
- mins = int((elapsed % 3600) // 60)
- secs = int(elapsed % 60)
- sys.stdout.write(f"\r[INFO] Uplynulý čas: {hrs:02d}h {mins:02d}m {secs:02d}s")
- sys.stdout.flush()
- time.sleep(1)
- ###############################################################################
- # Třída Board – reprezentuje šachovnici a inicializuje ji z FEN řetězce.
- ###############################################################################
- class Board:
- def __init__(self, fen=None):
- """Inicializace šachovnice; pokud je zadán FEN, nastaví pozici podle něj."""
- self.grid = [[' ' for _ in range(8)] for _ in range(8)]
- self.to_move = 'w'
- self.castling_rights = set() # např. {'K','Q','k','q'}
- self.en_passant = None # (row, col) nebo None
- self.halfmove_clock = 0
- self.fullmove_number = 1
- if fen:
- self.set_fen(fen)
- def set_fen(self, fen):
- """Nastaví šachovnici podle FEN řetězce."""
- parts = fen.split()
- while len(parts) < 6:
- parts.append('0')
- board_part, turn_part = parts[0], parts[1]
- castling_part = parts[2] if len(parts) > 2 else '-'
- en_passant_part = parts[3] if len(parts) > 3 else '-'
- halfmove = parts[4] if len(parts) > 4 else '0'
- fullmove = parts[5] if len(parts) > 5 else '1'
- self.grid = [['.' for _ in range(8)] for _ in range(8)]
- ranks = board_part.split('/')
- # FEN řady začínají od horní (8.) a jdou dolů
- for rank_idx, rank_str in enumerate(ranks):
- file_idx = 0
- for ch in rank_str:
- if ch.isdigit():
- file_idx += int(ch)
- else:
- self.grid[rank_idx][file_idx] = ch
- file_idx += 1
- self.to_move = 'w' if turn_part == 'w' else 'b'
- self.castling_rights = set() if castling_part == '-' else set(castling_part)
- self.en_passant = None
- if en_passant_part != '-' and en_passant_part != '':
- file = ord(en_passant_part[0]) - ord('a')
- rank = int(en_passant_part[1])
- ri = 8 - rank
- fi = file
- if 0 <= ri < 8 and 0 <= fi < 8:
- self.en_passant = (ri, fi)
- try:
- self.halfmove_clock = int(halfmove)
- except:
- self.halfmove_clock = 0
- try:
- self.fullmove_number = int(fullmove)
- except:
- self.fullmove_number = 1
- def copy(self):
- """Vytvoří hlubokou kopii šachovnice."""
- new_board = Board()
- new_board.grid = [row.copy() for row in self.grid]
- new_board.to_move = self.to_move
- new_board.castling_rights = set(self.castling_rights)
- new_board.en_passant = None if self.en_passant is None else (self.en_passant[0], self.en_passant[1])
- new_board.halfmove_clock = self.halfmove_clock
- new_board.fullmove_number = self.fullmove_number
- return new_board
- def display(self):
- """Vrátí textovou reprezentaci šachovnice."""
- lines = []
- lines.append(" a b c d e f g h")
- lines.append(" ---------------")
- for ri in range(8):
- line = f"{8-ri}|"
- for fi in range(8):
- line += self.grid[ri][fi] + " "
- lines.append(line + f"|{8-ri}")
- lines.append(" ---------------")
- lines.append(" a b c d e f g h")
- lines.append(f"Na tahu: {'Bílý' if self.to_move == 'w' else 'Černý'}")
- return "\n".join(lines)
- ###############################################################################
- # Funkce pro převod tahu do notace
- ###############################################################################
- def move_to_notation(move):
- """Převede interní formát tahu (r1, c1, r2, c2, promo, special) na šachovou notaci."""
- r1, c1, r2, c2, promo, special = move
- from_square = chr(c1 + ord('a')) + str(8 - r1)
- to_square = chr(c2 + ord('a')) + str(8 - r2)
- if special == 'castle':
- if c2 > c1:
- return "O-O"
- else:
- return "O-O-O"
- notation = from_square + to_square
- if promo:
- notation += promo.lower()
- return notation
- ###############################################################################
- # Funkce pro detekci šachu, generování tahů a jejich provádění
- ###############################################################################
- def find_king(board, side):
- """Najde pozici krále pro stranu 'w' nebo 'b'."""
- target = 'K' if side=='w' else 'k'
- for r in range(8):
- for c in range(8):
- if board.grid[r][c] == target:
- return (r, c)
- return None
- def is_square_attacked(board, r, c, by_side):
- """Zjistí, zda je pole (r,c) napadeno stranou by_side."""
- # Útoky pěšcem
- if by_side == 'b':
- if r+1 < 8 and c-1 >= 0 and board.grid[r+1][c-1] == 'p': return True
- if r+1 < 8 and c+1 < 8 and board.grid[r+1][c+1] == 'p': return True
- else:
- if r-1 >= 0 and c-1 >= 0 and board.grid[r-1][c-1] == 'P': return True
- if r-1 >= 0 and c+1 < 8 and board.grid[r-1][c+1] == 'P': return True
- # Útoky jezdcem (a dalšími s jezdcovým pohybem)
- enemy_knights = ['n','a','c','e'] if by_side=='b' else ['N','A','C','E']
- for dr, dc in knight_moves:
- nr, nc = r+dr, c+dc
- if 0<=nr<8 and 0<=nc<8 and board.grid[nr][nc] in enemy_knights:
- return True
- # Útoky po řadách/sloupcích (věž, dáma, případně i Cyril = R+N či další kombinace)
- enemy_rook_like = ['r','q','e','a'] if by_side=='b' else ['R','Q','E','A']
- for dr, dc in [(1,0),(-1,0),(0,1),(0,-1)]:
- nr, nc = r+dr, c+dc
- while 0<=nr<8 and 0<=nc<8:
- if board.grid[nr][nc] != '.':
- if board.grid[nr][nc] in enemy_rook_like:
- return True
- break
- nr += dr; nc += dc
- # Útoky diagonálně (střelec, dáma, případně i Eve = B+N)
- enemy_bishop_like = ['b','q','c','a'] if by_side=='b' else ['B','Q','C','A']
- for dr, dc in [(1,1),(1,-1),(-1,1),(-1,-1)]:
- nr, nc = r+dr, c+dc
- while 0<=nr<8 and 0<=nc<8:
- if board.grid[nr][nc] != '.':
- if board.grid[nr][nc] in enemy_bishop_like:
- return True
- break
- nr += dr; nc += dc
- # Sousední král
- enemy_king = 'k' if by_side=='b' else 'K'
- for dr in [-1,0,1]:
- for dc in [-1,0,1]:
- if dr==0 and dc==0: continue
- nr, nc = r+dr, c+dc
- if 0<=nr<8 and 0<=nc<8 and board.grid[nr][nc] == enemy_king:
- return True
- return False
- def is_in_check(board, side):
- """Zjistí, zda je král strany side ('w' nebo 'b') v šachu."""
- king_pos = find_king(board, side)
- if not king_pos:
- return False
- kr, kc = king_pos
- enemy_side = 'b' if side=='w' else 'w'
- return is_square_attacked(board, kr, kc, enemy_side)
- def generate_pseudo_moves(board, side):
- """Generuje všechny pseudolegální tahy pro stranu side ('w' nebo 'b')."""
- moves = []
- enemy = 'b' if side=='w' else 'w'
- is_white = (side=='w')
- pawn_dir = -1 if is_white else 1
- start_rank = 6 if is_white else 1
- promote_rank = 0 if is_white else 7
- for r in range(8):
- for c in range(8):
- piece = board.grid[r][c]
- if piece == '.': continue
- if is_white and not piece.isupper(): continue
- if not is_white and not piece.islower(): continue
- pt = piece.upper()
- if pt == 'P':
- nr = r + pawn_dir
- if 0<=nr<8 and board.grid[nr][c]=='.':
- if nr==promote_rank:
- for promo in ['Q','R','B','N','A','E','C']:
- moves.append((r, c, nr, c, promo if is_white else promo.lower(), None))
- else:
- moves.append((r, c, nr, c, None, None))
- if r==start_rank and board.grid[r+pawn_dir*2][c]=='.' and board.grid[r+pawn_dir][c]=='.':
- moves.append((r, c, r+pawn_dir*2, c, None, 'double'))
- for dc in [-1,1]:
- nc = c + dc
- if 0<=nc<8 and 0<=nr<8:
- if board.grid[nr][nc] != '.' and ((is_white and board.grid[nr][nc].islower()) or (not is_white and board.grid[nr][nc].isupper())):
- if nr==promote_rank:
- for promo in ['Q','R','B','N','A','E','C']:
- moves.append((r, c, nr, nc, promo if is_white else promo.lower(), None))
- else:
- moves.append((r, c, nr, nc, None, None))
- if board.en_passant == (nr, nc):
- moves.append((r, c, nr, nc, None, 'enpassant'))
- elif pt == 'K':
- for dr in [-1,0,1]:
- for dc in [-1,0,1]:
- if dr==0 and dc==0: continue
- nr, nc = r+dr, c+dc
- if 0<=nr<8 and 0<=nc<8:
- if board.grid[nr][nc]=='.' or ((is_white and board.grid[nr][nc].islower()) or (not is_white and board.grid[nr][nc].isupper())):
- moves.append((r, c, nr, nc, None, None))
- # Rošády (základní verze)
- if is_white and r==7 and c==4:
- if 'K' in board.castling_rights and board.grid[7][5]=='.' and board.grid[7][6]=='.':
- moves.append((7,4,7,6,None,'castle'))
- if 'Q' in board.castling_rights and board.grid[7][3]=='.' and board.grid[7][2]=='.' and board.grid[7][1]=='.':
- moves.append((7,4,7,2,None,'castle'))
- if not is_white and r==0 and c==4:
- if 'k' in board.castling_rights and board.grid[0][5]=='.' and board.grid[0][6]=='.':
- moves.append((0,4,0,6,None,'castle'))
- if 'q' in board.castling_rights and board.grid[0][3]=='.' and board.grid[0][2]=='.' and board.grid[0][1]=='.':
- moves.append((0,4,0,2,None,'castle'))
- else:
- # Tahy pro figury s jezdcovým pohybem (N, A, C, E)
- if pt in ['N','A','C','E']:
- for dr, dc in knight_moves:
- nr, nc = r+dr, c+dc
- if 0<=nr<8 and 0<=nc<8:
- if board.grid[nr][nc]=='.' or ((is_white and board.grid[nr][nc].islower()) or (not is_white and board.grid[nr][nc].isupper())):
- moves.append((r, c, nr, nc, None, None))
- # Klouzavé tahy – pro R, Q, E, A
- if pt in ['R','Q','E','A']:
- for dr, dc in [(1,0),(-1,0),(0,1),(0,-1)]:
- nr, nc = r+dr, c+dc
- while 0<=nr<8 and 0<=nc<8:
- if board.grid[nr][nc]=='.':
- moves.append((r, c, nr, nc, None, None))
- else:
- if ((is_white and board.grid[nr][nc].islower()) or (not is_white and board.grid[nr][nc].isupper())):
- moves.append((r, c, nr, nc, None, None))
- break
- nr += dr; nc += dc
- if pt in ['B','Q','C','A']:
- for dr, dc in [(1,1),(1,-1),(-1,1),(-1,-1)]:
- nr, nc = r+dr, c+dc
- while 0<=nr<8 and 0<=nc<8:
- if board.grid[nr][nc]=='.':
- moves.append((r, c, nr, nc, None, None))
- else:
- if ((is_white and board.grid[nr][nc].islower()) or (not is_white and board.grid[nr][nc].isupper())):
- moves.append((r, c, nr, nc, None, None))
- break
- nr += dr; nc += dc
- return moves
- def get_legal_moves(board, side):
- """Vrátí seznam legálních tahů pro danou stranu."""
- moves = generate_pseudo_moves(board, side)
- legal_moves = []
- for move in moves:
- make_move(move, board)
- if not is_in_check(board, side):
- legal_moves.append(move)
- undo_move(board)
- return legal_moves
- # Zásobník pro tahy (pro undo)
- move_stack = []
- def make_move(move, board):
- """Provede tah na šachovnici a uloží stav pro možnost undo.
- move: (r1, c1, r2, c2, promo, special)"""
- r1, c1, r2, c2, promo, special = move
- piece = board.grid[r1][c1]
- captured = board.grid[r2][c2] if special != 'enpassant' else ('p' if piece=='P' else 'P')
- prev_state = (set(board.castling_rights), board.en_passant)
- move_stack.append((r1, c1, r2, c2, promo, special, piece, captured, prev_state))
- board.grid[r1][c1] = '.'
- if special == 'castle':
- board.grid[r2][c2] = piece
- if piece == 'K':
- if c2 == 6:
- board.grid[7][5] = 'R'; board.grid[7][7] = '.'
- else:
- board.grid[7][3] = 'R'; board.grid[7][0] = '.'
- else:
- if c2 == 6:
- board.grid[0][5] = 'r'; board.grid[0][7] = '.'
- else:
- board.grid[0][3] = 'r'; board.grid[0][0] = '.'
- elif special == 'enpassant':
- board.grid[r2][c2] = piece
- if piece == 'P':
- board.grid[r2+1][c2] = '.'
- else:
- board.grid[r2-1][c2] = '.'
- else:
- board.grid[r2][c2] = promo if promo else piece
- if piece == 'K':
- board.castling_rights.discard('K'); board.castling_rights.discard('Q')
- if piece == 'k':
- board.castling_rights.discard('k'); board.castling_rights.discard('q')
- if piece == 'R' and (r1, c1)==(7,7):
- board.castling_rights.discard('K')
- if piece == 'R' and (r1, c1)==(7,0):
- board.castling_rights.discard('Q')
- if piece == 'r' and (r1, c1)==(0,7):
- board.castling_rights.discard('k')
- if piece == 'r' and (r1, c1)==(0,0):
- board.castling_rights.discard('q')
- if special == 'double':
- board.en_passant = (r1 + (-1 if board.to_move=='w' else 1), c1)
- else:
- board.en_passant = None
- board.to_move = 'b' if board.to_move=='w' else 'w'
- def undo_move(board):
- """Vrátí poslední provedený tah."""
- r1, c1, r2, c2, promo, special, piece, captured, prev_state = move_stack.pop()
- board.grid[r1][c1] = piece
- if special == 'castle':
- board.grid[r2][c2] = '.'
- if piece == 'K':
- if c2 == 6:
- board.grid[7][7] = 'R'; board.grid[7][5] = '.'
- else:
- board.grid[7][0] = 'R'; board.grid[7][3] = '.'
- else:
- if c2 == 6:
- board.grid[0][7] = 'r'; board.grid[0][5] = '.'
- else:
- board.grid[0][0] = 'r'; board.grid[0][3] = '.'
- elif special == 'enpassant':
- board.grid[r2][c2] = '.'
- if piece == 'P':
- board.grid[r2+1][c2] = 'p'
- else:
- board.grid[r2-1][c2] = 'P'
- else:
- board.grid[r2][c2] = captured
- board.castling_rights, board.en_passant = prev_state
- board.to_move = 'b' if board.to_move=='w' else 'w'
- def analyze_position(board):
- """Analyzuje pozici a vrátí textový popis."""
- white_legal = get_legal_moves(board, 'w')
- black_legal = get_legal_moves(board, 'b')
- white_in_check = is_in_check(board, 'w')
- black_in_check = is_in_check(board, 'b')
- if not white_legal and white_in_check:
- return "Mat – černý vyhrává"
- if not black_legal and black_in_check:
- return "Mat – bílý vyhrává"
- if (not white_legal and not white_in_check) or (not black_legal and not black_in_check):
- return "Pat – remíza"
- return "Pozice je hratelná"
- ###############################################################################
- # Čistý negamax bez heuristik – hledáme pouze mate (terminální stavy)
- ###############################################################################
- MATE = 10000 # Hodnota pro mate
- # --- Ostatní funkce (time_reporter, Board, move_to_notation, find_king, is_square_attacked, is_in_check,
- # generate_pseudo_moves, get_legal_moves, make_move, undo_move, analyze_position) zůstávají beze změny --- #
- ###############################################################################
- # Upravený čistý negamax s check extension (a omezením počtu extenzí)
- ###############################################################################
- def negamax(board, alpha, beta, depth, max_depth, ext=0, max_ext=1):
- legal_moves = get_legal_moves(board, board.to_move)
- if not legal_moves:
- # Terminální uzel: pokud je strana v šachu, je to mate, jinak pat.
- if is_in_check(board, board.to_move):
- return -(MATE - depth), []
- else:
- return 0, []
- # Pokud jsme dosáhli cutoff hloubky, zkusíme prodloužit hledání, když je hráč v šachu.
- if depth == max_depth:
- if is_in_check(board, board.to_move) and ext < max_ext:
- # Prodloužíme hledání o jeden ply a zvýšíme počet extenzí
- return negamax(board, alpha, beta, depth, max_depth + 1, ext + 1, max_ext)
- else:
- return 0, []
- best_value = -inf
- best_line = []
- for move in legal_moves:
- make_move(move, board)
- child_score, child_line = negamax(board, -beta, -alpha, depth + 1, max_depth, ext, max_ext)
- score = -child_score
- undo_move(board)
- if score > best_value:
- best_value = score
- best_line = [move] + child_line
- alpha = max(alpha, score)
- if alpha >= beta:
- break
- return best_value, best_line
- ###############################################################################
- # Iterativní prohlubování s negamax – hledáme mate s “neomezenou” hloubkou
- ###############################################################################
- def iterative_deepening_negamax(board, max_time=300):
- global running, start_time
- running = True
- start_time = time.time()
- timer_thread = threading.Thread(target=time_reporter)
- timer_thread.daemon = True
- timer_thread.start()
- best_value = -inf
- best_moves = []
- current_depth = 1
- try:
- while True:
- if time.time() - start_time > max_time:
- print(f"\nČasový limit vypršel po {max_time} sekundách")
- break
- t0 = time.time()
- value, moves = negamax(board, -inf, inf, 0, current_depth)
- t1 = time.time()
- elapsed = time.time() - start_time
- hrs = int(elapsed // 3600)
- mins = int((elapsed % 3600) // 60)
- secs = int(elapsed % 60)
- print(f"\nHloubka {current_depth} – čas: {hrs:02d}h {mins:02d}m {secs:02d}s (krok: {t1 - t0:.2f}s)")
- if moves:
- print(f"Nejlepší sekvence: {' '.join([move_to_notation(m) for m in moves])}")
- else:
- print("Žádná platná sekvence nalezena.")
- # Pokud bylo nalezeno mate (skóre v řádu MATE minus aktuální ply), ukončíme vyhledávání.
- if abs(value) >= MATE - current_depth:
- print(f"NALEZEN MAT v {len(moves)} tazích!")
- best_value, best_moves = value, moves
- break
- best_value = value
- best_moves = moves
- current_depth += 1
- return best_value, best_moves, time.time() - start_time
- finally:
- running = False
- timer_thread.join(timeout=1.0)
- sys.stdout.write("\n")
- ###############################################################################
- # Iterativní prohlubování s negamax – hledáme mate s “neomezenou” hloubkou
- ###############################################################################
- ###############################################################################
- # Hlavní funkce
- ###############################################################################
- def main():
- # Příklad FEN – např. pozice s černou dámou proti bílému králi,
- # případně upravte dle potřeby:
- # fen = "8/6q1/8/8/8/k1K5/8/8 w - - 0 1" # Bílý na tahu, v šachu od černé dámy
- fen = "6k1/8/3K4/5C2/8/8/8/8 w - - 0 1"
- # Další možné testovací pozice:
- # fen = "8/6q1/8/8/8/k1K5/8/8 b - - 0 1" # Černý na tahu
- # fen = "8/6A1/8/8/8/k1K5/8/8 w - - 0 1" # Bílý král a amazonka proti černému králi
- # fen = "8/8/8/8/8/kq2K3/8/8 w - - 0 1" # Bílý král proti černému králi a dámě
- board = Board(fen)
- print("\n" + "="*60)
- print("ŠACHOVÝ ENDGAME ENGINE S PODPOROU SPECIÁLNÍCH FIGUR")
- print("="*60)
- print("\nPočáteční pozice:")
- print(board.display())
- if is_in_check(board, 'w'):
- print("UPOZORNĚNÍ: Bílý je v šachu!")
- if is_in_check(board, 'b'):
- print("UPOZORNĚNÍ: Černý je v šachu!")
- print("\nVyhledávání mate – čistý negamax bez heuristik...\n")
- value, moves, elapsed = iterative_deepening_negamax(board, max_time=300)
- hrs = int(elapsed // 3600)
- mins = int((elapsed % 3600) // 60)
- secs = int(elapsed % 60)
- print("\n" + "="*60)
- print("NALEZENÁ OPTIMÁLNÍ SEKVENCE")
- print("="*60)
- if not moves:
- print("Nebyla nalezena žádná platná sekvence tahů.")
- else:
- temp_board = board.copy()
- print("Začátek:")
- print(temp_board.display())
- print("-" * 40)
- for i, move in enumerate(moves, 1):
- make_move(move, temp_board)
- print(f"Tah {i}: {move_to_notation(move)}")
- print(temp_board.display())
- print("-" * 40)
- if abs(value) >= MATE - len(moves):
- result = "Mat pro " + ("bílého" if value > 0 else "černého")
- else:
- result = analyze_position(temp_board)
- print(f"Kompletní sekvence: {' '.join([move_to_notation(m) for m in moves])}")
- print(f"Výsledek: {result}")
- print(f"Celkový čas: {hrs:02d}h {mins:02d}m {secs:02d}s")
- if __name__ == '__main__':
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement