Skip to content

Commit e54c61b

Browse files
committed
feat: Add depth benchmarking tool for engine performance evaluation
1 parent a609b5e commit e54c61b

File tree

1 file changed

+107
-0
lines changed

1 file changed

+107
-0
lines changed

tools/depth_benchmark.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#!/usr/bin/env python
2+
"""
3+
Benchmark engine performance at different depths (1-12).
4+
Shows average time per move for each depth.
5+
"""
6+
7+
import time
8+
import sys
9+
from pathlib import Path
10+
11+
# Add project root to path
12+
sys.path.insert(0, str(Path(__file__).parent.parent))
13+
14+
from draughts.boards.standard import Board
15+
from draughts.engine import AlphaBetaEngine
16+
17+
18+
# Test positions - mix of opening, midgame, and endgame
19+
TEST_POSITIONS = [
20+
None, # Starting position
21+
'[FEN "W:W31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50:B1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20"]',
22+
'[FEN "W:W27,28,32,33,37,38,42,43,47,48:B3,4,8,9,13,14,18,19,23,24"]',
23+
'[FEN "B:W24,28,29,30,33,34,38,39,44,45,49,50:B1,2,6,7,8,12,13,17,18,22,23,27"]',
24+
'[FEN "W:WK10,K20:BK30,K40"]', # King endgame
25+
'[FEN "W:W45,50:B5,10"]', # Simple endgame
26+
]
27+
28+
MOVES_PER_POSITION = 3 # Number of moves to make per position
29+
30+
31+
def benchmark_depth(depth: int) -> dict:
32+
"""Benchmark engine at given depth, return stats."""
33+
engine = AlphaBetaEngine(depth=depth)
34+
35+
total_time = 0.0
36+
total_moves = 0
37+
total_nodes = 0
38+
39+
for fen in TEST_POSITIONS:
40+
if fen:
41+
board = Board.from_fen(fen)
42+
else:
43+
board = Board()
44+
45+
for _ in range(MOVES_PER_POSITION):
46+
if not list(board.legal_moves):
47+
break
48+
49+
engine.nodes = 0
50+
start = time.perf_counter()
51+
move = engine.get_best_move(board)
52+
elapsed = time.perf_counter() - start
53+
54+
total_time += elapsed
55+
total_moves += 1
56+
total_nodes += engine.nodes
57+
58+
board.push(move)
59+
60+
return {
61+
"depth": depth,
62+
"total_moves": total_moves,
63+
"avg_time_ms": (total_time / total_moves * 1000) if total_moves > 0 else 0,
64+
"avg_nodes": total_nodes // total_moves if total_moves > 0 else 0,
65+
"total_time": total_time,
66+
}
67+
68+
69+
def main():
70+
print("=" * 70)
71+
print("Engine Depth Benchmark")
72+
print("=" * 70)
73+
print(f"Testing {len(TEST_POSITIONS)} positions, {MOVES_PER_POSITION} moves each")
74+
print()
75+
print(f"{'Depth':>6} | {'Avg Time':>12} | {'Avg Nodes':>12} | {'Total Time':>12}")
76+
print("-" * 70)
77+
78+
results = []
79+
80+
for depth in range(1, 13):
81+
result = benchmark_depth(depth)
82+
results.append(result)
83+
84+
avg_time = result["avg_time_ms"]
85+
if avg_time < 1000:
86+
time_str = f"{avg_time:.2f} ms"
87+
else:
88+
time_str = f"{avg_time/1000:.2f} s"
89+
90+
total_time = result["total_time"]
91+
if total_time < 60:
92+
total_str = f"{total_time:.2f} s"
93+
else:
94+
total_str = f"{total_time/60:.1f} min"
95+
96+
print(f"{depth:>6} | {time_str:>12} | {result['avg_nodes']:>12,} | {total_str:>12}")
97+
98+
# Stop if taking too long (> 30s per move on average)
99+
if avg_time > 30000:
100+
print(f"\nStopping - depth {depth} takes > 30s per move")
101+
break
102+
103+
print("=" * 70)
104+
105+
106+
if __name__ == "__main__":
107+
main()

0 commit comments

Comments
 (0)