mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-06 11:03:07 +02:00
feat: fix the queen blunder
This commit is contained in:
parent
1daeee077c
commit
7f02645ca6
@ -1 +1 @@
|
||||
14
|
||||
17
|
||||
@ -179,7 +179,9 @@ class RandomEngine:
|
||||
alpha = score
|
||||
if alpha >= beta:
|
||||
break
|
||||
scored.sort(key=lambda t: t[1], reverse=True)
|
||||
# Prefer higher score; on ties, prefer lower risk
|
||||
risk_map = {m: self._risk_score(board, m) for m, _ in scored}
|
||||
scored.sort(key=lambda t: (t[1], -risk_map[t[0]]), reverse=True)
|
||||
return scored
|
||||
|
||||
def _search_root(self, board: chess.Board, depth: int, start: float) -> Tuple[float, Optional[chess.Move]]:
|
||||
@ -306,6 +308,17 @@ class RandomEngine:
|
||||
s -= 600
|
||||
# Discourage premature queen adventures in the opening
|
||||
if piece.piece_type == chess.QUEEN and early:
|
||||
# Strongly demote greedy corner rook captures like Qxh8/Qxa8/Qxh1/Qxa1
|
||||
if is_cap:
|
||||
victim = board.piece_at(m.to_square)
|
||||
if victim and victim.piece_type == chess.ROOK and m.to_square in {chess.A8, chess.H8, chess.A1, chess.H1}:
|
||||
try:
|
||||
if board.gives_check(m):
|
||||
s -= 1200
|
||||
else:
|
||||
s -= 900
|
||||
except Exception:
|
||||
s -= 900
|
||||
victim = board.piece_at(m.to_square)
|
||||
# Penalize queen pawn-grabs on edge pawns (a2/b2/g2/h2 or a7/b7/g7/h7)
|
||||
poison_targets_white = {chess.A7, chess.B7, chess.G7, chess.H7}
|
||||
@ -706,4 +719,67 @@ class RandomEngine:
|
||||
# Extra risk for early bishop sac on f2/f7
|
||||
if self._is_early_game(board) and self._is_bishop_sac_on_f2f7(board, move):
|
||||
risk += 600
|
||||
# Queen trap risk (e.g., greedy corner rook grabs like Qxh8?)
|
||||
try:
|
||||
risk += self._queen_trap_risk(board, move)
|
||||
except Exception:
|
||||
pass
|
||||
return risk
|
||||
|
||||
def _queen_trap_risk(self, board: chess.Board, move: chess.Move) -> int:
|
||||
"""Estimate risk of the mover's queen becoming trapped or heavily attacked after this move.
|
||||
|
||||
Adds a notable penalty for queen captures on corner rooks when defenders outweigh attackers
|
||||
or when the queen has very limited safe mobility from the destination square.
|
||||
"""
|
||||
pc = board.piece_at(move.from_square)
|
||||
if not pc or pc.piece_type != chess.QUEEN:
|
||||
return 0
|
||||
|
||||
# Pre-move info about target square
|
||||
victim_pre = board.piece_at(move.to_square)
|
||||
is_corner = move.to_square in {chess.A8, chess.H8, chess.A1, chess.H1}
|
||||
is_corner_rook_capture = bool(victim_pre and victim_pre.piece_type == chess.ROOK and is_corner)
|
||||
|
||||
# Simulate the move
|
||||
board.push(move)
|
||||
try:
|
||||
my_color = not board.turn # after push, side to move flipped; queen belongs to the previous mover
|
||||
qsq = move.to_square
|
||||
risk = 0
|
||||
|
||||
# If queen moved to a corner, that's typically risky (limited squares)
|
||||
if qsq in {chess.A8, chess.H8, chess.A1, chess.H1}:
|
||||
risk += 120
|
||||
|
||||
# Count attackers/defenders on the queen's square
|
||||
attackers = len(board.attackers(not my_color, qsq))
|
||||
defenders = len(board.attackers(my_color, qsq))
|
||||
if attackers >= max(1, defenders):
|
||||
# Heavily attacked or under-defended queen on destination
|
||||
risk += 350
|
||||
|
||||
# Estimate queen mobility: how many immediate moves are not landing on attacked squares
|
||||
safe_exits = 0
|
||||
for m in board.legal_moves:
|
||||
if m.from_square == qsq:
|
||||
# Quick static safety: avoid landing on currently attacked squares
|
||||
if not board.is_attacked_by(not my_color, m.to_square):
|
||||
safe_exits += 1
|
||||
if safe_exits >= 4:
|
||||
break
|
||||
if safe_exits <= 1:
|
||||
risk += 450
|
||||
elif safe_exits <= 3:
|
||||
risk += 200
|
||||
# Extra penalty if this was a corner rook capture and exits are limited or square is contested
|
||||
if is_corner_rook_capture:
|
||||
base = 300
|
||||
# If heavily attacked or exits are poor, escalate
|
||||
if attackers >= max(1, defenders) or safe_exits <= 2:
|
||||
base += 600
|
||||
# Taking with check is often tempting; still risky. Keep the penalty significant.
|
||||
risk += base
|
||||
finally:
|
||||
board.pop()
|
||||
return risk
|
||||
|
||||
@ -138,8 +138,11 @@ def run_bot(log_level: str = "INFO", decline_correspondence: bool = False) -> No
|
||||
logging.info(
|
||||
f"Game {game_id}: turn={'white' if is_white_turn else 'black'}, my_turn={my_turn}"
|
||||
)
|
||||
# Only move on 'gameState' events; skip making a move on initial 'gameFull'
|
||||
allow_move = (et == "gameState")
|
||||
# Move policy:
|
||||
# - Always move on 'gameState' (authoritative)
|
||||
# - Also allow moving on the initial 'gameFull' when there are zero moves and it's our turn.
|
||||
# This avoids stalling at game start when Lichess doesn't immediately send a 'gameState' for 0 moves.
|
||||
allow_move = (et == "gameState") or (et == "gameFull" and new_len == 0)
|
||||
if my_turn and allow_move:
|
||||
# Compute a per-move time budget (seconds) based on remaining time
|
||||
# Heuristic: use min( max_time_sec, max(0.05, 0.6 * my_time_left/remaining_moves + inc) )
|
||||
@ -168,10 +171,8 @@ def run_bot(log_level: str = "INFO", decline_correspondence: bool = False) -> No
|
||||
api.make_move(game_id, move)
|
||||
except Exception as e:
|
||||
logging.warning(f"Game {game_id}: move {move.uci()} failed: {e}")
|
||||
# Mark this position as handled only on authoritative gameState,
|
||||
# or after we've actually attempted a move. This prevents missing
|
||||
# our very first move as White when gameFull (len=0) is followed by
|
||||
# gameState (len=0).
|
||||
# Mark this position as handled on authoritative gameState, or after we've
|
||||
# actually attempted a move (including the first move on gameFull len=0).
|
||||
if et == "gameState" or (my_turn and allow_move):
|
||||
last_handled_len = new_len
|
||||
if status in {"mate", "resign", "stalemate", "timeout", "draw"}:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user