| | """ |
| | Move Ordering for Nexus-Nano |
| | Simplified for speed - MVV-LVA + Killer moves only |
| | """ |
| |
|
| | import chess |
| | from typing import List, Optional, Dict |
| |
|
| |
|
| | class MoveOrderer: |
| | """Fast move ordering with minimal overhead""" |
| | |
| | PIECE_VALUES = { |
| | chess.PAWN: 100, |
| | chess.KNIGHT: 320, |
| | chess.BISHOP: 330, |
| | chess.ROOK: 500, |
| | chess.QUEEN: 900, |
| | chess.KING: 20000 |
| | } |
| | |
| | def __init__(self): |
| | |
| | self.killer_moves: Dict[int, List[Optional[chess.Move]]] = {} |
| | self.max_killers = 2 |
| | self.killer_hits = 0 |
| | |
| | def order_moves( |
| | self, |
| | board: chess.Board, |
| | moves: List[chess.Move], |
| | depth: int, |
| | tt_move: Optional[chess.Move] = None |
| | ) -> List[chess.Move]: |
| | """ |
| | Fast move ordering |
| | Priority: TT move > Captures (MVV-LVA) > Killers > Others |
| | """ |
| | |
| | scored_moves = [] |
| | |
| | for move in moves: |
| | score = 0 |
| | |
| | |
| | if tt_move and move == tt_move: |
| | score += 1000000 |
| | |
| | |
| | elif board.is_capture(move): |
| | score += self._score_capture(board, move) |
| | |
| | |
| | else: |
| | |
| | if self._is_killer(move, depth): |
| | score += 9000 |
| | self.killer_hits += 1 |
| | |
| | |
| | if move.promotion == chess.QUEEN: |
| | score += 8000 |
| | |
| | |
| | board.push(move) |
| | if board.is_check(): |
| | score += 5000 |
| | board.pop() |
| | |
| | |
| | center = [chess.D4, chess.D5, chess.E4, chess.E5] |
| | if move.to_square in center: |
| | score += 30 |
| | |
| | scored_moves.append((score, move)) |
| | |
| | scored_moves.sort(key=lambda x: x[0], reverse=True) |
| | return [move for _, move in scored_moves] |
| | |
| | def _score_capture(self, board: chess.Board, move: chess.Move) -> int: |
| | """MVV-LVA scoring""" |
| | |
| | captured = board.piece_at(move.to_square) |
| | attacker = board.piece_at(move.from_square) |
| | |
| | if not captured or not attacker: |
| | return 0 |
| | |
| | victim = self.PIECE_VALUES.get(captured.piece_type, 0) |
| | attacker_val = self.PIECE_VALUES.get(attacker.piece_type, 1) |
| | |
| | return (victim * 10 - attacker_val) * 100 |
| | |
| | def _is_killer(self, move: chess.Move, depth: int) -> bool: |
| | killers = self.killer_moves.get(depth, []) |
| | return move in killers |
| | |
| | def update_killer_move(self, move: chess.Move, depth: int): |
| | if depth not in self.killer_moves: |
| | self.killer_moves[depth] = [] |
| | |
| | killers = self.killer_moves[depth] |
| | if move not in killers: |
| | killers.insert(0, move) |
| | self.killer_moves[depth] = killers[:self.max_killers] |
| | |
| | def clear(self): |
| | self.killer_moves.clear() |
| | self.killer_hits = 0 |
| | |
| | def get_stats(self) -> Dict: |
| | return { |
| | 'killer_hits': self.killer_hits, |
| | 'killer_depths': len(self.killer_moves) |
| | } |