search.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. use bitboard::*;
  2. use movegen::*;
  3. use game::Game;
  4. use evaluate::*;
  5. use log::info;
  6. use rand::prelude::*;
  7. use hash::*;
  8. pub struct SearchControl<'a> {
  9. /// node counter
  10. pub nodes: usize,
  11. /// function to check if the search should be exited
  12. pub check: &'a mut dyn FnMut() -> bool,
  13. pub move_history: &'a RepetitionTable,
  14. /// depth the search was started at
  15. pub initial_depth: i32,
  16. }
  17. pub enum SearchResult {
  18. Finished(Move, PosValue),
  19. Cancelled(Option<(Move, PosValue)>),
  20. Invalid
  21. }
  22. /**
  23. * searches for moves and returns the best move found plus its value
  24. */
  25. pub fn search(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha: PosValue, beta: PosValue, depth: i32) -> SearchResult {
  26. if depth == 0 {
  27. return SearchResult::Invalid;
  28. }
  29. let mut moves = generate_legal_moves(game, game.turn);
  30. let mut rng = rand::thread_rng();
  31. sort_moves(game, hash, &mut moves);
  32. info!("moves: {:?}", moves.iter().map(|mv| mv.to_string()).collect::<Vec<String>>());
  33. //let mut alpha: PosValue = MIN_VALUE;
  34. //let mut beta: PosValue = MAX_VALUE;
  35. // use a slight offset for the alpha value in the root node in order to
  36. // determine possibly multiple good moves
  37. const ALPHA_OFFSET: PosValue = 50 as _;
  38. let mut valued_moves: Vec<(Move, PosValue)> = Vec::with_capacity(moves.len());
  39. let mut cancelled = false;
  40. for mov in moves {
  41. let undo = game.apply(mov);
  42. //assert_eq!(new_game, *game, );
  43. //info!("searching {}", mov.to_string());
  44. let (mut val, ret) = negamax(game, sc, hash, -beta, -alpha, depth - 1);
  45. val = -val;
  46. if let Some(turns) = is_mate_in_p1(val) {
  47. if turns < 0 {
  48. val = mate_in_p1(turns - 1);
  49. }
  50. if turns > 0 {
  51. val = mate_in_p1(turns + 1);
  52. }
  53. if turns == 0 {
  54. if val < 0 {
  55. val = mate_in_p1(-1);
  56. }
  57. else {
  58. val = mate_in_p1(1);
  59. }
  60. }
  61. }
  62. if ret {
  63. //return (Move::default(), 0);
  64. cancelled = true;
  65. game.undo_move(undo);
  66. break;
  67. }
  68. if val >= mate() {
  69. // mate in 1 --- can't get better than that
  70. info!("yay mate!");
  71. game.undo_move(undo);
  72. return SearchResult::Finished(mov, val);
  73. }
  74. //info!("searched {} -> {}", mov.to_string(), val);
  75. if val > alpha {
  76. alpha = val - ALPHA_OFFSET;
  77. //info!("updated alpha to {}", alpha);
  78. }
  79. valued_moves.push((mov, -val));
  80. game.undo_move(undo);
  81. }
  82. //info!("movvalues: {:?}", valued_moves.iter().map(|mv| mv.0.to_string() + " - " + &mv.1.to_string()).collect::<Vec<String>>());
  83. valued_moves.sort_by_key(|mv| mv.1);
  84. //info!("best movvalues: {:?}", valued_moves.iter().map(|mv| mv.0.to_string() + " - " + &mv.1.to_string()).collect::<Vec<String>>());
  85. if valued_moves.len() > 0 {
  86. let min_val = valued_moves[0].1;
  87. let best_moves = valued_moves.iter().filter(|mv| mv.1 == min_val).collect::<Vec<&(Move, PosValue)>>();
  88. //info!("bestmove value {}", -min_val);
  89. let chosen_mov = best_moves[(rng.next_u64() % best_moves.len() as u64) as usize];
  90. if cancelled {
  91. return SearchResult::Cancelled(Some((chosen_mov.0, -chosen_mov.1)));
  92. }
  93. else {
  94. hash.cache(game, CacheEntry::new_value(depth, chosen_mov.1));
  95. return SearchResult::Finished(chosen_mov.0, -chosen_mov.1);
  96. }
  97. }
  98. else {
  99. return SearchResult::Invalid;
  100. }
  101. }
  102. fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha: PosValue, beta: PosValue, depth: i32) -> (PosValue, bool) {
  103. if let Some(e) = hash.lookup(game) {
  104. if e.depth >= depth {
  105. //println!("TABLE HIT!");
  106. match e.entry_type {
  107. EntryType::Value => { return (e.value, false); },
  108. //EntryType::LowerBound => { if e.value > alpha { return (e.value, false) } },
  109. EntryType::LowerBound => { if e.value > alpha { alpha = e.value; } },
  110. //EntryType::UpperBound => { if e.value >= beta { return (beta, false); } },
  111. EntryType::UpperBound => { if e.value >= beta { return (beta, false); } },
  112. }
  113. }
  114. }
  115. if depth == 0 {
  116. return quiescence_search(game, sc, hash, alpha, beta, 9);
  117. }
  118. sc.nodes += 1;
  119. if sc.nodes % 1024 == 0 {
  120. if (sc.check)() {
  121. return (0 as _, true);
  122. }
  123. }
  124. if game.get_piece(KING, game.turn) == 0 { return (-mate(), false); }
  125. let mut moves = generate_legal_moves(game, game.turn);
  126. let check = is_check(game, game.turn);
  127. if moves.len() == 0 {
  128. if check {
  129. // mate
  130. return (-mate(), false);
  131. }
  132. else {
  133. // stalemate
  134. return (0 as _, false);
  135. }
  136. }
  137. // Nullmove
  138. if !check && depth >= 4 && game_lateness(game) < 80 {
  139. let nmov = Move::Nullmove;
  140. let undo = game.apply(nmov);
  141. let (mut val, ret) = negamax(game, sc, hash, -beta, -alpha, depth / 2 - 1);
  142. val = -val;
  143. val = increase_mate_in(val);
  144. if ret {
  145. game.undo_move(undo);
  146. return (alpha, ret)
  147. }
  148. if val >= beta {
  149. game.undo_move(undo);
  150. //hash.cache(game, CacheEntry::new_beta(depth, beta));
  151. //println!("but ret {}: {}", depth, beta);
  152. return (beta, false);
  153. }
  154. game.undo_move(undo);
  155. }
  156. sort_moves(game, hash, &mut moves);
  157. let mut alpha_is_exact = false;
  158. for mov in moves {
  159. let undo = game.apply(mov);
  160. let (mut val, ret) = negamax(game, sc, hash, -beta, -alpha, depth - 1);
  161. val = -val;
  162. val = increase_mate_in(val);
  163. if ret {
  164. game.undo_move(undo);
  165. return (alpha, ret)
  166. }
  167. if val >= beta {
  168. game.undo_move(undo);
  169. hash.cache(game, CacheEntry::new_lower(depth, val));
  170. //println!("but ret {}: {}", depth, beta);
  171. return (beta, false);
  172. }
  173. if val > alpha {
  174. alpha = val;//(val as f64 * 0.95) as _;
  175. alpha_is_exact = true;
  176. }
  177. game.undo_move(undo);
  178. }
  179. if alpha_is_exact {
  180. hash.cache(game, CacheEntry::new_value(depth, alpha));
  181. }
  182. else {
  183. hash.cache(game, CacheEntry::new_upper(depth, alpha));
  184. }
  185. //info!("best alpha {}", best);
  186. return (alpha, false);
  187. }
  188. fn quiescence_search(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha: PosValue, beta: PosValue, depth: i32) -> (PosValue, bool) {
  189. let val = evaluate(game);
  190. sc.nodes += 1;
  191. if sc.nodes % 1024 == 0 {
  192. if (sc.check)() {
  193. return (0 as _, true);
  194. }
  195. }
  196. if val >= beta {
  197. return (beta, false);
  198. }
  199. if val > alpha {
  200. alpha = val;
  201. }
  202. if depth <= 0 {
  203. return (alpha, false);
  204. }
  205. if game.get_piece(KING, game.turn) == 0 { return (-mate(), false); }
  206. let moves = generate_attacking_moves(game, game.turn);
  207. for mov in moves {
  208. let undo = game.apply(mov);
  209. let (mut val, ret) = quiescence_search(game, sc, hash, -beta, -alpha, depth - 1);
  210. val = -val;
  211. if val >= beta {
  212. game.undo_move(undo);
  213. return (beta, false);
  214. }
  215. if val > alpha {
  216. alpha = val;
  217. }
  218. game.undo_move(undo);
  219. }
  220. return (alpha, false);
  221. }
  222. pub fn apply_move(game: &Game, mov: Move) -> Game {
  223. let mut new_game = game.clone();
  224. let side = game.turn;
  225. let friends = game.get_all_side(side);
  226. let others = game.get_all_side(!side);
  227. match mov {
  228. Move::Default{ mov, piece_type: pt, captured: _ } => {
  229. if pt == KING {
  230. // invalidate castling rights
  231. new_game.castling_rights[if game.turn == BLACK { 2 } else { 0 }] = false;
  232. new_game.castling_rights[if game.turn == BLACK { 3 } else { 1 }] = false;
  233. }
  234. // invalidate castling rights
  235. if mov.from == square_from_indices(7, 0) || mov.to == square_from_indices(7, 0) {
  236. new_game.castling_rights[0] = false;
  237. }
  238. if mov.from == square_from_indices(0, 0) || mov.to == square_from_indices(0, 0) {
  239. new_game.castling_rights[1] = false;
  240. }
  241. if mov.from == square_from_indices(7, 7) || mov.to == square_from_indices(7, 7) {
  242. new_game.castling_rights[2] = false;
  243. }
  244. if mov.from == square_from_indices(0, 7) || mov.to == square_from_indices(0, 7) {
  245. new_game.castling_rights[3] = false;
  246. }
  247. // if it is a capture
  248. if from_square(mov.to) & others != 0 {
  249. let (other_piece, other_side) = game.get_square(mov.to);
  250. if let Some((ref zt, ref _zv)) = game.zobrist {
  251. let hup = zt.piece_hash(other_piece, other_side, mov.to);
  252. new_game.update_zobrist(hup);
  253. }
  254. new_game.apply_mask(!from_square(mov.to));
  255. }
  256. let moved_piece = mov.apply_to(new_game.get_piece(pt, side));
  257. new_game.set_piece(pt, side, moved_piece);
  258. if let Some((ref zt, ref _zv)) = game.zobrist {
  259. let hup = zt.piece_hash(pt, side, mov.from) ^ zt.piece_hash(pt, side, mov.to);
  260. new_game.update_zobrist(hup);
  261. }
  262. },
  263. Move::Castling { side, left } => {
  264. // invalidate future castling rights
  265. new_game.castling_rights[if game.turn == BLACK { 2 } else { 0 }] = false;
  266. new_game.castling_rights[if game.turn == BLACK { 3 } else { 1 }] = false;
  267. let king = game.get_piece(KING, side);
  268. let rook = if left {
  269. game.get_piece(ROOK, side) & (king << 4)
  270. }
  271. else {
  272. game.get_piece(ROOK, side) & (king >> 3)
  273. };
  274. let new_king = if left { king << 2 } else { king >> 2 };
  275. let new_rook = if left { rook >> 3 } else { rook << 2 };
  276. new_game.set_piece(KING, side, new_king);
  277. *new_game.get_piece_mut(ROOK, side) |= new_rook;
  278. *new_game.get_piece_mut(ROOK, side) &= !rook;
  279. },
  280. Move::EnPassant { mov, beaten } => {
  281. let pawns = game.pawns(side);
  282. if let Some(ep) = game.en_passant {
  283. *new_game.get_piece_mut(PAWN, side) &= !from_square(mov.from);
  284. *new_game.get_piece_mut(PAWN, side) |= from_square(mov.to);
  285. *new_game.get_piece_mut(PAWN, !side) &= !from_square(beaten);
  286. }
  287. },
  288. Move::Promotion { mov, promote_to, captured } => {
  289. //if from_square(mov.to) & others != 0 {
  290. if let Some(pt) = captured {
  291. *new_game.get_piece_mut(pt, !side) &= !from_square(mov.to);
  292. }
  293. new_game.apply_mask(!from_square(mov.from));
  294. match promote_to {
  295. QUEEN => { *new_game.queens_mut(side) |= from_square(mov.to); }
  296. ROOK => { *new_game.rooks_mut(side) |= from_square(mov.to); }
  297. BISHOP => { *new_game.bishops_mut(side) |= from_square(mov.to); }
  298. KNIGHT => { *new_game.knights_mut(side) |= from_square(mov.to); }
  299. _ => {
  300. info!("internal error");
  301. }
  302. }
  303. },
  304. Move::Nullmove => {}
  305. }
  306. new_game.turn = !new_game.turn;
  307. if let Some((ref zt, ref zv)) = new_game.zobrist {
  308. //info!("applied {}, zobrist now {}", mov.to_string(), *zv);
  309. }
  310. return new_game;
  311. }
  312. pub fn perft(game: &mut Game, sc: &mut SearchControl, depth: i32) -> bool {
  313. let moves = generate_legal_moves(game, game.turn);
  314. if depth <= 1 {
  315. sc.nodes += moves.len();
  316. if sc.nodes % 1024 < moves.len() {
  317. if (sc.check)() {
  318. return true;
  319. }
  320. }
  321. return false;
  322. }
  323. for mov in moves {
  324. let undo = game.apply(mov);
  325. let do_return = perft(game, sc, depth - 1);
  326. if do_return {
  327. game.undo_move(undo);
  328. return true;
  329. }
  330. game.undo_move(undo);
  331. }
  332. return false;
  333. }
  334. #[cfg(test)]
  335. mod tests {
  336. use super::*;
  337. #[test]
  338. fn test_move_generation() {
  339. let positions = [
  340. "rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8",
  341. "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10"
  342. ];
  343. let perft_results: [Vec<usize>; 2] = [
  344. vec![44, 1486, 62379, 2103487],
  345. vec![46, 2079, 89890, 3894594]
  346. ];
  347. for (i, &position) in positions.iter().enumerate() {
  348. let mut game = Game::from_fen_str(position).unwrap();
  349. for (j, &p_res) in perft_results[i].iter().enumerate() {
  350. let depth = j + 1;
  351. let mut sc = SearchControl{ nodes: 0, check: &mut (|| false), move_history: &mut RepetitionTable::new(), initial_depth: depth as _ };
  352. perft(&mut game, &mut sc, depth as _);
  353. assert_eq!(sc.nodes, p_res);
  354. }
  355. }
  356. }
  357. }