Advertisement
max2201111

excellent 3

Apr 14th, 2025
339
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 16.14 KB | Science | 0 0
  1. import time
  2.  
  3. # Chess board representation and FEN parsing
  4. def parse_fen(fen_str):
  5.     """Parse a FEN string into a board representation and additional info."""
  6.     parts = fen_str.split()
  7.     rows = parts[0].split('/')
  8.     side_to_move = parts[1]
  9.    
  10.     # Initialize 8x8 board with empty squares
  11.     board = []
  12.     for rank in rows:
  13.         row = []
  14.         for ch in rank:
  15.             if ch.isdigit():
  16.                 # Add empty squares
  17.                 row.extend(['.'] * int(ch))
  18.             else:
  19.                 # Add piece
  20.                 row.append(ch)
  21.         board.append(row)
  22.    
  23.     # Ensure 8x8 dimensions
  24.     for i in range(len(board)):
  25.         if len(board[i]) < 8:
  26.             board[i].extend(['.'] * (8 - len(board[i])))
  27.     while len(board) < 8:
  28.         board.append(['.'] * 8)
  29.        
  30.     return board, side_to_move
  31.  
  32. def print_board(board):
  33.     """Print the chess board in a readable format."""
  34.     print("  a b c d e f g h")
  35.     for r in range(8):
  36.         print(f"{8-r} {' '.join(board[r])} {8-r}")
  37.     print("  a b c d e f g h")
  38.  
  39. # Move generation for different piece types
  40. def generate_moves(board, side):
  41.     """Generate all possible moves for the given side."""
  42.     moves = []
  43.    
  44.     # Define movement patterns for pieces
  45.     king_dirs = [(-1,-1), (-1,0), (-1,1), (0,-1), (0,1), (1,-1), (1,0), (1,1)]
  46.     rook_dirs = [(-1,0), (1,0), (0,-1), (0,1)]
  47.     bishop_dirs = [(-1,-1), (-1,1), (1,-1), (1,1)]
  48.     knight_moves = [(-2,-1), (-2,1), (-1,-2), (-1,2), (1,-2), (1,2), (2,-1), (2,1)]
  49.    
  50.     # Piece movement definitions
  51.     piece_moves = {
  52.         'p': {'dirs': [], 'sliding': False},  # Pawn (special handling)
  53.         'r': {'dirs': rook_dirs, 'sliding': True},  # Rook
  54.         'n': {'dirs': knight_moves, 'sliding': False},  # Knight
  55.         'b': {'dirs': bishop_dirs, 'sliding': True},  # Bishop
  56.         'q': {'dirs': rook_dirs + bishop_dirs, 'sliding': True},  # Queen
  57.         'k': {'dirs': king_dirs, 'sliding': False},  # King
  58.         'a': {'dirs': rook_dirs + bishop_dirs, 'sliding': True, 'knight_moves': knight_moves},  # Amazonka (queen + knight)
  59.         'c': {'dirs': rook_dirs, 'sliding': True, 'knight_moves': knight_moves},  # Cardinal (rook + knight)
  60.         'e': {'dirs': bishop_dirs, 'sliding': True, 'knight_moves': knight_moves}  # Eve (bishop + knight)
  61.     }
  62.    
  63.     for r in range(8):
  64.         for c in range(8):
  65.             piece = board[r][c]
  66.             if piece == '.':
  67.                 continue
  68.                
  69.             # Skip pieces of the opposite side
  70.             if side == 'w' and piece.islower():
  71.                 continue
  72.             if side == 'b' and piece.isupper():
  73.                 continue
  74.                
  75.             piece_type = piece.lower()
  76.            
  77.             # Handle pawns specially
  78.             if piece_type == 'p':
  79.                 if side == 'w':
  80.                     # Move forward
  81.                     if r > 0 and board[r-1][c] == '.':
  82.                         moves.append((r, c, r-1, c))
  83.                         # Double move from starting position
  84.                         if r == 6 and board[r-2][c] == '.':
  85.                             moves.append((r, c, r-2, c))
  86.                     # Captures
  87.                     for dc in [-1, 1]:
  88.                         if 0 <= c+dc < 8 and r > 0:
  89.                             target = board[r-1][c+dc]
  90.                             if target != '.' and target.islower():
  91.                                 moves.append((r, c, r-1, c+dc))
  92.                 else:  # Black pawn
  93.                     # Move forward
  94.                     if r < 7 and board[r+1][c] == '.':
  95.                         moves.append((r, c, r+1, c))
  96.                         # Double move from starting position
  97.                         if r == 1 and board[r+2][c] == '.':
  98.                             moves.append((r, c, r+2, c))
  99.                     # Captures
  100.                     for dc in [-1, 1]:
  101.                         if 0 <= c+dc < 8 and r < 7:
  102.                             target = board[r+1][c+dc]
  103.                             if target != '.' and target.isupper():
  104.                                 moves.append((r, c, r+1, c+dc))
  105.            
  106.             # Handle other pieces
  107.             elif piece_type in piece_moves:
  108.                 movement = piece_moves[piece_type]
  109.                
  110.                 # Handle standard sliding or non-sliding moves
  111.                 for dr, dc in movement['dirs']:
  112.                     nr, nc = r + dr, c + dc
  113.                     # For non-sliding pieces, check one step
  114.                     if not movement['sliding']:
  115.                         if 0 <= nr < 8 and 0 <= nc < 8:
  116.                             target = board[nr][nc]
  117.                             if target == '.' or (side == 'w' and target.islower()) or (side == 'b' and target.isupper()):
  118.                                 moves.append((r, c, nr, nc))
  119.                    
  120.                     # For sliding pieces, check the ray
  121.                     else:
  122.                         while 0 <= nr < 8 and 0 <= nc < 8:
  123.                             target = board[nr][nc]
  124.                             if target == '.':
  125.                                 moves.append((r, c, nr, nc))
  126.                             elif (side == 'w' and target.islower()) or (side == 'b' and target.isupper()):
  127.                                 moves.append((r, c, nr, nc))
  128.                                 break
  129.                             else:
  130.                                 break
  131.                             nr += dr
  132.                             nc += dc
  133.                
  134.                 # Handle knight moves for special pieces
  135.                 if 'knight_moves' in movement:
  136.                     for dr, dc in movement['knight_moves']:
  137.                         nr, nc = r + dr, c + dc
  138.                         if 0 <= nr < 8 and 0 <= nc < 8:
  139.                             target = board[nr][nc]
  140.                             if target == '.' or (side == 'w' and target.islower()) or (side == 'b' and target.isupper()):
  141.                                 moves.append((r, c, nr, nc))
  142.    
  143.     return moves
  144.  
  145. # Board state evaluation functions
  146. def make_move(board, move):
  147.     """Apply a move to a board and return the new board."""
  148.     fr, fc, tr, tc = move
  149.     new_board = [row[:] for row in board]
  150.     new_board[tr][tc] = new_board[fr][fc]
  151.     new_board[fr][fc] = '.'
  152.     return new_board
  153.  
  154. def is_in_check(board, side):
  155.     """Check if the king of the given side is in check."""
  156.     # Find the king
  157.     king_char = 'K' if side == 'w' else 'k'
  158.     king_pos = None
  159.    
  160.     for r in range(8):
  161.         for c in range(8):
  162.             if board[r][c] == king_char:
  163.                 king_pos = (r, c)
  164.                 break
  165.         if king_pos:
  166.             break
  167.            
  168.     if not king_pos:
  169.         return False  # No king found (shouldn't happen in normal chess)
  170.        
  171.     # Check if king is attacked by any opponent piece
  172.     opponent_side = 'b' if side == 'w' else 'w'
  173.     opponent_moves = generate_moves(board, opponent_side)
  174.    
  175.     for move in opponent_moves:
  176.         _, _, tr, tc = move
  177.         if (tr, tc) == king_pos:
  178.             return True
  179.    
  180.     return False
  181.  
  182. def get_legal_moves(board, side):
  183.     """Get all legal moves (not leaving king in check)."""
  184.     moves = generate_moves(board, side)
  185.     legal_moves = []
  186.    
  187.     for move in moves:
  188.         new_board = make_move(board, move)
  189.         if not is_in_check(new_board, side):
  190.             legal_moves.append(move)
  191.    
  192.     return legal_moves
  193.  
  194. def is_checkmate(board, side):
  195.     """Check if the given side is in checkmate."""
  196.     if not is_in_check(board, side):
  197.         return False
  198.     return len(get_legal_moves(board, side)) == 0
  199.  
  200. def find_checkmate_in_one(board, side_to_move):
  201.     """Find all moves that result in checkmate in one move."""
  202.     checkmate_moves = []
  203.    
  204.     # Generate all moves for the side to move
  205.     moves = generate_moves(board, side_to_move)
  206.    
  207.     for move in moves:
  208.         # Apply the move
  209.         new_board = make_move(board, move)
  210.        
  211.         # Check if opponent is in checkmate
  212.         opponent = 'b' if side_to_move == 'w' else 'w'
  213.         if is_checkmate(new_board, opponent):
  214.             checkmate_moves.append(move)
  215.    
  216.     return checkmate_moves
  217.  
  218. def move_to_algebraic(board, move):
  219.     """Convert a move from coordinates to algebraic notation."""
  220.     fr, fc, tr, tc = move
  221.     piece = board[fr][fc]
  222.    
  223.     from_square = chr(ord('a') + fc) + str(8 - fr)
  224.     to_square = chr(ord('a') + tc) + str(8 - tr)
  225.    
  226.     return f"{piece}{from_square}-{to_square}"
  227.  
  228. # Main function to analyze and find mates
  229. # Minimax search with focus on finding checkmate
  230. def minimax(board, depth, maximizing_player, side, alpha=-float('inf'), beta=float('inf')):
  231.     """
  232.    Minimax search with alpha-beta pruning to find checkmate sequences.
  233.    Returns (score, best_move, mate_found)
  234.    """
  235.     # Constants for scoring
  236.     MATE_SCORE = 10000
  237.    
  238.     # Terminal state check
  239.     legal_moves = get_legal_moves(board, side)
  240.    
  241.     # Check for checkmate or stalemate
  242.     if not legal_moves:
  243.         if is_in_check(board, side):
  244.             return -MATE_SCORE + (100 - depth) if maximizing_player else MATE_SCORE - (100 - depth), None, True
  245.         else:
  246.             return 0, None, False  # Stalemate
  247.    
  248.     # Reached maximum depth without finding checkmate
  249.     if depth == 0:
  250.         return 0, None, False
  251.    
  252.     mate_found = False
  253.     best_move = None
  254.    
  255.     if maximizing_player:
  256.         max_eval = -float('inf')
  257.        
  258.         for move in legal_moves:
  259.             new_board = make_move(board, move)
  260.             next_side = 'b' if side == 'w' else 'w'
  261.            
  262.             eval_score, _, child_mate_found = minimax(new_board, depth - 1, False, next_side, alpha, beta)
  263.            
  264.             # Update best score and move
  265.             if eval_score > max_eval:
  266.                 max_eval = eval_score
  267.                 best_move = move
  268.                 mate_found = child_mate_found
  269.            
  270.             # Alpha-beta pruning
  271.             alpha = max(alpha, eval_score)
  272.             if beta <= alpha:
  273.                 break
  274.        
  275.         return max_eval, best_move, mate_found
  276.     else:
  277.         min_eval = float('inf')
  278.        
  279.         for move in legal_moves:
  280.             new_board = make_move(board, move)
  281.             next_side = 'b' if side == 'w' else 'w'
  282.            
  283.             eval_score, _, child_mate_found = minimax(new_board, depth - 1, True, next_side, alpha, beta)
  284.            
  285.             # Update best score and move
  286.             if eval_score < min_eval:
  287.                 min_eval = eval_score
  288.                 best_move = move
  289.                 mate_found = child_mate_found
  290.            
  291.             # Alpha-beta pruning
  292.             beta = min(beta, eval_score)
  293.             if beta <= alpha:
  294.                 break
  295.        
  296.         return min_eval, best_move, mate_found
  297.  
  298. def find_mate_sequence(board, side, max_depth=10):
  299.     """Find a checkmate sequence up to max_depth."""
  300.     # Iterative deepening to find mate
  301.     for depth in range(1, max_depth + 1):
  302.         print(f"Searching for mate in {depth} moves...")
  303.         start_time = time.time()
  304.        
  305.         # Maximizing for the side to move
  306.         score, best_move, mate_found = minimax(board, depth, True, side)
  307.        
  308.         end_time = time.time()
  309.         elapsed = end_time - start_time
  310.         print(f"Depth {depth} completed in {elapsed:.4f} seconds")
  311.        
  312.         if mate_found:
  313.             print(f"Mate in {depth} found!")
  314.            
  315.             # Generate the mate sequence
  316.             mate_sequence = []
  317.             current_board = [row[:] for row in board]
  318.             current_side = side
  319.             current_depth = depth
  320.            
  321.             while current_depth > 0 and best_move:
  322.                 # Add the move to sequence
  323.                 mate_sequence.append((current_side, best_move))
  324.                
  325.                 # Make the move
  326.                 current_board = make_move(current_board, best_move)
  327.                
  328.                 # Switch sides
  329.                 current_side = 'b' if current_side == 'w' else 'w'
  330.                 current_depth -= 1
  331.                
  332.                 # If this is checkmate, we're done
  333.                 if is_checkmate(current_board, current_side):
  334.                     break
  335.                
  336.                 # Find next best move in the sequence
  337.                 if current_depth > 0:
  338.                     _, best_move, _ = minimax(current_board, current_depth, True, current_side)
  339.            
  340.             return mate_sequence
  341.    
  342.     print(f"No mate found within {max_depth} moves")
  343.     return []
  344.  
  345. def analyze_position(fen, max_depth=10):
  346.     """Analyze a position for checkmate possibilities."""
  347.     print(f"Analyzing position: {fen}")
  348.     board, side_to_move = parse_fen(fen)
  349.    
  350.     print("\nInitial position:")
  351.     print_board(board)
  352.     print(f"Side to move: {'White' if side_to_move == 'w' else 'Black'}")
  353.    
  354.     start_time = time.time()
  355.    
  356.     # First, find checkmate in one move
  357.     checkmate_moves = find_checkmate_in_one(board, side_to_move)
  358.    
  359.     if checkmate_moves:
  360.         end_time = time.time()
  361.         elapsed = end_time - start_time
  362.        
  363.         print(f"\nFound {len(checkmate_moves)} immediate checkmate moves in {elapsed:.4f} seconds:")
  364.         for i, move in enumerate(checkmate_moves, 1):
  365.             algebraic = move_to_algebraic(board, move)
  366.             print(f"{i}. {algebraic}")
  367.            
  368.             # Show the position after the checkmate move
  369.             print(f"\nPosition after {algebraic}:")
  370.             new_board = make_move(board, move)
  371.             print_board(new_board)
  372.            
  373.             # Verify it's checkmate
  374.             opponent = 'b' if side_to_move == 'w' else 'w'
  375.             is_check = is_in_check(new_board, opponent)
  376.             legal_moves = get_legal_moves(new_board, opponent)
  377.             print(f"Opponent is in check: {is_check}")
  378.             print(f"Opponent has {len(legal_moves)} legal moves")
  379.             print(f"Checkmate: {is_check and len(legal_moves) == 0}")
  380.     else:
  381.         print("\nNo checkmate in one move found.")
  382.         print("Proceeding with deeper search...")
  383.        
  384.         # If no immediate checkmate, search for longer mate sequences
  385.         mate_sequence = find_mate_sequence(board, side_to_move, max_depth)
  386.        
  387.         if mate_sequence:
  388.             print("\nCheckmate sequence found:")
  389.            
  390.             current_board = [row[:] for row in board]
  391.            
  392.             for i, (move_side, move) in enumerate(mate_sequence, 1):
  393.                 algebraic = move_to_algebraic(current_board, move)
  394.                 side_name = "White" if move_side == 'w' else "Black"
  395.                 print(f"Move {i}: {side_name} plays {algebraic}")
  396.                
  397.                 # Apply the move
  398.                 current_board = make_move(current_board, move)
  399.                 print_board(current_board)
  400.                
  401.                 # Check if this move results in checkmate
  402.                 opponent = 'b' if move_side == 'w' else 'w'
  403.                 if is_checkmate(current_board, opponent):
  404.                     print(f"Checkmate! {side_name} wins.")
  405.                     break
  406.         else:
  407.             print("No checkmate sequence found within the specified depth.")
  408.            
  409.     end_time = time.time()
  410.     total_time = end_time - start_time
  411.     print(f"\nTotal analysis time: {total_time:.4f} seconds")
  412.  
  413. # Execute the analysis
  414. if __name__ == "__main__":
  415.     # The position with Amazonka where we want to find the one-move checkmate
  416.     fen = "8/8/8/q7/2k1R3/8/8/3K4 b - - 0 1"
  417.    
  418.     # You can test different positions by uncommenting these:
  419.     # fen = "8/1K2k3/r7/8/8/8/8/8 b - - 0 1"  # Rook checkmate
  420.     # fen = "8/1K6/3k1r2/8/8/8/8/8 b - - 0 1"  # Another rook position
  421.     # fen = "8/2k2q2/8/3K1Q2/8/8/8/8 w - - 0 1"  # Queens position
  422.     # fen = "8/k4q2/8/1K3Q2/8/8/8/8 w - - 0 1"  # Another queens position
  423.     # fen = "8/1k6/1q6/8/1Q6/1K6/8/8 b - - 9 5"  # Position with queens
  424.    
  425.     # Set max_depth to control how far the search goes when no immediate mate is found
  426.     analyze_position(fen, max_depth=50)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement