perf: alpha-beta pruning
This commit is contained in:
parent
39d5ebc2b3
commit
8d5ea3de31
@ -20,9 +20,34 @@ use std::cmp::max;
|
||||
|
||||
// min can't be represented as positive
|
||||
const EVAL_WORST: EvalInt = -(EvalInt::MAX);
|
||||
const EVAL_BEST: EvalInt = EvalInt::MAX;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_eval_int {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_eval_worst_best_symm() {
|
||||
// int limits will bite you if you don't test this
|
||||
assert_eq!(EVAL_WORST, -EVAL_BEST);
|
||||
assert_eq!(-EVAL_WORST, EVAL_BEST);
|
||||
}
|
||||
}
|
||||
|
||||
/// Search the game tree to find the absolute (positive good) eval for the current player.
|
||||
fn minmax(board: &mut Board, depth: usize) -> EvalInt {
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * board: board position to analyze.
|
||||
/// * depth: how deep to analyze the game tree.
|
||||
/// * alpha: best score (absolute, from current player perspective) guaranteed for current player.
|
||||
/// * beta: best score (absolute, from current player perspective) guaranteed for other player.
|
||||
fn minmax(board: &mut Board, depth: usize, alpha: Option<EvalInt>, beta: Option<EvalInt>) -> EvalInt {
|
||||
// default to worst, then gradually improve
|
||||
let mut alpha = alpha.unwrap_or(EVAL_WORST);
|
||||
// our best is their worst
|
||||
let beta = beta.unwrap_or(EVAL_BEST);
|
||||
|
||||
if depth == 0 {
|
||||
let eval = board.eval();
|
||||
match board.turn {
|
||||
@ -46,8 +71,19 @@ fn minmax(board: &mut Board, depth: usize) -> EvalInt {
|
||||
|
||||
for mv in mvs {
|
||||
let anti_mv = mv.make(board);
|
||||
abs_best = max(abs_best, -minmax(board, depth - 1));
|
||||
let abs_score = -minmax(board, depth - 1, Some(-beta), Some(-alpha));
|
||||
abs_best = max(abs_best, abs_score);
|
||||
alpha = max(alpha, abs_best);
|
||||
anti_mv.unmake(board);
|
||||
if alpha >= beta {
|
||||
// alpha-beta prune.
|
||||
//
|
||||
// Beta represents the best eval that the other player can get in sibling branches
|
||||
// (different moves in the parent node). Alpha >= beta means the eval here is _worse_
|
||||
// for the other player, so they will never make the move that leads into this branch.
|
||||
// Therefore, we stop evaluating this branch at all.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
abs_best
|
||||
@ -64,7 +100,7 @@ fn search(board: &mut Board) -> Option<Move> {
|
||||
|
||||
for mv in mvs {
|
||||
let anti_mv = mv.make(board);
|
||||
let abs_eval = -minmax(board, DEPTH);
|
||||
let abs_eval = -minmax(board, DEPTH, None, None);
|
||||
if abs_eval >= best_eval {
|
||||
best_eval = abs_eval;
|
||||
best_mv = Some(mv);
|
||||
|
Loading…
Reference in New Issue
Block a user