From eb196063607f572b3cb316c8230f7675fbe96a52 Mon Sep 17 00:00:00 2001 From: Moritz Date: Sun, 16 Nov 2025 20:18:44 +0100 Subject: [PATCH] alpha beta pruning --- .gitignore | 1 + progress_tracking/progress.xlsx | Bin 0 -> 5413 bytes src/bin/suite.rs | 4 +- src/engine.rs | 4 +- src/search/alpha_beta.rs | 78 ++++++++++++++++++++++++++++++++ src/search/mod.rs | 3 +- 6 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 progress_tracking/progress.xlsx create mode 100644 src/search/alpha_beta.rs diff --git a/.gitignore b/.gitignore index 181f3ef..c487998 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target /.idea /Cargo.lock +progress_tracking/~$progress.xlsx \ No newline at end of file diff --git a/progress_tracking/progress.xlsx b/progress_tracking/progress.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..d5fadec9e97f97b78a50ba45f95b6655ec4278e8 GIT binary patch literal 5413 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 String { - let (opt_move, _score) = minimax(&mut self.board, depth, 0); + let (opt_move, _score) = alpha_beta(&mut self.board, depth, 0, -i32::MAX, i32::MAX); if let Some(mv) = opt_move { mv.to_algebraic() diff --git a/src/search/alpha_beta.rs b/src/search/alpha_beta.rs new file mode 100644 index 0000000..a284833 --- /dev/null +++ b/src/search/alpha_beta.rs @@ -0,0 +1,78 @@ +use crate::board::{Board, Color}; // <-- Assuming you have a Color enum (e.g., Color::White, Color::Black) +use crate::eval::basic::evaluate_board; +use crate::movegen::generate_pseudo_legal_moves; +use crate::movegen::legal_check::is_other_king_attacked; +use crate::r#move::{Move, MoveList}; + +// A score high enough to be > any material eval, but low enough to not overflow when adding ply +const MATE_SCORE: i32 = 1_000_000; + +fn evaluate_board_relative(board: &Board) -> i32 { + let static_eval = evaluate_board(board); + match board.side_to_move { + Color::White => static_eval, + Color::Black => -static_eval, + } +} + +pub fn alpha_beta( + board: &mut Board, + depth: u8, + ply: u8, + mut alpha: i32, + beta: i32, +) -> (Option, i32) { + if depth == 0 { + return (None, evaluate_board_relative(board)); + } + + let mut list = MoveList::new(); + generate_pseudo_legal_moves(board, &mut list); + let mut best_move: Option = None; + let mut best_score: i32 = -i32::MAX; // This is our local "worst case" + let mut legal_moves_found = false; + + for mv in list.iter() { + let undo_mv = board.make_move(*mv); + let is_illegal = is_other_king_attacked(board); + if is_illegal { + board.undo_move(undo_mv); + continue; + } + legal_moves_found = true; + + // Recursive call with negated and swapped alpha/beta + let (_, score) = alpha_beta(board, depth - 1, ply + 1, -beta, -alpha); + let current_score = -score; + + if current_score > best_score { + best_score = current_score; + best_move = Some(*mv); + } + + board.undo_move(undo_mv); + + // Alpha-Beta Pruning logic + if best_score > alpha { + alpha = best_score; + } + + if alpha >= beta { + break; // Beta cutoff (Pruning) + } + } + + if !legal_moves_found { + if is_other_king_attacked(board) { + // Checkmate + // The score is *less* negative the *longer* it takes to be mated (higher ply) + // This translates to a *higher* score for the winner for a *faster* mate + return (None, -MATE_SCORE + (ply as i32)); + } else { + // Stalemate + return (None, 0); + } + } + + (best_move, best_score) +} \ No newline at end of file diff --git a/src/search/mod.rs b/src/search/mod.rs index 4ccb1d9..30df11f 100644 --- a/src/search/mod.rs +++ b/src/search/mod.rs @@ -1 +1,2 @@ -pub mod minimax; \ No newline at end of file +pub mod minimax; +pub mod alpha_beta; \ No newline at end of file