diff --git a/progress_tracking/progress.xlsx b/progress_tracking/progress.xlsx index f28baa3..b0b5126 100644 Binary files a/progress_tracking/progress.xlsx and b/progress_tracking/progress.xlsx differ diff --git a/src/bin/suite.rs b/src/bin/suite.rs index cd7e569..5b4d8e5 100644 --- a/src/bin/suite.rs +++ b/src/bin/suite.rs @@ -29,7 +29,7 @@ fn main() { let mut engine = Engine::new("Yakari".to_string(), "EiSiMo".to_string()); // Set the time limit to 1 second - let time_limit = Duration::from_secs(1); + let time_limit = Duration::from_millis(500); for test in &sts { let fen = &test[0]; @@ -40,7 +40,7 @@ fn main() { // Record start time let start_time = Instant::now(); - let result = engine.search(5); + let result = engine.search(490_u64); // Calculate duration let duration = start_time.elapsed(); diff --git a/src/engine.rs b/src/engine.rs index a3aa051..f51268c 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,7 +1,7 @@ -// ... (your use statements) use crate::board::Board; use crate::r#move::Move; use crate::search::alpha_beta::alpha_beta; +use std::time::{Instant, Duration}; pub struct Engine { pub name: String, @@ -32,8 +32,51 @@ impl Engine { } } - pub fn search(&mut self, depth: u8) -> String { - let (opt_move, _score) = alpha_beta(&mut self.board, depth, 0, -i32::MAX, i32::MAX); + pub fn search(&mut self, time_limit_ms: u64) -> String { + let start_time = Instant::now(); + let time_limit = Duration::from_millis(time_limit_ms); + + // We track nodes to limit how often we check the clock inside alpha_beta + let mut nodes = 0; + + // Initial search at depth 1 + let (mut opt_move, mut _score) = alpha_beta( + &mut self.board, + 1, + 0, + -i32::MAX, + i32::MAX, + start_time, + time_limit, + &mut nodes + ); + + let mut depth = 2; + + // Iterative Deepening + while start_time.elapsed() < time_limit { + let (new_move, new_score) = alpha_beta( + &mut self.board, + depth, + 0, + -i32::MAX, + i32::MAX, + start_time, + time_limit, + &mut nodes + ); + + // If time ran out during the search, alpha_beta returns garbage (None, 0). + // We must verify we still have time before accepting the new result. + if start_time.elapsed() > time_limit { + break; // Discard new_move, keep the one from the previous depth + } + + opt_move = new_move; + _score = new_score; + + depth += 1; + } if let Some(mv) = opt_move { mv.to_algebraic() @@ -42,4 +85,4 @@ impl Engine { "null".to_string() } } -} \ No newline at end of file +} diff --git a/src/search/alpha_beta.rs b/src/search/alpha_beta.rs index ea9e69a..7562d3a 100644 --- a/src/search/alpha_beta.rs +++ b/src/search/alpha_beta.rs @@ -1,8 +1,9 @@ -use crate::board::{Board, Color}; // <-- Assuming you have a Color enum (e.g., Color::White, Color::Black) +use crate::board::{Board, Color}; use crate::eval::basic::evaluate_board; use crate::movegen::generate_pseudo_legal_moves; use crate::movegen::legal_check::*; use crate::r#move::{Move, MoveList}; +use std::time::{Instant, Duration}; // A score high enough to be > any material eval, but low enough to not overflow when adding ply const MATE_SCORE: i32 = 1_000_000; @@ -21,7 +22,20 @@ pub fn alpha_beta( ply: u8, mut alpha: i32, beta: i32, + start_time: Instant, + time_limit: Duration, + nodes: &mut u64, ) -> (Option, i32) { + // Check for time usage every 4096 nodes to reduce system call overhead + if *nodes % 4096 == 0 { + if start_time.elapsed() > time_limit { + // Return immediately. The return value here effectively signals an abort, + // but the engine must discard this result. + return (None, 0); + } + } + *nodes += 1; + if depth == 0 { return (None, evaluate_board_relative(board)); } @@ -42,7 +56,18 @@ pub fn alpha_beta( legal_moves_found = true; // Recursive call with negated and swapped alpha/beta - let (_, score) = alpha_beta(board, depth - 1, ply + 1, -beta, -alpha); + // Pass time parameters and node counter down + let (_, score) = alpha_beta(board, depth - 1, ply + 1, -beta, -alpha, start_time, time_limit, nodes); + + // If we aborted deeper in the tree (returned 0 due to timeout), + // we should technically propagate that up, but checking elapsed() + // at the loop start (via recursion) handles it eventually. + // For a strict abort, we check here too: + if *nodes % 4096 == 0 && start_time.elapsed() > time_limit { + board.undo_move(undo_mv); + return (None, 0); + } + let current_score = -score; if current_score > best_score { diff --git a/src/uci.rs b/src/uci.rs index 47ef0e4..53526ad 100644 --- a/src/uci.rs +++ b/src/uci.rs @@ -42,7 +42,7 @@ pub fn uci_mainloop(engine: &mut Engine) { } "go" => { // TODO add a lot functionality - println!("bestmove {}", engine.search(6)); + println!("bestmove {}", engine.search(1000_u64)); } "stop" => { // TODO stop search as soon as possible