Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import time
- import math
- MATE_SCORE = 10000
- INF = 10000000
- # Parse FEN string into board matrix and side to move
- def parse_fen(fen_str):
- parts = fen_str.split()
- rows = parts[0].split('/')
- side_to_move = parts[1]
- board = []
- for rank in rows:
- row = []
- for ch in rank:
- if ch.isdigit():
- row.extend(['.'] * int(ch))
- else:
- row.append(ch)
- board.append(row)
- # Ensure 8 columns per rank
- for i in range(len(board)):
- if len(board[i]) < 8:
- board[i].extend(['.'] * (8 - len(board[i])))
- # Ensure 8 ranks
- while len(board) < 8:
- board.append(['.'] * 8)
- return board, side_to_move
- # Directions for moves of each piece type
- king_dirs = [(-1,-1), (-1,0), (-1,1), (0,-1), (0,1), (1,-1), (1,0), (1,1)]
- rook_dirs = [(-1,0), (1,0), (0,-1), (0,1)]
- bishop_dirs = [(-1,-1), (-1,1), (1,-1), (1,1)]
- knight_moves = [(-2,-1), (-2,1), (-1,-2), (-1,2), (1,-2), (1,2), (2,-1), (2,1)]
- # Czech piece names
- piece_names = {
- 'p': "pěšec",
- 'r': "věž",
- 'n': "jezdec",
- 'b': "střelec",
- 'q': "dáma",
- 'k': "král",
- 'a': "amazonka",
- 'c': "kardinál",
- 'e': "eve"
- }
- # Dictionary defining how each piece moves
- piece_moves = {
- 'p': {'dirs': [], 'sliding': False}, # Pawn (special handling)
- 'r': {'dirs': rook_dirs, 'sliding': True}, # Rook
- 'n': {'dirs': knight_moves, 'sliding': False}, # Knight
- 'b': {'dirs': bishop_dirs, 'sliding': True}, # Bishop
- 'q': {'dirs': rook_dirs + bishop_dirs, 'sliding': True}, # Queen
- 'k': {'dirs': king_dirs, 'sliding': False}, # King
- 'a': {'dirs': rook_dirs + bishop_dirs + knight_moves, # Amazonka (queen + knight)
- 'sliding': True, 'knight_like': True},
- 'c': {'dirs': rook_dirs + knight_moves, # Cardinal (rook + knight)
- 'sliding': True, 'knight_like': True},
- 'e': {'dirs': bishop_dirs + knight_moves, # Eve (bishop + knight)
- 'sliding': True, 'knight_like': True}
- }
- # Generate all moves for the given side
- def generate_moves(board, side):
- moves = []
- for r in range(8):
- for c in range(8):
- piece = board[r][c]
- if piece == '.':
- continue
- # Skip pieces of the opposite side
- if side == 'w' and not piece.isupper():
- continue
- if side == 'b' and not piece.islower():
- continue
- piece_type = piece.lower()
- # Handle pawns specially
- if piece_type == 'p':
- if side == 'w':
- # Move forward
- if r > 0 and board[r-1][c] == '.':
- moves.append((r, c, r-1, c))
- # Double move from starting position
- if r == 6 and board[r-2][c] == '.':
- moves.append((r, c, r-2, c))
- # Captures
- for dc in [-1, 1]:
- if 0 <= c+dc < 8 and r > 0:
- target = board[r-1][c+dc]
- if target != '.' and target.islower():
- moves.append((r, c, r-1, c+dc))
- else: # Black pawn
- # Move forward
- if r < 7 and board[r+1][c] == '.':
- moves.append((r, c, r+1, c))
- # Double move from starting position
- if r == 1 and board[r+2][c] == '.':
- moves.append((r, c, r+2, c))
- # Captures
- for dc in [-1, 1]:
- if 0 <= c+dc < 8 and r < 7:
- target = board[r+1][c+dc]
- if target != '.' and target.isupper():
- moves.append((r, c, r+1, c+dc))
- # Handle all other pieces
- elif piece_type in piece_moves:
- movement = piece_moves[piece_type]
- # Handle knight-like moves separately for special pieces
- if movement.get('knight_like', False):
- for dr, dc in knight_moves:
- nr, nc = r + dr, c + dc
- if 0 <= nr < 8 and 0 <= nc < 8:
- target = board[nr][nc]
- if target == '.' or (side == 'w' and target.islower()) or (side == 'b' and target.isupper()):
- moves.append((r, c, nr, nc))
- # Handle sliding or non-sliding moves
- dirs = movement['dirs']
- for dr, dc in dirs:
- # Skip knight moves which are handled separately above
- if movement.get('knight_like', False) and (abs(dr) == 2 or abs(dc) == 2):
- continue
- nr, nc = r + dr, c + dc
- # For non-sliding pieces, just check one step
- if not movement['sliding']:
- if 0 <= nr < 8 and 0 <= nc < 8:
- target = board[nr][nc]
- if target == '.' or (side == 'w' and target.islower()) or (side == 'b' and target.isupper()):
- moves.append((r, c, nr, nc))
- # For sliding pieces, check the entire ray
- else:
- while 0 <= nr < 8 and 0 <= nc < 8:
- target = board[nr][nc]
- if target == '.':
- moves.append((r, c, nr, nc))
- elif (side == 'w' and target.islower()) or (side == 'b' and target.isupper()):
- moves.append((r, c, nr, nc))
- break
- else:
- break
- nr += dr
- nc += dc
- return moves
- # Check if king is in check
- def is_in_check(board, side):
- # Find the king
- king_char = 'K' if side == 'w' else 'k'
- king_pos = None
- for r in range(8):
- for c in range(8):
- if board[r][c] == king_char:
- king_pos = (r, c)
- break
- if king_pos:
- break
- if not king_pos:
- return False # No king found (shouldn't happen in normal chess)
- # Check if the king is attacked by any opponent piece
- attacking_side = 'b' if side == 'w' else 'w'
- opponent_moves = generate_moves(board, attacking_side)
- for move in opponent_moves:
- _, _, tr, tc = move
- if (tr, tc) == king_pos:
- return True
- return False
- # Get legal moves (not leaving king in check)
- def get_legal_moves(board, side):
- moves = generate_moves(board, side)
- legal_moves = []
- for move in moves:
- fr, fc, tr, tc = move
- # Make the move on a copy of the board
- board_copy = [row[:] for row in board]
- board_copy[tr][tc] = board_copy[fr][fc]
- board_copy[fr][fc] = '.'
- # Check if the move leaves the king in check
- if not is_in_check(board_copy, side):
- legal_moves.append(move)
- return legal_moves
- # Apply a move to a board
- def make_move(board, move):
- fr, fc, tr, tc = move
- new_board = [row[:] for row in board]
- new_board[tr][tc] = new_board[fr][fc]
- new_board[fr][fc] = '.'
- return new_board
- # Print board in ASCII format
- def print_board(board):
- print(" a b c d e f g h")
- for r in range(8):
- print(f"{8-r} {' '.join(board[r])} {8-r}")
- print(" a b c d e f g h")
- # Get move notation
- def get_move_notation(board, move):
- fr, fc, tr, tc = move
- piece = board[fr][fc]
- piece_type = piece.lower()
- from_square = chr(ord('a') + fc) + str(8 - fr)
- to_square = chr(ord('a') + tc) + str(8 - tr)
- if piece_type == 'p':
- return from_square + '-' + to_square
- else:
- piece_symbol = piece_type.upper()
- if piece.islower():
- piece_symbol = piece_symbol.lower()
- return piece_symbol + from_square + '-' + to_square
- # Pure minimax without alpha-beta pruning, focused on finding mate
- def minimax(board, depth, maximizing_player, side):
- # Terminal state check
- legal_moves = get_legal_moves(board, side)
- if not legal_moves:
- if is_in_check(board, side):
- return -MATE_SCORE if maximizing_player else MATE_SCORE, None
- else:
- return 0, None # Stalemate
- if depth == 0:
- # At max depth, if no terminal state is found, return a neutral score
- return 0, None
- if maximizing_player:
- max_eval = -INF
- best_move = None
- for move in legal_moves:
- new_board = make_move(board, move)
- next_side = 'b' if side == 'w' else 'w'
- eval_score, _ = minimax(new_board, depth - 1, False, next_side)
- if eval_score > max_eval:
- max_eval = eval_score
- best_move = move
- return max_eval, best_move
- else:
- min_eval = INF
- best_move = None
- for move in legal_moves:
- new_board = make_move(board, move)
- next_side = 'b' if side == 'w' else 'w'
- eval_score, _ = minimax(new_board, depth - 1, True, next_side)
- if eval_score < min_eval:
- min_eval = eval_score
- best_move = move
- return min_eval, best_move
- # Find complete mate sequence
- def find_mate_sequence(board, side, max_depth=100):
- # Matovou posloupnost hledáme iterativním prohlubováním
- for depth in range(1, max_depth + 1):
- start_time = time.time()
- # Spustíme minimax s aktuální hloubkou
- maximizing = (side == 'w')
- score, best_move = minimax(board, depth, maximizing, side)
- end_time = time.time()
- search_time = end_time - start_time
- # Vypíšeme informace o aktuální hloubce na jednom řádku
- print(f"Hloubka {depth}: Čas = {search_time:.3f}s", end="")
- # Pokud byl nalezen mat
- if abs(score) >= MATE_SCORE - 100:
- print(f", Nalezen mat v hloubce {depth}!")
- # Generujeme posloupnost tahů až do matu
- move_sequence = []
- current_board = [row[:] for row in board]
- current_side = side
- current_depth = depth
- # Budeme postupně generovat nejlepší tahy až do matu
- while current_depth > 0:
- # Najdeme nejlepší tah pro současnou stranu
- maximizing = (current_side == 'w')
- _, best_move = minimax(current_board, current_depth, maximizing, current_side)
- if not best_move:
- break
- # Přidáme tah do sekvence
- move_sequence.append((current_side, best_move))
- # Vytiskneme stav po každém tahu
- fr, fc, tr, tc = best_move
- piece = current_board[fr][fc]
- piece_type = piece.lower()
- piece_name = piece_names.get(piece_type, f"figura {piece_type}")
- move_from = chr(ord('a') + fc) + str(8 - fr)
- move_to = chr(ord('a') + tc) + str(8 - tr)
- print(f"\nTah {len(move_sequence)}: {'Bílý' if current_side == 'w' else 'Černý'} - {piece_name.capitalize()} {move_from}-{move_to}")
- # Aplikujeme tah
- current_board = make_move(current_board, best_move)
- print_board(current_board)
- # Přepneme stranu
- current_side = 'b' if current_side == 'w' else 'w'
- # Když byl tento tah mat, skončíme
- if not get_legal_moves(current_board, current_side) and is_in_check(current_board, current_side):
- print("\nŠach mat!")
- break
- # Zmenšíme hloubku o 1 pro další tah
- current_depth -= 1
- return move_sequence
- else:
- print("") # Nový řádek po výpisu informací o hloubce
- print("Žádný mat nebyl nalezen do zadané hloubky.")
- return []
- # Hlavní funkce pro analýzu pozice z FEN řetězce
- def analyze_position(fen, max_depth=100):
- print("Šachový engine s čistým minimaxem")
- print("=================================")
- print(f"Analyzuji pozici: {fen}")
- board, side = parse_fen(fen)
- print("\nPočáteční pozice:")
- print_board(board)
- print(f"Na tahu je: {'Bílý' if side == 'w' else 'Černý'}")
- # Najdeme matovou posloupnost
- mate_sequence = find_mate_sequence(board, side, max_depth)
- if mate_sequence:
- print("\nKompletní matová posloupnost:")
- for i, (move_side, move) in enumerate(mate_sequence):
- fr, fc, tr, tc = move
- # Zjistit typ figury a její název
- piece = board[fr][fc]
- piece_type = piece.lower()
- piece_name = piece_names.get(piece_type, f"figura {piece_type}")
- # Formát tahu v šachové notaci
- move_from = chr(ord('a') + fc) + str(8 - fr)
- move_to = chr(ord('a') + tc) + str(8 - tr)
- # Výpis tahu
- print(f"{i+1}. {'Bílý' if move_side == 'w' else 'Černý'}: {piece_name.capitalize()} {move_from}-{move_to}")
- # Aplikovat tah na šachovnici pro další iteraci
- board = make_move(board, move)
- else:
- print("Nebyla nalezena žádná matová posloupnost.")
- # Hlavní spuštění
- if __name__ == "__main__":
- # Pozice s věží
- fen = "8/1K2k3/r7/8/8/8/8/8 b - - 0 1"
- fen = "8/1K6/3k1r2/8/8/8/8/8 b - - 6 4"
- fen = "8/AK6/3k1a2/8/8/8/8/8 b - - 0 1"
- # Spustíme analýzu s hledáním matové posloupnosti
- analyze_position(fen, max_depth=20)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement