|
@@ -26,11 +26,21 @@ pub struct SearchControl<'a> {
|
|
|
pub check: &'a mut dyn FnMut() -> bool,
|
|
|
}
|
|
|
|
|
|
+pub enum SearchResult {
|
|
|
+ Finished(Move, PosValue),
|
|
|
+ Mate(Move, i32),
|
|
|
+ Cancelled(Option<(Move, PosValue)>),
|
|
|
+ Invalid
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
-pub fn search(game: &Game, sc: &mut SearchControl, depth: i32) -> Move {
|
|
|
+/**
|
|
|
+ * searches for moves and returns the best move found plus its value
|
|
|
+ */
|
|
|
+pub fn search(game: &Game, sc: &mut SearchControl, depth: i32) -> SearchResult {
|
|
|
|
|
|
if depth == 0 {
|
|
|
- return Move::default();
|
|
|
+ return SearchResult::Invalid;
|
|
|
}
|
|
|
|
|
|
let mut moves = generate_legal_moves(game, game.turn);
|
|
@@ -49,14 +59,15 @@ pub fn search(game: &Game, sc: &mut SearchControl, depth: i32) -> Move {
|
|
|
|
|
|
info!("moves: {:?}", moves.iter().map(|mv| mv.to_string()).collect::<Vec<String>>());
|
|
|
|
|
|
- let mut alpha = i32::min_value() + 1;
|
|
|
- let mut beta = i32::max_value();
|
|
|
+ let mut alpha: PosValue = MIN_VALUE;
|
|
|
+ let mut beta: PosValue = MAX_VALUE;
|
|
|
|
|
|
// use a slight offset for the alpha value in the root node in order to
|
|
|
// determine possibly multiple good moves
|
|
|
- const ALPHA_OFFSET: i32 = 50;
|
|
|
+ const ALPHA_OFFSET: PosValue = 50 as _;
|
|
|
|
|
|
- let mut valued_moves: Vec<(Move, i32)> = Vec::with_capacity(moves.len());
|
|
|
+ let mut valued_moves: Vec<(Move, PosValue)> = Vec::with_capacity(moves.len());
|
|
|
+ let mut cancelled = false;
|
|
|
for mov in moves {
|
|
|
let new_game = apply_move(game, mov);
|
|
|
//info!("searching {}", mov.to_string());
|
|
@@ -64,10 +75,18 @@ pub fn search(game: &Game, sc: &mut SearchControl, depth: i32) -> Move {
|
|
|
val = -val;
|
|
|
|
|
|
if ret {
|
|
|
- return Move::default();
|
|
|
+ //return (Move::default(), 0);
|
|
|
+ cancelled = true;
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
- info!("searched {} -> {}", mov.to_string(), val);
|
|
|
+ if val >= MAX_VALUE {
|
|
|
+ // mate in 1
|
|
|
+ info!("yay mate!");
|
|
|
+ return SearchResult::Mate(mov, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ //info!("searched {} -> {}", mov.to_string(), val);
|
|
|
|
|
|
if val > alpha {
|
|
|
alpha = val - ALPHA_OFFSET;
|
|
@@ -79,38 +98,42 @@ pub fn search(game: &Game, sc: &mut SearchControl, depth: i32) -> Move {
|
|
|
|
|
|
//info!("movvalues: {:?}", valued_moves.iter().map(|mv| mv.0.to_string() + " - " + &mv.1.to_string()).collect::<Vec<String>>());
|
|
|
|
|
|
- valued_moves.sort_by_key(|mv| mv.1);
|
|
|
+ valued_moves.sort_by_key(|mv| (mv.1 * 1000.0f32) as i64);
|
|
|
|
|
|
//info!("best movvalues: {:?}", valued_moves.iter().map(|mv| mv.0.to_string() + " - " + &mv.1.to_string()).collect::<Vec<String>>());
|
|
|
|
|
|
if valued_moves.len() > 0 {
|
|
|
let min_val = valued_moves[0].1;
|
|
|
- let best_moves = valued_moves.iter().filter(|mv| mv.1 == min_val).map(|(mov, _)| *mov).collect::<Vec<Move>>();
|
|
|
+ let best_moves = valued_moves.iter().filter(|mv| mv.1 == min_val).collect::<Vec<&(Move, PosValue)>>();
|
|
|
|
|
|
//info!("bestmove value {}", -min_val);
|
|
|
- return best_moves[(rng.next_u64() % best_moves.len() as u64) as usize];
|
|
|
+ let chosen_mov = best_moves[(rng.next_u64() % best_moves.len() as u64) as usize];
|
|
|
+ if cancelled {
|
|
|
+ return SearchResult::Cancelled(Some((chosen_mov.0, chosen_mov.1)));
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return SearchResult::Finished(chosen_mov.0, chosen_mov.1);
|
|
|
+ }
|
|
|
}
|
|
|
else {
|
|
|
- return Move::Default{ mov: SimpleMove{ from: 0, to: 0 }, piece_type: PAWN };
|
|
|
+ return SearchResult::Invalid;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-fn negamax(game: &Game, sc: &mut SearchControl, mut alpha: i32, beta: i32, depth: i32) -> (i32, bool) {
|
|
|
+fn negamax(game: &Game, sc: &mut SearchControl, mut alpha: PosValue, beta: PosValue, depth: i32) -> (PosValue, bool) {
|
|
|
if depth == 0 {
|
|
|
return (quiescence_search(game, sc, alpha, beta, 7), false);
|
|
|
let eval = evaluate(game);
|
|
|
- if eval != 0 {
|
|
|
+ if eval != 0.0f32 {
|
|
|
//info!("eval: {}", eval);
|
|
|
}
|
|
|
//return eval;
|
|
|
}
|
|
|
|
|
|
- const MIN_VALUE: i32 = i32::min_value() + 1;
|
|
|
-
|
|
|
sc.nodes += 1;
|
|
|
if sc.nodes % 128 == 0 {
|
|
|
if (sc.check)() {
|
|
|
- return (0, true);
|
|
|
+ return (0 as _, true);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -129,7 +152,7 @@ fn negamax(game: &Game, sc: &mut SearchControl, mut alpha: i32, beta: i32, depth
|
|
|
}
|
|
|
else {
|
|
|
// stalemate
|
|
|
- return (0, false);
|
|
|
+ return (0 as _, false);
|
|
|
}
|
|
|
}
|
|
|
for mov in moves {
|
|
@@ -145,7 +168,7 @@ fn negamax(game: &Game, sc: &mut SearchControl, mut alpha: i32, beta: i32, depth
|
|
|
return (beta, false);
|
|
|
}
|
|
|
if val > alpha {
|
|
|
- alpha = (val as f64 * 0.95) as i32;
|
|
|
+ alpha = (val as f64 * 0.95) as _;
|
|
|
}
|
|
|
//info!(" -> negamaxed {} -> {}", mov.to_string(), depth - 1);
|
|
|
if val > best {
|
|
@@ -155,12 +178,10 @@ fn negamax(game: &Game, sc: &mut SearchControl, mut alpha: i32, beta: i32, depth
|
|
|
}
|
|
|
}
|
|
|
//info!("best alpha {}", best);
|
|
|
- return ((best as f64 * 0.99) as i32, false);
|
|
|
+ return ((best as f64 * 0.99) as _, false);
|
|
|
}
|
|
|
|
|
|
-fn quiescence_search(game: &Game, si: &mut SearchControl, mut alpha: i32, beta: i32, depth: i32) -> i32 {
|
|
|
- const MIN_VALUE: i32 = i32::min_value() + 1;
|
|
|
-
|
|
|
+fn quiescence_search(game: &Game, si: &mut SearchControl, mut alpha: PosValue, beta: PosValue, depth: i32) -> PosValue {
|
|
|
let val = evaluate(game);
|
|
|
si.nodes += 1;
|
|
|
|