use crate::{ board::game::Game, evaluation::{MAX_SCORE, MIN_SCORE}, interface::uci::Response, movegen::r#move::Move, }; use super::{ negamax, time::TimeInfo, ASPIRATION_WINDOW_DEPTH_THRESHOLD, ASPIRATION_WINDOW_EXPANSION, ASPIRATION_WINDOW_INITIAL, }; pub fn iterative_deepening( game: &mut Game, max_depth: u8, time: &TimeInfo, ) -> anyhow::Result> { let mut best_move = None; let mut best_score = MIN_SCORE; 'iterative_deepening: for depth in 1..=max_depth { if time.exceed_soft_limit() { println!("Soft limit exceeded in negamax"); return Ok(best_move); } let mut nodes = 0; let mut score; if depth < ASPIRATION_WINDOW_DEPTH_THRESHOLD { score = negamax::negamax(game, MIN_SCORE, MAX_SCORE, depth, 0, time, &mut nodes, true); } else { let mut window_size = ASPIRATION_WINDOW_INITIAL; let mut alpha = (best_score - window_size).max(MIN_SCORE); let mut beta = (best_score + window_size).min(MAX_SCORE); 'aspiration_windows: loop { score = negamax::negamax(game, alpha, beta, depth, 0, time, &mut nodes, true); let score = match score { Ok(score) => score, Err(ref e) => { println!("Error: {}", Response::Info(e.to_string())); break 'iterative_deepening; } }; if score >= beta { beta = MAX_SCORE.min(beta.saturating_add(window_size)); window_size = window_size.saturating_mul(ASPIRATION_WINDOW_EXPANSION); } else if score <= alpha { alpha = MIN_SCORE.max(alpha.saturating_sub(window_size)); window_size = window_size.saturating_mul(ASPIRATION_WINDOW_EXPANSION); } else { best_score = score; break 'aspiration_windows; } } } if let Err(e) = score { println!("Error: {}", Response::Info(e.to_string())); break; } best_move = game.tt.lookup(game.hash).and_then(|entry| entry.mv); println!( "info depth {} ms {} nodes {} nps {} eval {} bestmove {}", depth, time.instant.elapsed().as_millis(), nodes, time.nps(nodes), score?, best_move.expect("No best move found") ); } Ok(best_move) } #[cfg(test)] mod tests { use crate::{ board::fen::from_fen, movegen::attack_generator::init_attacks, search::{iterative_deepening, time::TimeInfo, INC, MAX_DEPTH, TIME}, }; const FEN: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R w KQk g6 0 1"; #[test] fn test_iterative_deepening() -> anyhow::Result<()> { init_attacks(); let mut game = from_fen(FEN).unwrap(); let instant = std::time::Instant::now(); iterative_deepening::iterative_deepening( &mut game, MAX_DEPTH, &TimeInfo::new(instant, TIME, INC), )?; dbg!(instant.elapsed()); Ok(()) } }