Hand-written alpha-beta search with move ordering, null move pruning, late move reductions, and aspiration windows. Zero dependencies. Fully open source.
SEARCH
Transposition table, killer moves, history heuristic, null move pruning, late move reductions, and aspiration windows.
ZERO_DEPS
Standard library only. No external crates, no bloat — code you can read end to end in an afternoon.
MEASURED
Each phase ships with before/after numbers in CHANGELOG. Performance regressions caught by CI.
The numbers behind FAZ 2.
BEFORE
32,149,509search nodes per move(FAZ 1 baseline)AFTER
0search nodes per move(FAZ 2 complete, −99.36%)Search node count, averaged across startpos d6, kiwipete d5, and endgame d8 from the standard bench suite.
A glimpse of the search loop.
1// search/negamax.rs (excerpt)
2fn negamax(
3 board: &mut Board,
4 depth: i32,
5 ply: i32,
6 mut alpha: i32,
7 beta: i32,
8 ctx: &mut SearchContext,
9 tt: &mut TT,
10 null_allowed: bool,
11) -> i32 {
12 if depth <= 0 {
13 return quiescence(board, alpha, beta, ctx);
14 }
15
16 let in_check = is_in_check(board, board.side_to_move);
17
18 // Null move pruning
19 if null_allowed && depth >= 3 && !in_check
20 && !board.side_to_move_has_only_pawns()
21 {
22 let r = if depth > 6 { 3 } else { 2 };
23 let undo = board.make_null_move();
24 let null_score = -negamax(
25 board, depth - 1 - r, ply + 1,
26 -beta, -beta + 1,
27 ctx, tt, false,
28 );
29 board.unmake_null_move(undo);
30 if null_score >= beta { return beta; }
31 }
32
33 // Move ordering, LMR, aspiration windows...
34 // See full source on GitHub.
35}The codebase is structured for reading. Start with the README, browse the FAZ-by-FAZ changelog, or jump straight to the search loop.
View on GitHub