blunders_engine/search/
minimax.rs1use std::cmp;
4use std::time::Instant;
5
6use crate::coretypes::Color::*;
7use crate::coretypes::{Cp, Move, PlyKind, Square};
8use crate::eval::{evaluate_abs, terminal_abs};
9use crate::movelist::Line;
10use crate::search::SearchResult;
11use crate::Position;
12
13const WHITE: u8 = White as u8;
14const BLACK: u8 = Black as u8;
15
16pub fn minimax(position: Position, ply: PlyKind) -> SearchResult {
20 assert_ne!(ply, 0);
21
22 let instant = Instant::now();
23 let mut pv = Line::new();
24 let mut nodes = 0;
25
26 let (score, best_move) = minimax_root(position, ply, &mut nodes);
27
28 pv.push(best_move);
29
30 SearchResult {
31 player: position.player,
32 depth: ply,
33 best_move,
34 score,
35 pv,
36 nodes,
37 elapsed: instant.elapsed(),
38 stopped: false,
39 ..Default::default()
40 }
41}
42
43fn minimax_root(mut position: Position, ply: PlyKind, nodes: &mut u64) -> (Cp, Move) {
52 *nodes += 1;
53 let cache = position.cache();
54 let legal_moves = position.get_legal_moves();
55 assert_ne!(ply, 0);
56 assert!(legal_moves.len() > 0);
57
58 let mut best_move = Move::new(Square::D2, Square::D4, None);
59 let mut best_cp;
60
61 if position.player == White {
62 best_cp = Cp::MIN;
63
64 for legal_move in legal_moves {
65 let move_info = position.do_move(legal_move);
66 let move_cp = minimax_impl::<BLACK>(&mut position, ply - 1, nodes);
67 position.undo_move(move_info, cache);
68
69 if move_cp > best_cp {
70 best_cp = move_cp;
71 best_move = legal_move;
72 }
73 }
74 } else {
75 best_cp = Cp::MAX;
76
77 for legal_move in legal_moves {
78 let move_info = position.do_move(legal_move);
79 let move_cp = minimax_impl::<WHITE>(&mut position, ply - 1, nodes);
80 position.undo_move(move_info, cache);
81
82 if move_cp < best_cp {
83 best_cp = move_cp;
84 best_move = legal_move;
85 }
86 }
87 }
88
89 (best_cp, best_move)
90}
91
92fn minimax_impl<const COLOR: u8>(position: &mut Position, ply: PlyKind, nodes: &mut u64) -> Cp {
93 *nodes += 1;
94 let cache = position.cache();
95 let legal_moves = position.get_legal_moves();
96 let num_moves = legal_moves.len();
97
98 if num_moves == 0 {
100 return terminal_abs(position);
101 } else if ply == 0 {
102 return evaluate_abs(position);
103 }
104
105 let mut best_cp;
106
107 if COLOR == White as u8 {
108 best_cp = Cp::MIN;
109
110 for legal_move in legal_moves {
111 let move_info = position.do_move(legal_move);
112 let move_cp = minimax_impl::<BLACK>(position, ply - 1, nodes);
113 position.undo_move(move_info, cache);
114 best_cp = cmp::max(best_cp, move_cp);
115 }
116 } else {
117 best_cp = Cp::MAX;
118
119 for legal_move in legal_moves {
120 let move_info = position.do_move(legal_move);
121 let move_cp = minimax_impl::<WHITE>(position, ply - 1, nodes);
122 position.undo_move(move_info, cache);
123 best_cp = cmp::min(best_cp, move_cp);
124 }
125 }
126
127 best_cp
128}