use std::cmp;
use std::time::Instant;
use crate::coretypes::Color::*;
use crate::coretypes::{Cp, Move, PlyKind, Square};
use crate::eval::{evaluate_abs, terminal_abs};
use crate::movelist::Line;
use crate::search::SearchResult;
use crate::Position;
pub fn alpha_beta(position: Position, ply: PlyKind) -> SearchResult {
debug_assert_ne!(ply, 0);
let mut pv = Line::new();
let mut nodes = 0;
let instant = Instant::now();
let (score, best_move) = alpha_beta_root(position, ply, &mut nodes, Cp::MIN, Cp::MAX);
pv.push(best_move);
SearchResult {
player: position.player,
depth: ply,
best_move,
score,
pv,
nodes,
elapsed: instant.elapsed(),
..Default::default()
}
}
const WHITE: u8 = White as u8;
const BLACK: u8 = Black as u8;
pub(crate) fn alpha_beta_root(
mut position: Position,
ply: PlyKind,
nodes: &mut u64,
mut alpha: Cp,
mut beta: Cp,
) -> (Cp, Move) {
*nodes += 1;
let cache = position.cache();
let legal_moves = position.get_legal_moves();
debug_assert_ne!(ply, 0);
debug_assert!(legal_moves.len() > 0);
let mut best_move = Move::new(Square::D2, Square::D4, None);
if position.player == White {
for legal_move in legal_moves {
let move_info = position.do_move(legal_move);
let move_cp = alpha_beta_impl::<BLACK>(&mut position, ply - 1, nodes, alpha, beta);
position.undo_move(move_info, cache);
if move_cp > alpha {
alpha = move_cp;
best_move = legal_move;
}
}
(alpha, best_move)
} else {
for legal_move in legal_moves {
let move_info = position.do_move(legal_move);
let move_cp = alpha_beta_impl::<WHITE>(&mut position, ply - 1, nodes, alpha, beta);
position.undo_move(move_info, cache);
if move_cp < beta {
beta = move_cp;
best_move = legal_move;
}
}
(beta, best_move)
}
}
fn alpha_beta_impl<const COLOR: u8>(
position: &mut Position,
ply: PlyKind,
nodes: &mut u64,
alpha: Cp,
beta: Cp,
) -> Cp {
*nodes += 1;
let cache = position.cache();
let legal_moves = position.get_legal_moves();
let num_moves = legal_moves.len();
if num_moves == 0 {
return terminal_abs(position);
} else if ply == 0 {
return evaluate_abs(position);
}
if COLOR == White as u8 {
let mut best_cp = Cp::MIN;
let mut alpha = alpha;
for legal_move in legal_moves {
let move_info = position.do_move(legal_move);
let move_cp = alpha_beta_impl::<BLACK>(position, ply - 1, nodes, alpha, beta);
position.undo_move(move_info, cache);
best_cp = cmp::max(best_cp, move_cp);
alpha = cmp::max(alpha, best_cp);
if alpha >= beta {
return best_cp;
}
}
best_cp
} else {
let mut best_cp = Cp::MAX;
let mut beta = beta;
for legal_move in legal_moves {
let move_info = position.do_move(legal_move);
let move_cp = alpha_beta_impl::<WHITE>(position, ply - 1, nodes, alpha, beta);
position.undo_move(move_info, cache);
best_cp = cmp::min(best_cp, move_cp);
beta = cmp::min(beta, best_cp);
if alpha >= beta {
return best_cp;
}
}
best_cp
}
}