File size: 3,388 Bytes
ef8a5fe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
"""
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)
        }