Fix transposition table and add iterative deepening#54
Fix transposition table and add iterative deepening#54
Conversation
- Remove alpha/beta from TT key (was causing near-zero cache hit rate) - Store entry type (EXACT, LOWER_BOUND, UPPER_BOUND) for proper TT probing with bound-based cutoffs - Add iterative deepening: search depths 1..N, populating TT each iteration so the next iteration has better move ordering - Place TT best move first in move ordering (organize_moves) - Update LazySMP to use new TT key format Benchmark (depth 4, 48 positions): - Nodes: 4,760,507 → 2,834,290 (−40.5%) - Time: 210.32s → 123.86s (−41.1%)
|
/run-nps-benchmark |
BenchmarksThe following benchmarks are available for this PR:
Post a comment with the command to trigger a benchmark run. |
Greptile SummaryFixed the transposition table key to use only the board hash (removing alpha/beta), added proper TT entry types (EXACT/LOWER/UPPER), implemented iterative deepening, and integrated TT move-first ordering to improve alpha-beta pruning efficiency.
Issues found:
Confidence Score: 2/5
|
| Filename | Overview |
|---|---|
| moonfish/engines/alpha_beta.py | Fixed TT key format and added iterative deepening, but has unreachable code in TT entry type logic and missing draw detection |
| moonfish/move_ordering.py | Added TT move-first ordering, but doesn't validate TT move legality before insertion (hash collision risk) |
| moonfish/engines/lazy_smp.py | Updated to use new TT key format, but bypasses iterative deepening by calling negamax directly at full depth |
Flowchart
flowchart TD
A[search_move called] --> B[Initialize empty TT cache]
B --> C{Iterative Deepening Loop<br/>d=1 to negamax_depth}
C --> D[negamax depth=d]
D --> E{TT key in cache?}
E -->|Yes| F{TT depth >= current depth?}
F -->|Yes| G{Check TT entry type}
G -->|EXACT| H[Return cached score]
G -->|LOWER| I[Update alpha, check cutoff]
G -->|UPPER| J[Update beta, check cutoff]
E -->|No| K[Search position]
F -->|No| K
I --> K
J --> K
K --> L[Organize moves<br/>TT move first]
L --> M{For each move}
M --> N[Recursive negamax]
N --> O{Score >= beta?}
O -->|Yes| P[Store TT_LOWER, return]
O -->|No| Q{Best score found?}
Q --> R{All moves done?}
R -->|No| M
R -->|Yes| S{Determine entry type}
S -->|score <= orig_alpha| T[Store TT_UPPER]
S -->|else| U[Store TT_EXACT]
T --> V[Return to iterative loop]
U --> V
P --> V
H --> V
V --> W{More depths?}
W -->|Yes| C
W -->|No| X[Return best_move]
Last reviewed commit: aada48a
| # Determine TT entry type based on the score relative to original bounds | ||
| if best_score <= original_alpha: | ||
| tt_type = TT_UPPER # Failed low: score is an upper bound | ||
| elif best_score >= beta: |
There was a problem hiding this comment.
unreachable: if best_score >= beta, would have returned at line 302
| elif best_score >= beta: | |
| elif best_score >= beta: | |
| # This branch is unreachable - beta cutoffs return early at line 302 | |
| tt_type = TT_LOWER # Failed high: score is a lower bound |
| if board.is_checkmate(): | ||
| cache[cache_key] = (-self.config.checkmate_score, None) | ||
| cache[tt_key] = (-self.config.checkmate_score, None, depth, TT_EXACT) | ||
| return (-self.config.checkmate_score, None) | ||
|
|
||
| if board.is_stalemate(): | ||
| cache[cache_key] = (0, None) | ||
| cache[tt_key] = (0, None, depth, TT_EXACT) | ||
| return (0, None) |
There was a problem hiding this comment.
missing draw detection: should check is_repetition(), is_fifty_moves(), and is_insufficient_material() before checkmate/stalemate (like in quiescence_search:114)
| if tt_move is not None: | ||
| result.insert(0, tt_move) |
There was a problem hiding this comment.
TT move legality not checked: hash collision could insert illegal move. Verify tt_move in board.legal_moves before inserting
Additional Comments (1)
|
⚡ NPS Benchmark Results
Per-position breakdown |
Summary
(alpha, beta), which meant the same position at the same depth would get different cache entries depending on the alpha-beta window. This resulted in near-zero cache hit rates. Now uses just the board position hash as the key.organize_moves(), which dramatically improves alpha-beta cutoff rates.Benchmark (depth 4, 48 positions)
Local Stockfish Benchmark
Settings: 20 games, Stockfish skill 3, st=60, concurrency=4.
Note: This PR was benchmarked with different settings (st=60, concurrency=4) than the other PRs (10s/move, concurrency=1), so results are not directly comparable. The regression may be related to the different test conditions rather than a true strength regression.
Use
/run-stockfish-benchmarkfor CI validation with opening book and longer time control.Test plan
pytest tests/ -k "not lazy_smp and not layer_1 and not layer_2")/run-nps-benchmarkfor CI validation/run-stockfish-benchmarkfor strength validation