diff options
Diffstat (limited to 'src/grossmeister/search.rs')
-rw-r--r-- | src/grossmeister/search.rs | 309 |
1 files changed, 250 insertions, 59 deletions
diff --git a/src/grossmeister/search.rs b/src/grossmeister/search.rs index 372fecc..e8cb696 100644 --- a/src/grossmeister/search.rs +++ b/src/grossmeister/search.rs @@ -1,8 +1,15 @@ use std::f32::INFINITY; -use crate::{moves::{Move, MoveKind}, board::io::IO}; +use crate::{ + board::io::IO, + moves::{Move, MoveKind}, +}; -use super::{Grossmeister, ttable::{NodeType, TranspositionTableItem}, evaluation::Score}; +use super::{ + evaluation::Score, + ttable::{NodeType, TranspositionTableItem}, + Grossmeister, +}; const SCORE_MATE: Score = 20_000.0; @@ -19,38 +26,46 @@ impl Grossmeister { // Mate distance pruning // If a non-zero value (mating score) is returned, branch could be safely pruned pub fn MDP(alpha: &mut Score, beta: &mut Score, root_distance: u8) -> Score { - { // Update bound in case we are winning + { + // Update bound in case we are winning let mating_score = SCORE_MATE - root_distance as Score; if mating_score < *beta { *beta = mating_score; if *alpha >= mating_score { - return mating_score + return mating_score; } } } - { // Update bound in case we are losing + { + // Update bound in case we are losing let mating_score = -SCORE_MATE + root_distance as Score; if mating_score > *alpha { *alpha = mating_score; if *beta <= mating_score { - return mating_score + return mating_score; } } } 0.0 } - pub fn negamax_search(&mut self, mut alpha: Score, mut beta: Score, depth_left: u8, root_distance: u8) -> Score { + pub fn negamax_search( + &mut self, + mut alpha: Score, + mut beta: Score, + depth_left: u8, + root_distance: u8, + ) -> Score { let color = self.board.color(); if self.board.threefold_repetition() { - return 0.0 + return 0.0; } // Mate distance pruning let mating_score = Grossmeister::MDP(&mut alpha, &mut beta, root_distance); if mating_score != 0.0 { - return mating_score + return mating_score; } if let Some(transposition) = self.transposition() { @@ -84,7 +99,7 @@ impl Grossmeister { }; if depth_left == 0 { - return self.quiscence(alpha, beta, root_distance) + return self.quiscence(alpha, beta, root_distance); } let mut full_window_search = true; @@ -106,7 +121,12 @@ impl Grossmeister { } else { // After we have PV-node (that raised alpha) all other nodes will be searched // with zero-window first to confirm this assumption - let score = -self.negamax_search(-(alpha + 0.01), -alpha, depth_left - 1, root_distance + 1); + let score = -self.negamax_search( + -(alpha + 0.01), + -alpha, + depth_left - 1, + root_distance + 1, + ); // In case some of the other nodes raises alpha, then it's true PV node now, // let's research with full window to find its exact value if score > alpha { @@ -115,7 +135,13 @@ impl Grossmeister { score } }; - self.board.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before, hash_before); + self.board.unmake_move( + mov, + captured_piece, + ep_target_before, + castling_rights_before, + hash_before, + ); if score >= beta { transposition.mov = Some(mov); @@ -127,7 +153,7 @@ impl Grossmeister { self.register_killer(mov); } - return beta + return beta; } if score > alpha { alpha = score; @@ -137,7 +163,13 @@ impl Grossmeister { transposition.node_type = NodeType::PV; } } else { - self.board.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before, hash_before); + self.board.unmake_move( + mov, + captured_piece, + ep_target_before, + castling_rights_before, + hash_before, + ); } // Could not finish in time, return what we have so far @@ -148,11 +180,11 @@ impl Grossmeister { if !legal_move_found { if self.board.is_king_in_check(color) { - return -SCORE_MATE + root_distance as Score + return -SCORE_MATE + root_distance as Score; } else { transposition.score = 0.0; self.store_transposition(transposition); - return 0.0 + return 0.0; } } @@ -164,13 +196,13 @@ impl Grossmeister { let color = self.board.color(); if self.board.threefold_repetition() { - return 0.0 + return 0.0; } // Mate distance pruning let mating_score = Grossmeister::MDP(&mut alpha, &mut beta, root_distance); if mating_score != 0.0 { - return mating_score + return mating_score; } if let Some(transposition) = self.transposition() { @@ -219,7 +251,11 @@ impl Grossmeister { let mut legal_move_found = false; self.cleanup_selector(); - while let Some(mov) = if tactical_only { self.next_tactical() } else { self.next_move() } { + while let Some(mov) = if tactical_only { + self.next_tactical() + } else { + self.next_move() + } { let ep_target_before = self.board.ep_target; let castling_rights_before = self.board.castling_rights; let hash_before = self.board.hash; @@ -228,7 +264,13 @@ impl Grossmeister { if !self.board.is_king_in_check(color) { legal_move_found = true; let evaluation = -self.quiscence(-beta, -alpha, root_distance + 1); - self.board.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before, hash_before); + self.board.unmake_move( + mov, + captured_piece, + ep_target_before, + castling_rights_before, + hash_before, + ); if evaluation >= beta { transposition.mov = Some(mov); @@ -249,12 +291,18 @@ impl Grossmeister { transposition.node_type = NodeType::All; } } else { - self.board.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before, hash_before); + self.board.unmake_move( + mov, + captured_piece, + ep_target_before, + castling_rights_before, + hash_before, + ); } } if !legal_move_found && self.board.is_king_in_check(color) { - return -SCORE_MATE + root_distance as Score + return -SCORE_MATE + root_distance as Score; } self.store_transposition(transposition); @@ -282,8 +330,13 @@ impl Grossmeister { let mut subtree_pv = self.collect_pv(depth - 1); pv.append(&mut subtree_pv); - self.board.unmake_move(mov, captured, ep_target_before, castling_rights_before, hash_before); - + self.board.unmake_move( + mov, + captured, + ep_target_before, + castling_rights_before, + hash_before, + ); } } else { println!("No PV node found for hash {}", self.board.hash); @@ -304,11 +357,12 @@ impl Grossmeister { let mut upperbound = INFINITY; while lowerbound < upperbound { - let beta = score + if score == lowerbound { - window_size - } else { - 0.0 - }; + let beta = score + + if score == lowerbound { + window_size + } else { + 0.0 + }; score = self.negamax_search(beta - window_size, beta, depth, 0); @@ -340,12 +394,14 @@ impl Grossmeister { // Print UCI info print!("info depth {} score ", depth); - if score.abs() >= SCORE_MATE - 128f32 { // TODO: VALUE_WIN - MAX_PLY - let mate_distance = (SCORE_MATE - score.abs()) * if score > 0.0 { - 1. - } else { - -1. // -N means we are mated in N plies - }; + if score.abs() >= SCORE_MATE - 128f32 { + // TODO: VALUE_WIN - MAX_PLY + let mate_distance = (SCORE_MATE - score.abs()) + * if score > 0.0 { + 1. + } else { + -1. // -N means we are mated in N plies + }; print!("mate {:.0} ", (mate_distance / 2.0).ceil()); } else { print!("cp {:.0} ", score * 100.0) @@ -430,35 +486,92 @@ impl Grossmeister { result.checks += subtree_result.checks; result.castles += subtree_result.castles; result.en_passants += subtree_result.en_passants; - } - self.board.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before, hash_before); + self.board.unmake_move( + mov, + captured_piece, + ep_target_before, + castling_rights_before, + hash_before, + ); } if print { - println!("Found {} leaf nodes in this subtree (depth {})", result.leaf_nodes, depth); + println!( + "Found {} leaf nodes in this subtree (depth {})", + result.leaf_nodes, depth + ); } result } } - #[cfg(test)] mod tests { - use crate::{board::{Board, io::IO}, square::Square, moves::{Move, MoveKind}, grossmeister::{Grossmeister, search::PerftResult}}; use super::SCORE_MATE; + use crate::{ + board::{io::IO, Board}, + grossmeister::{search::PerftResult, Grossmeister}, + moves::{Move, MoveKind}, + square::Square, + }; #[test] fn perft() { let board = Board::new(); let mut gm = Grossmeister::new(board); - assert_eq!(gm.perft(0, false), PerftResult { leaf_nodes: 1, captures: 0, en_passants: 0, castles: 0 , checks: 0 }); - assert_eq!(gm.perft(1, false), PerftResult { leaf_nodes: 20, captures: 0, en_passants: 0, castles: 0 , checks: 0 }); - assert_eq!(gm.perft(2, false), PerftResult { leaf_nodes: 400, captures: 0, en_passants: 0, castles: 0 , checks: 0 }); - assert_eq!(gm.perft(3, false), PerftResult { leaf_nodes: 8902, captures: 34, en_passants: 0, castles: 0 , checks: 12 }); - assert_eq!(gm.perft(4, false), PerftResult { leaf_nodes: 197281, captures: 1576, en_passants: 0, castles: 0 , checks: 469 }); + assert_eq!( + gm.perft(0, false), + PerftResult { + leaf_nodes: 1, + captures: 0, + en_passants: 0, + castles: 0, + checks: 0 + } + ); + assert_eq!( + gm.perft(1, false), + PerftResult { + leaf_nodes: 20, + captures: 0, + en_passants: 0, + castles: 0, + checks: 0 + } + ); + assert_eq!( + gm.perft(2, false), + PerftResult { + leaf_nodes: 400, + captures: 0, + en_passants: 0, + castles: 0, + checks: 0 + } + ); + assert_eq!( + gm.perft(3, false), + PerftResult { + leaf_nodes: 8902, + captures: 34, + en_passants: 0, + castles: 0, + checks: 12 + } + ); + assert_eq!( + gm.perft(4, false), + PerftResult { + leaf_nodes: 197281, + captures: 1576, + en_passants: 0, + castles: 0, + checks: 469 + } + ); // assert_eq!(board.perft(5, false), PerftResult { leaf_nodes: 4865609, captures: 82719, en_passants: 258, castles: 0, checks: 27351 }); } @@ -468,10 +581,46 @@ mod tests { let board = Board::from_FEN(fen); let mut gm = Grossmeister::new(board); - assert_eq!(gm.perft(0, false), PerftResult { leaf_nodes: 1, captures: 0, en_passants: 0, castles: 0 , checks: 0 }); - assert_eq!(gm.perft(1, false), PerftResult { leaf_nodes: 48, captures: 8, en_passants: 0, castles: 2 , checks: 0 }); - assert_eq!(gm.perft(2, false), PerftResult { leaf_nodes: 2039, captures: 351, en_passants: 1, castles: 91 , checks: 3 }); - assert_eq!(gm.perft(3, false), PerftResult { leaf_nodes: 97862, captures: 17102, en_passants: 45, castles: 3162, checks: 993 }); + assert_eq!( + gm.perft(0, false), + PerftResult { + leaf_nodes: 1, + captures: 0, + en_passants: 0, + castles: 0, + checks: 0 + } + ); + assert_eq!( + gm.perft(1, false), + PerftResult { + leaf_nodes: 48, + captures: 8, + en_passants: 0, + castles: 2, + checks: 0 + } + ); + assert_eq!( + gm.perft(2, false), + PerftResult { + leaf_nodes: 2039, + captures: 351, + en_passants: 1, + castles: 91, + checks: 3 + } + ); + assert_eq!( + gm.perft(3, false), + PerftResult { + leaf_nodes: 97862, + captures: 17102, + en_passants: 45, + castles: 3162, + checks: 993 + } + ); // assert_eq!(board.perft(4, false), PerftResult { leaf_nodes: 4085603, captures: 757163, en_passants: 1929, castles: 128013, checks: 25523 }); } @@ -481,8 +630,26 @@ mod tests { let board = Board::from_FEN(fen); let mut gm = Grossmeister::new(board); - assert_eq!(gm.perft(1, false), PerftResult { leaf_nodes: 14, captures: 1, en_passants: 0, castles: 0 , checks: 2 }); - assert_eq!(gm.perft(2, false), PerftResult { leaf_nodes: 191, captures: 14, en_passants: 0, castles: 0 , checks: 10 }); + assert_eq!( + gm.perft(1, false), + PerftResult { + leaf_nodes: 14, + captures: 1, + en_passants: 0, + castles: 0, + checks: 2 + } + ); + assert_eq!( + gm.perft(2, false), + PerftResult { + leaf_nodes: 191, + captures: 14, + en_passants: 0, + castles: 0, + checks: 10 + } + ); // assert_eq!(board.perft(3, false), PerftResult { leaf_nodes: 2812, captures: 209, en_passants: 2, castles: 0 , checks: 267 }); } @@ -494,11 +661,26 @@ mod tests { let (score, pv) = gm.iterative_deepening(8); assert_eq!(score, SCORE_MATE - 3.0); - assert_eq!(pv, vec![ - Move { source: Square::F3, target: Square::C6, kind: MoveKind::Capture }, - Move { source: Square::B7, target: Square::C6, kind: MoveKind::Capture }, - Move { source: Square::D3, target: Square::A6, kind: MoveKind::Quiet }, - ]); + assert_eq!( + pv, + vec![ + Move { + source: Square::F3, + target: Square::C6, + kind: MoveKind::Capture + }, + Move { + source: Square::B7, + target: Square::C6, + kind: MoveKind::Capture + }, + Move { + source: Square::D3, + target: Square::A6, + kind: MoveKind::Quiet + }, + ] + ); } #[test] @@ -510,21 +692,30 @@ mod tests { let (_, pv) = gm.iterative_deepening(6); assert_eq!( pv[0], - Move { source: Square::F5, target: Square::H4, kind: MoveKind::Quiet }, + Move { + source: Square::F5, + target: Square::H4, + kind: MoveKind::Quiet + }, "You should save this poor knight from danger!" ); } #[test] fn weird_bishop_sac() { - let fen = String::from("r1b1k1nr/p4pp1/1pp1p3/4n2p/1b1qP3/1B1P3N/PPPBQPPP/RN2K2R w KQkq - 7 10"); + let fen = + String::from("r1b1k1nr/p4pp1/1pp1p3/4n2p/1b1qP3/1B1P3N/PPPBQPPP/RN2K2R w KQkq - 7 10"); let board = Board::from_FEN(fen); let mut gm = Grossmeister::new(board); let (_, pv) = gm.iterative_deepening(5); assert_eq!( pv[0], - Move { source: Square::C2, target: Square::C3, kind: MoveKind::Quiet }, + Move { + source: Square::C2, + target: Square::C3, + kind: MoveKind::Quiet + }, "You should fork this bastard!" ); } |