The leading agent on Chess960, written by a language model.
import time
PIECE_VALUE = {0: 0, 1: 100, 2: 320, 3: 330, 4: 500, 5: 900, 6: 20000}
PST = {
1: [0,0,0,0,0,0,0,0, 50,50,50,50,50,50,50,50, 45,45,55,70,70,55,45,45, 28,28,42,58,58,42,28,28, 18,18,32,48,48,32,18,18, 10,8,14,28,28,14,8,10, 8,12,12,2,2,12,12,8, 0,0,0,0,0,0,0,0],
2: [-45,-35,-25,-25,-25,-25,-35,-45, -35,0,15,25,25,15,0,-35, -25,15,32,45,45,32,15,-25, -20,20,42,58,58,42,20,-20, -20,20,42,58,58,42,20,-20, -25,15,32,45,45,32,15,-25, -35,0,15,25,25,15,0,-35, -45,-35,-25,-25,-25,-25,-35,-45],
3: [-20,-10,-10,-10,-10,-10,-10,-20, -10,12,15,15,15,15,12,-10, -10,15,24,32,32,24,15,-10, -8,16,32,40,40,32,16,-8, -8,16,32,40,40,32,16,-8, -10,15,24,32,32,24,15,-10, -10,12,15,15,15,15,12,-10, -20,-10,-10,-10,-10,-10,-10,-20],
4: [5,5,5,8,8,5,5,5, 10,18,18,18,18,18,18,10, 5,12,15,15,15,15,12,5, 5,12,15,15,15,15,12,5, 5,12,15,15,15,15,12,5, 5,12,15,15,15,15,12,5, 5,10,12,12,12,12,10,5, 0,0,0,0,0,0,0,0],
5: [-20,-10,-10,-5,-5,-10,-10,-20, -10,0,0,0,0,0,0,-10, -10,0,10,10,10,10,0,-10, -5,0,10,15,15,10,0,-5, -5,0,10,15,15,10,0,-5, -10,0,10,10,10,10,0,-10, -10,0,0,0,0,0,0,-10, -20,-10,-10,-5,-5,-10,-10,-20],
6: [-30,-40,-40,-50,-50,-40,-40,-30, -30,-40,-40,-50,-50,-40,-40,-30, -30,-40,-40,-50,-50,-40,-40,-30, -30,-40,-40,-50,-50,-40,-40,-30, -20,-30,-30,-40,-40,-30,-30,-20, -10,-20,-20,-20,-20,-20,-20,-10, 20,20,0,0,0,0,20,20, 20,30,10,0,0,10,30,20]
}
def parse_move_uci(move_str):
"""Parse UCI move string to from_sq, to_sq"""
if len(move_str) < 4:
return None, None
from_col = ord(move_str[0]) - ord('a')
from_row = int(move_str[1]) - 1
to_col = ord(move_str[2]) - ord('a')
to_row = int(move_str[3]) - 1
from_sq = from_row * 8 + from_col
to_sq = to_row * 8 + to_col
return from_sq, to_sq
def reconstruct_board(state):
"""Reconstruct board from state"""
board = [0] * 64
if 'board' in state:
board_str = state['board']
pos = 0
for i, ch in enumerate(board_str):
if ch == ' ':
continue
if ch.isdigit():
pos += int(ch)
else:
piece_map = {'P': 1, 'N': 2, 'B': 3, 'R': 4, 'Q': 5, 'K': 6,
'p': -1, 'n': -2, 'b': -3, 'r': -4, 'q': -5, 'k': -6}
board[pos] = piece_map.get(ch, 0)
pos += 1
return board
def apply_move_to_board(board, from_sq, to_sq):
"""Create new board with move applied"""
new_board = board[:]
new_board[to_sq] = new_board[from_sq]
new_board[from_sq] = 0
return new_board
def evaluate(board, is_white=True):
"""Evaluate board position"""
score = 0
for sq in range(64):
piece = board[sq]
if piece == 0:
continue
ptype = abs(piece)
pval = PIECE_VALUE.get(ptype, 0)
pst_val = PST.get(ptype, [0]*64)[sq]
piece_score = pval + pst_val
if piece > 0: # White piece
score += piece_score
else: # Black piece
score -= piece_score
# King safety: penalize exposed kings
white_king_sq = None
black_king_sq = None
for sq in range(64):
if board[sq] == 6:
white_king_sq = sq
elif board[sq] == -6:
black_king_sq = sq
if white_king_sq is not None:
# Count pieces attacking white king
attacked = 0
for sq in range(64):
if board[sq] < 0: # Black piece
dist = abs(sq - white_king_sq)
if dist <= 2:
attacked += 15
score -= attacked
if black_king_sq is not None:
attacked = 0
for sq in range(64):
if board[sq] > 0: # White piece
dist = abs(sq - black_king_sq)
if dist <= 2:
attacked += 15
score += attacked
return score if is_white else -score
def has_legal_move(board, from_sq, to_sq, is_white):
"""Check if a move is potentially legal (simplified)"""
piece = board[from_sq]
if piece == 0:
return False
is_white_piece = piece > 0
if is_white_piece != is_white:
return False
target = board[to_sq]
if target != 0 and (target > 0) == is_white:
return False
return True
def evaluate_move(board, from_sq, to_sq, is_white):
"""Evaluate a single move"""
new_board = apply_move_to_board(board, from_sq, to_sq)
score = 0
# Material gain
captured = board[to_sq]
if captured != 0:
score += PIECE_VALUE.get(abs(captured), 0) * 10
# Positional change
piece = board[from_sq]
ptype = abs(piece)
old_pst = PST.get(ptype, [0]*64)[from_sq]
new_pst = PST.get(ptype, [0]*64)[to_sq]
score += (new_pst - old_pst) * 2
return score
def order_moves(board, moves, is_white):
"""Order moves for alpha-beta pruning"""
scored = []
for move_str in moves:
from_sq, to_sq = parse_move_uci(move_str)
if from_sq is None:
continue
eval_score = evaluate_move(board, from_sq, to_sq, is_white)
scored.append((eval_score, move_str))
scored.sort(reverse=True, key=lambda x: x[0])
return [m for _, m in scored]
def minimax(board, depth, alpha, beta, is_white, start_time, max_time=0.085):
"""Minimax with alpha-beta pruning"""
# Time check
if time.time() - start_time > max_time:
return evaluate(board, is_white=True)
if depth == 0:
return evaluate(board, is_white=True)
if is_white:
max_eval = -100000
# Generate moves (simplified - would need legal move generation)
for sq in range(64):
if board[sq] > 0: # White piece
for to_sq in range(64):
if has_legal_move(board, sq, to_sq, True):
new_board = apply_move_to_board(board, sq, to_sq)
eval = minimax(new_board, depth - 1, alpha, beta, False, start_time, max_time)
max_eval = max(max_eval, eval)
alpha = max(alpha, eval)
if beta <= alpha:
return max_eval
return max_eval if max_eval != -100000 else evaluate(board, is_white=True)
else:
min_eval = 100000
for sq in range(64):
if board[sq] < 0: # Black piece
for to_sq in range(64):
if has_legal_move(board, sq, to_sq, False):
new_board = apply_move_to_board(board, sq, to_sq)
eval = minimax(new_board, depth - 1, alpha, beta, True, start_time, max_time)
min_eval = min(min_eval, eval)
beta = min(beta, eval)
if beta <= alpha:
return min_eval
return min_eval if min_eval != 100000 else evaluate(board, is_white=True)
def get_move(state):
"""Main move selection function"""
legal_moves = state.get('legal_moves', [])
if not legal_moves:
return None
start_time = time.time()
try:
board = reconstruct_board(state)
# Determine whose turn it is
is_white = state.get('is_white', True)
# Order moves
ordered_moves = order_moves(board, legal_moves, is_white)
best_move = ordered_moves[0] if ordered_moves else legal_moves[0]
best_eval = -100000 if is_white else 100000
# Evaluate each move with limited depth
for move_str in ordered_moves[:20]: # Limit candidates
from_sq, to_sq = parse_move_uci(move_str)
if from_sq is None:
continue
new_board = apply_move_to_board(board, from_sq, to_sq)
# Search depth 2 for speed
eval = minimax(new_board, 2, -100000, 100000, not is_white, start_time, max_time=0.085)
if is_white:
eval = -eval
if eval > best_eval:
best_eval = eval
best_move = move_str
else:
if eval < best_eval:
best_eval = eval
best_move = move_str
# Time check
if time.time() - start_time > 0.085:
break
return best_move
except Exception as e:
return legal_moves[0] if legal_moves else None