Rafs-an09002 commited on
Commit
ef8a5fe
·
verified ·
1 Parent(s): fbb8499

Create engine/move_ordering.py

Browse files
Files changed (1) hide show
  1. engine/move_ordering.py +115 -0
engine/move_ordering.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Move Ordering for Nexus-Nano
3
+ Simplified for speed - MVV-LVA + Killer moves only
4
+ """
5
+
6
+ import chess
7
+ from typing import List, Optional, Dict
8
+
9
+
10
+ class MoveOrderer:
11
+ """Fast move ordering with minimal overhead"""
12
+
13
+ PIECE_VALUES = {
14
+ chess.PAWN: 100,
15
+ chess.KNIGHT: 320,
16
+ chess.BISHOP: 330,
17
+ chess.ROOK: 500,
18
+ chess.QUEEN: 900,
19
+ chess.KING: 20000
20
+ }
21
+
22
+ def __init__(self):
23
+ # Only killer moves (no history table for speed)
24
+ self.killer_moves: Dict[int, List[Optional[chess.Move]]] = {}
25
+ self.max_killers = 2
26
+ self.killer_hits = 0
27
+
28
+ def order_moves(
29
+ self,
30
+ board: chess.Board,
31
+ moves: List[chess.Move],
32
+ depth: int,
33
+ tt_move: Optional[chess.Move] = None
34
+ ) -> List[chess.Move]:
35
+ """
36
+ Fast move ordering
37
+ Priority: TT move > Captures (MVV-LVA) > Killers > Others
38
+ """
39
+
40
+ scored_moves = []
41
+
42
+ for move in moves:
43
+ score = 0
44
+
45
+ # TT move
46
+ if tt_move and move == tt_move:
47
+ score += 1000000
48
+
49
+ # Captures
50
+ elif board.is_capture(move):
51
+ score += self._score_capture(board, move)
52
+
53
+ # Quiet moves
54
+ else:
55
+ # Killer moves
56
+ if self._is_killer(move, depth):
57
+ score += 9000
58
+ self.killer_hits += 1
59
+
60
+ # Promotions
61
+ if move.promotion == chess.QUEEN:
62
+ score += 8000
63
+
64
+ # Checks
65
+ board.push(move)
66
+ if board.is_check():
67
+ score += 5000
68
+ board.pop()
69
+
70
+ # Center control
71
+ center = [chess.D4, chess.D5, chess.E4, chess.E5]
72
+ if move.to_square in center:
73
+ score += 30
74
+
75
+ scored_moves.append((score, move))
76
+
77
+ scored_moves.sort(key=lambda x: x[0], reverse=True)
78
+ return [move for _, move in scored_moves]
79
+
80
+ def _score_capture(self, board: chess.Board, move: chess.Move) -> int:
81
+ """MVV-LVA scoring"""
82
+
83
+ captured = board.piece_at(move.to_square)
84
+ attacker = board.piece_at(move.from_square)
85
+
86
+ if not captured or not attacker:
87
+ return 0
88
+
89
+ victim = self.PIECE_VALUES.get(captured.piece_type, 0)
90
+ attacker_val = self.PIECE_VALUES.get(attacker.piece_type, 1)
91
+
92
+ return (victim * 10 - attacker_val) * 100
93
+
94
+ def _is_killer(self, move: chess.Move, depth: int) -> bool:
95
+ killers = self.killer_moves.get(depth, [])
96
+ return move in killers
97
+
98
+ def update_killer_move(self, move: chess.Move, depth: int):
99
+ if depth not in self.killer_moves:
100
+ self.killer_moves[depth] = []
101
+
102
+ killers = self.killer_moves[depth]
103
+ if move not in killers:
104
+ killers.insert(0, move)
105
+ self.killer_moves[depth] = killers[:self.max_killers]
106
+
107
+ def clear(self):
108
+ self.killer_moves.clear()
109
+ self.killer_hits = 0
110
+
111
+ def get_stats(self) -> Dict:
112
+ return {
113
+ 'killer_hits': self.killer_hits,
114
+ 'killer_depths': len(self.killer_moves)
115
+ }