alpha beta pruning

This commit is contained in:
Moritz 2025-11-16 20:18:44 +01:00
parent 96ea7fd2c9
commit eb19606360
6 changed files with 86 additions and 4 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
/target /target
/.idea /.idea
/Cargo.lock /Cargo.lock
progress_tracking/~$progress.xlsx

Binary file not shown.

View file

@ -40,7 +40,7 @@ fn main() {
// Record start time // Record start time
let start_time = Instant::now(); let start_time = Instant::now();
let result = engine.search(4); let result = engine.search(5);
// Calculate duration // Calculate duration
let duration = start_time.elapsed(); let duration = start_time.elapsed();
@ -57,6 +57,8 @@ fn main() {
if result == *bm { if result == *bm {
correct_tests += 1.0; correct_tests += 1.0;
} }
println!("{}%", (total_tests / (sts.len() as f32 / 100.0)) as u8);
} }
println!("{}", correct_tests / (total_tests / 100.0)); println!("{}", correct_tests / (total_tests / 100.0));

View file

@ -1,7 +1,7 @@
// ... (your use statements) // ... (your use statements)
use crate::board::Board; use crate::board::Board;
use crate::r#move::Move; use crate::r#move::Move;
use crate::search::minimax::minimax; use crate::search::alpha_beta::alpha_beta;
pub struct Engine { pub struct Engine {
pub name: String, pub name: String,
@ -33,7 +33,7 @@ impl Engine {
} }
pub fn search(&mut self, depth: u8) -> String { pub fn search(&mut self, depth: u8) -> 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 { if let Some(mv) = opt_move {
mv.to_algebraic() mv.to_algebraic()

78
src/search/alpha_beta.rs Normal file
View file

@ -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<Move>, 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<Move> = 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)
}

View file

@ -1 +1,2 @@
pub mod minimax; pub mod minimax;
pub mod alpha_beta;