Nexus-Nano-Inference-api / engine /move_ordering.py
Rafs-an09002's picture
Create engine/move_ordering.py
ef8a5fe verified
"""
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):
# Only killer moves (no history table for speed)
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
# TT move
if tt_move and move == tt_move:
score += 1000000
# Captures
elif board.is_capture(move):
score += self._score_capture(board, move)
# Quiet moves
else:
# Killer moves
if self._is_killer(move, depth):
score += 9000
self.killer_hits += 1
# Promotions
if move.promotion == chess.QUEEN:
score += 8000
# Checks
board.push(move)
if board.is_check():
score += 5000
board.pop()
# Center control
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)
}