aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoreug-vs <eugene@eug-vs.xyz>2023-08-31 14:03:07 +0300
committereug-vs <eugene@eug-vs.xyz>2023-08-31 14:03:07 +0300
commit02a87211a862e76a5fa7611fe1d1dcdc270a9d47 (patch)
tree46307a89b94d55bbd649f2e23469f7b0f4726adf
parent1891aed81193bd8913e806f58cfc802c13f9df1e (diff)
downloadchessnost-02a87211a862e76a5fa7611fe1d1dcdc270a9d47.tar.gz
perf: only generate moves needed for current stage
-rw-r--r--src/board/move_generation.rs30
-rw-r--r--src/grossmeister/move_selector.rs34
-rw-r--r--src/grossmeister/search.rs13
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;