use std::io; use crate::{ board::game::Game, evaluation::{MAX_SCORE, MIN_SCORE}, interface::uci::{write_response, Response}, movegen::r#move::Move, }; use super::{ negamax, time::{hard_limit, TimeInfo}, }; pub fn iterative_deepening( game: &mut Game, max_depth: u8, time_info: &TimeInfo, ) -> anyhow::Result> { let mut best_move = None; for depth in 1..=max_depth { if hard_limit(time_info.instant, time_info.time, time_info.inc) { return Ok(best_move); } let mut nodes = 0; let score = negamax::negamax(game, MIN_SCORE, MAX_SCORE, depth, 0, time_info, &mut nodes); if score.is_err() { break; } best_move = game.tt.lookup(game.hash).and_then(|entry| entry.mv); write_response( &mut io::stdout(), &log_depth_results( depth, time_info.instant.elapsed().as_millis() as u64, nodes, time_info.nps(nodes), score?, best_move, ), )?; } Ok(best_move) } fn log_depth_results( depth: u8, seconds: u64, nodes: u64, nps: u64, best_score: i32, best_move: Option, ) -> Response { Response::Info(format!( "info depth {} seconds {} nodes {} nps {} eval {} bestmove {}", depth, seconds, nodes, nps, best_score, best_move.expect("No best move found") )) } #[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(()) } }