diff --git a/src/board/board.rs b/src/board/board.rs index 24e0137..895f80e 100644 --- a/src/board/board.rs +++ b/src/board/board.rs @@ -10,7 +10,7 @@ use crate::movegen::move_generator::{ bishop_pseudo_moves, king_pseudo_moves, knight_pseudo_moves, pawn_pseudo_moves, queen_pseudo_moves, rook_pseudo_moves, }; -use crate::movegen::r#move::{Move, Promote}; +use crate::movegen::r#move::{Move, MoveType, Promote}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Board { @@ -120,6 +120,18 @@ impl Board { moves } + pub fn pseudo_moves_all_captures(&self) -> Vec { + self.pseudo_moves_all() + .into_iter() + .filter(|m| { + matches!( + m.move_type, + MoveType::Capture | MoveType::PromotionCapture(_) + ) + }) + .collect() + } + pub fn pseudo_moves(&self, color: Color, piece_type: PieceType) -> Vec { let all_occupancies = self.all_occupancies(); let (pieces, opponent_occupancies, own_occupancies) = match color { diff --git a/src/board/transposition_table.rs b/src/board/transposition_table.rs index ad33b54..ff521c3 100644 --- a/src/board/transposition_table.rs +++ b/src/board/transposition_table.rs @@ -89,9 +89,13 @@ mod tests { init_attacks(); let mut game = from_fen(FEN)?; let mut tt = TranspositionTable::new(1000000); + let mut nodes = 0; + let time_now = std::time::Instant::now(); // fill Transposition Table - search::negamax::negamax(&mut game, MIN_SCORE, MAX_SCORE, 2, 0, &mut tt); + search::negamax::negamax(&mut game, MIN_SCORE, MAX_SCORE, 4, 0, &mut tt, &mut nodes); + dbg!(nodes); + dbg!(time_now.elapsed()); let will_be_hash = from_fen(FEN_WILL_BE)?.hash; let tt_entry = tt.lookup(will_be_hash); diff --git a/src/interface/uci.rs b/src/interface/uci.rs index 3c1cf63..76cd1f9 100644 --- a/src/interface/uci.rs +++ b/src/interface/uci.rs @@ -150,6 +150,7 @@ pub fn uci_go(go: &mut SplitWhitespace, game: &mut Game) -> Result params.depth.unwrap_or(MAX_DEPTH), 0, &mut tt, + &mut 0, ) .0 .expect("No move selected")) diff --git a/src/search/negamax.rs b/src/search/negamax.rs index 1be46ff..d1601ca 100644 --- a/src/search/negamax.rs +++ b/src/search/negamax.rs @@ -9,6 +9,8 @@ use crate::{ use super::{move_ordering::score_by_mvv_lva, quiescence::quiescence}; +const QUIESCENCE_DEPTH: u8 = 3; + pub fn negamax( game: &mut Game, mut alpha: i32, @@ -16,9 +18,13 @@ pub fn negamax( depth: u8, plies: u8, tt: &mut TranspositionTable, + nodes: &mut u64, ) -> (Option, i32) { + *nodes += 1; + if depth == 0 { - return (None, quiescence(game, alpha, beta).1); + let q = quiescence(game, alpha, beta, nodes, QUIESCENCE_DEPTH).1; + return (None, q); } if let Some(entry) = tt.lookup(game.hash) { @@ -44,9 +50,9 @@ pub fn negamax( game.unmake_move(); continue; } - legal_moves += 1; - let move_score = -negamax(game, -beta, -alpha, depth - 1, plies + 1, tt).1; + legal_moves += 1; + let move_score = -negamax(game, -beta, -alpha, depth - 1, plies + 1, tt, nodes).1; game.unmake_move(); if move_score > best_score { @@ -117,9 +123,11 @@ mod tests { let mut tt = TranspositionTable::new(1000000); let e3f2 = Move::new(Square::E3, Square::F2); - let anointed_move = negamax(&mut game, MIN_SCORE, MAX_SCORE, 2, 0, &mut tt) + let mut nodes = 0; + let anointed_move = negamax(&mut game, MIN_SCORE, MAX_SCORE, 2, 0, &mut tt, &mut nodes) .0 .unwrap(); + dbg!(nodes); assert_eq!(e3f2, anointed_move); diff --git a/src/search/perft.rs b/src/search/perft.rs index 87cd730..f998a94 100644 --- a/src/search/perft.rs +++ b/src/search/perft.rs @@ -73,13 +73,30 @@ mod tests { fn perft(fen: &str, depth: u8) -> Result { init_attacks(); + + let time_now = std::time::Instant::now(); + let mut game = from_fen(fen)?; let mut nodes = 0; driver(&mut game, &mut nodes, depth); + dbg!(time_now.elapsed()); + Ok(nodes) } + const FEN: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R w KQk g6 0 1"; + + #[ignore] + #[test] + fn test_nodes() -> Result<(), String> { + init_attacks(); + assert_eq!(perft(FEN, 6)?, 0); + + Ok(()) + } + + #[test] fn test_perft_1() -> Result<(), String> { init_attacks(); diff --git a/src/search/quiescence.rs b/src/search/quiescence.rs index 3598e2d..6ee9247 100644 --- a/src/search/quiescence.rs +++ b/src/search/quiescence.rs @@ -1,12 +1,20 @@ -use crate::{ - board::game::Game, - evaluation::evaluation::evaluate_position, - movegen::r#move::{Move, MoveType}, -}; +use crate::{board::game::Game, evaluation::evaluation::evaluate_position, movegen::r#move::Move}; use super::move_ordering::score_by_mvv_lva; -pub fn quiescence(game: &mut Game, mut alpha: i32, beta: i32) -> (Option, i32) { +pub fn quiescence( + game: &mut Game, + mut alpha: i32, + beta: i32, + nodes: &mut u64, + depth: u8, +) -> (Option, i32) { + *nodes += 1; + + if depth == 0 { + return (None, evaluate_position(&game.board)); + } + let color = game.current_player(); let stand_pat = evaluate_position(&game.board); @@ -18,18 +26,7 @@ pub fn quiescence(game: &mut Game, mut alpha: i32, beta: i32) -> (Option, alpha = stand_pat; } - let mut captures: Vec<_> = game - .board - .pseudo_moves_all() - .into_iter() - .filter(|m| { - matches!( - m.move_type, - MoveType::Capture | MoveType::PromotionCapture(_) - ) - }) - .collect(); - + let mut captures: Vec<_> = game.board.pseudo_moves_all_captures(); captures.sort_unstable_by_key(|mv| score_by_mvv_lva(&game.mailbox, *mv)); for mv in captures { @@ -40,7 +37,7 @@ pub fn quiescence(game: &mut Game, mut alpha: i32, beta: i32) -> (Option, continue; } - let move_score = -quiescence(game, -beta, -alpha).1; + let move_score = -quiescence(game, -beta, -alpha, nodes, depth - 1).1; game.unmake_move(); if move_score >= beta {