diff options
author | eug-vs <eugene@eug-vs.xyz> | 2023-08-31 14:03:07 +0300 |
---|---|---|
committer | eug-vs <eugene@eug-vs.xyz> | 2023-08-31 14:03:07 +0300 |
commit | 02a87211a862e76a5fa7611fe1d1dcdc270a9d47 (patch) | |
tree | 46307a89b94d55bbd649f2e23469f7b0f4726adf | |
parent | 1891aed81193bd8913e806f58cfc802c13f9df1e (diff) | |
download | chessnost-02a87211a862e76a5fa7611fe1d1dcdc270a9d47.tar.gz |
perf: only generate moves needed for current stage
-rw-r--r-- | src/board/move_generation.rs | 30 | ||||
-rw-r--r-- | src/grossmeister/move_selector.rs | 34 | ||||
-rw-r--r-- | src/grossmeister/search.rs | 13 |
3 files changed, 49 insertions, 28 deletions
diff --git a/src/board/move_generation.rs b/src/board/move_generation.rs index 5a3650a..7669b85 100644 --- a/src/board/move_generation.rs +++ b/src/board/move_generation.rs @@ -195,5 +195,35 @@ mod tests { println!("{:?}", board.castling_rights); assert!(castle.is_none(), "Castle should not be allowed after king has moved"); } + + #[test] + fn tactical_move_generation() { + let fen = String::from("4k2r/ppp1n3/8/4R1Pp/5P2/q1P5/P1P1BP2/1K1R4 b - - 2 22"); + let board = Board::from_FEN(fen); + + let all_moves = board.generate_pseudolegal_moves(); + + let tactical_only = board.generate_moves_core(true); + let quiet_only = board.generate_moves_core(false); + + let true_tactical = all_moves.iter().filter(|m| m.is_tactical()).copied().collect::<MoveList>(); + let true_quiet = all_moves.iter().filter(|m| !m.is_tactical()).copied().collect::<MoveList>(); + + // Make sure tactical_only and true_tactical are identical sets + for mov in tactical_only.iter() { + assert!(true_tactical.iter().any(|m| *m == *mov)); + } + for mov in true_tactical { + assert!(tactical_only.iter().any(|m| *m == mov)); + } + + // Make sure quiet_only and true_quiet are identical sets + for mov in quiet_only.iter() { + assert!(true_quiet.iter().any(|m| *m == *mov)); + } + for mov in true_quiet { + assert!(quiet_only.iter().any(|m| *m == mov)); + } + } } diff --git a/src/grossmeister/move_selector.rs b/src/grossmeister/move_selector.rs index d26ffc0..a3671e4 100644 --- a/src/grossmeister/move_selector.rs +++ b/src/grossmeister/move_selector.rs @@ -72,7 +72,7 @@ impl MoveGenStage { #[derive(Default, Debug, Clone)] pub struct MoveSelector { - all_moves: ScoredMoveList, + tactical_moves: ScoredMoveList, stage_moves: ScoredMoveIter, pub killer_moves: SmallVec<[Move; 4]>, stage: MoveGenStage, @@ -134,12 +134,11 @@ impl Grossmeister { .collect() } - pub fn next_capture(&mut self) -> Option<Move> { + pub fn next_tactical(&mut self) -> Option<Move> { if self.move_selector().stage_moves.moves.is_empty() { - let moves = self.board.generate_pseudolegal_moves(); - self.move_selector().all_moves = self.score_moves(moves); - let captures = self.move_selector().all_moves.iter().filter(|(m, _)| m.is_tactical()).copied().collect(); - self.init_stage(captures); + let moves = self.board.generate_moves_core(true); + let scored = self.score_moves(moves); + self.init_stage(scored); } self.move_selector().stage_moves.next().map(|(mov, _)| mov) @@ -172,14 +171,14 @@ impl Grossmeister { MoveGenStage::WinningOrEqualTactical => { // Init stage moves if self.move_selector().stage_moves.moves.is_empty() { - // Generate ALL moves (for future re-use) - let moves = self.board.generate_pseudolegal_moves(); - self.move_selector().all_moves = self.score_moves(moves); + // Generate all tactical moves (for future re-use) + let moves = self.board.generate_moves_core(true); + self.move_selector().tactical_moves = self.score_moves(moves); // But we only care about current stage now let new_stage = - self.move_selector().all_moves.iter() - .filter(|(m, score)| m.is_tactical() && *score >= 0.0) + self.move_selector().tactical_moves.iter() + .filter(|(_, score)| *score >= 0.0) .copied() .collect(); @@ -195,7 +194,7 @@ impl Grossmeister { if self.move_selector().stage_moves.moves.is_empty() { let new_stage = self.move_selector().killer_moves.clone() .iter() - .filter(|&m| self.move_selector().all_moves.iter().any(|(m2, _)| m2 == m)) // Test if killer is in the movelist + .filter(|&m| self.move_selector().tactical_moves.iter().any(|(m2, _)| m2 == m)) // Test if killer is in the movelist .map(|&m| (m, 0.0)) .collect(); self.init_stage(new_stage); @@ -207,12 +206,9 @@ impl Grossmeister { } MoveGenStage::Quiet => { if self.move_selector().stage_moves.moves.is_empty() { - let new_stage = self.move_selector().all_moves - .iter() - .filter(|(m, _)| !m.is_tactical()) - .copied() - .collect(); - self.init_stage(new_stage); + let moves = self.board.generate_moves_core(false); + let scored = self.score_moves(moves); + self.init_stage(scored); } match self.move_selector().stage_moves.next() { Some((mov, _)) => return Some(mov), @@ -222,7 +218,7 @@ impl Grossmeister { MoveGenStage::LosingTactical => { if self.move_selector().stage_moves.moves.is_empty() { let new_stage = - self.move_selector().all_moves.iter() + self.move_selector().tactical_moves.iter() .filter(|(m, score)| m.is_tactical() && *score < 0.0) .copied() .collect(); diff --git a/src/grossmeister/search.rs b/src/grossmeister/search.rs index 68756d8..b7a4208 100644 --- a/src/grossmeister/search.rs +++ b/src/grossmeister/search.rs @@ -1,11 +1,8 @@ -use std::cmp::Ordering; use std::f32::INFINITY; -use smallvec::SmallVec; +use crate::{moves::{Move, MoveKind}, board::io::IO}; -use crate::{moves::{Move, MoveKind}, board::{io::IO, move_generation::MoveList}}; - -use super::{Grossmeister, ttable::{NodeType, TTABLE_SIZE, TranspositionTableItem}, move_selector::MoveSelector}; +use super::{Grossmeister, ttable::{NodeType, TTABLE_SIZE, TranspositionTableItem}}; const VALUE_WIN: f32 = 20_000.0; @@ -166,15 +163,13 @@ impl Grossmeister { alpha = stand_pat; } - tactical_only = true; - // If we are not in check, we can only search tactical moves - // moves.retain(|m| m.is_tactical()) + tactical_only = true; } let mut legal_move_found = false; self.cleanup_selector(); - while let Some(mov) = if tactical_only { self.next_capture() } 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; |