Gemmy 4 v1
Back to bot7618 bytes · sha256 ce412a2fcbaa1732e18… · public
import chess, time, random
# Precise piece values
P_VALS = {1: 100, 2: 320, 3: 330, 4: 500, 5: 900, 6: 20000}
# Enhanced Piece-Square Tables for positional understanding
PST = {
1: [0, 0, 0, 0, 0, 0, 0, 0,
50, 50, 50, 50, 50, 50, 50, 50,
10, 10, 20, 30, 30, 20, 10, 10,
5, 5, 10, 25, 25, 10, 5, 5,
0, 0, 0, 20, 20, 0, 0, 0,
5, -5,-10, 0, 0,-10, -5, 5,
5, 10, 10,-20,-20, 10, 10, 5,
0, 0, 0, 0, 0, 0, 0, 0],
2: [-50,-40,-30,-30,-30,-30,-40,-50,
-40,-20, 0, 0, 0, 0,-20,-40,
-30, 0, 10, 15, 15, 10, 0,-30,
-30, 5, 15, 20, 20, 15, 5,-30,
-30, 0, 15, 20, 20, 15, 0,-30,
-30, 5, 10, 15, 15, 10, 5,-30,
-40,-20, 0, 5, 5, 0,-20,-40,
-50,-40,-30,-30,-30,-30,-40,-50]
}
def get_opening_move(board):
"""Opening book for White (Italian/Fried Liver) and Black (Caro-Kann / Englund)"""
move_stack = [m.uci() for m in board.move_stack]
# --- WHITE: Italian Game / Fried Liver Attack ---
if board.fullmove_number <= 6 and board.turn == chess.WHITE:
if move_stack == []:
return chess.Move.from_uci("e2e4")
elif move_stack == ["e7e5"]:
return chess.Move.from_uci("g1f3")
elif move_stack == ["e7e5", "b8c6"]:
return chess.Move.from_uci("f1c4")
elif move_stack == ["e7e5", "b8c6", "f1c4", "g8f6"]:
return chess.Move.from_uci("g5f3") # Fried Liver setup transition or Ng5
# --- BLACK vs 1.e4: Caro-Kann Defense ---
if board.turn == chess.BLACK:
if move_stack == ["e2e4"]:
return chess.Move.from_uci("c7c6")
elif move_stack == ["e2e4", "d2d4"]:
return chess.Move.from_uci("d7d5")
# --- BLACK vs 1.d4: Englund Gambit ---
if board.turn == chess.BLACK:
if move_stack == ["d2d4"]:
return chess.Move.from_uci("e7e5")
return None
def evaluate(board):
if board.is_checkmate():
return -99999 if board.turn else 99999
if board.is_stalemate() or board.is_insufficient_material():
return 0
score = 0
our_color = board.turn
opp_color = not our_color
white_mat = sum(P_VALS[p.piece_type] for p in board.piece_map().values() if p.color == chess.WHITE)
black_mat = sum(P_VALS[p.piece_type] for p in board.piece_map().values() if p.color == chess.BLACK)
we_are_ahead = (white_mat > black_mat + 100) if our_color == chess.WHITE else (black_mat > white_mat + 100)
for sq, piece in board.piece_map().items():
val = P_VALS.get(piece.piece_type, 0)
if piece.piece_type in PST:
idx = sq if piece.color == chess.WHITE else chess.square_mirror(sq)
val += PST[piece.piece_type][idx]
# Hanging piece detection
if piece.color != our_color:
if board.is_attacked_by(our_color, sq) and not board.is_attacked_by(opp_color, sq):
val += 1.5 * P_VALS[piece.piece_type]
if piece.color == our_color:
if board.is_attacked_by(opp_color, sq) and not board.is_attacked_by(our_color, sq):
val -= 2.0 * P_VALS[piece.piece_type]
# Conversion strategy when ahead
if we_are_ahead and piece.piece_type == 1:
rank = chess.square_rank(sq)
val += (rank if piece.color == chess.WHITE else (7 - rank)) * 20
if piece.color == chess.WHITE:
score += val
else:
score -= val
return score
def static_exchange_evaluation(board, move):
"""Calculates if a capture is safe or a bad trade (blunder prevention)"""
if not board.is_capture(move):
return 0
target = board.piece_at(move.to_square)
attacker = board.piece_at(move.from_square)
if not target or not attacker:
return 0
# Basic SEE: value gained minus risk of recapture by lower/equal piece
gain = P_VALS[target.piece_type]
loss = P_VALS[attacker.piece_type]
board.push(move)
is_defended = board.is_attacked_by(board.turn, move.to_square)
board.pop()
if is_defended and loss > gain:
return -500 # Bad trade penalty
return gain
def move_priority(board, move):
score = 0
attacker = board.piece_at(move.from_square)
target = board.piece_at(move.to_square)
# 1. Prevent moving king early or walking into danger unnecessarily
if attacker and attacker.piece_type == 6 and not board.is_castling(move) and len(board.piece_map()) > 24:
score -= 500
# 2. Blunder check: moving into attacked square
board.push(move)
attacked_by_opp = board.is_attacked_by(board.turn, move.to_square)
board.pop()
if attacked_by_opp and attacker and attacker.piece_type > 1:
score -= 300
# 3. Capture evaluation using SEE
if target:
see_score = static_exchange_evaluation(board, move)
score += see_score
if see_score > 0:
score += 400
# 4. Escaping threatened pieces
if attacker:
board.push(move)
was_attacked = board.is_attacked_by(not board.turn, move.from_square)
is_still_attacked = board.is_attacked_by(not board.turn, move.to_square)
board.pop()
if was_attacked and not is_still_attacked:
score += 300
return score
def alphabeta(board, depth, alpha, beta, maximizing, end_time):
if time.time() > end_time or depth == 0 or board.is_game_over():
return evaluate(board), None
moves = sorted(board.legal_moves, key=lambda m: move_priority(board, m), reverse=True)
best_move = moves[0] if moves else None
if maximizing:
max_eval = -999999
for move in moves:
board.push(move)
val, _ = alphabeta(board, depth - 1, alpha, beta, False, end_time)
board.pop()
if time.time() > end_time:
break
if val > max_eval:
max_eval = val
best_move = move
alpha = max(alpha, val)
if beta <= alpha:
break
return max_eval, best_move
else:
min_eval = 999999
for move in moves:
board.push(move)
val, _ = alphabeta(board, depth - 1, alpha, beta, True, end_time)
board.pop()
if time.time() > end_time:
break
if val < min_eval:
min_eval = val
best_move = move
beta = min(beta, val)
if beta <= alpha:
break
return min_eval, best_move
def get_move(board, time_left_ms):
# Check opening book first (plays instantly)
book_move = get_opening_move(board)
if book_move and book_move in board.legal_moves:
return book_move
# Balanced time management (~8% of remaining clock, min 0.5s)
time_limit = max((time_left_ms / 1000.0) * 0.08, 0.5)
end_time = time.time() + time_limit
best_move = random.choice(list(board.legal_moves))
for depth in range(1, 10):
if time.time() >= end_time:
break
_, move = alphabeta(board, depth, -999999, 999999, board.turn == chess.WHITE, end_time)
if move and time.time() < end_time:
best_move = move
return best_move