iterative deepening

This commit is contained in:
Moritz Eigenauer 2025-11-18 15:19:30 +01:00
parent 0c1055d7aa
commit ecf1de6c6a
5 changed files with 77 additions and 9 deletions

Binary file not shown.

View file

@ -29,7 +29,7 @@ fn main() {
let mut engine = Engine::new("Yakari".to_string(), "EiSiMo".to_string()); let mut engine = Engine::new("Yakari".to_string(), "EiSiMo".to_string());
// Set the time limit to 1 second // 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 { for test in &sts {
let fen = &test[0]; let fen = &test[0];
@ -40,7 +40,7 @@ fn main() {
// Record start time // Record start time
let start_time = Instant::now(); let start_time = Instant::now();
let result = engine.search(5); let result = engine.search(490_u64);
// Calculate duration // Calculate duration
let duration = start_time.elapsed(); let duration = start_time.elapsed();

View file

@ -1,7 +1,7 @@
// ... (your use statements)
use crate::board::Board; use crate::board::Board;
use crate::r#move::Move; use crate::r#move::Move;
use crate::search::alpha_beta::alpha_beta; use crate::search::alpha_beta::alpha_beta;
use std::time::{Instant, Duration};
pub struct Engine { pub struct Engine {
pub name: String, pub name: String,
@ -32,8 +32,51 @@ impl Engine {
} }
} }
pub fn search(&mut self, depth: u8) -> String { pub fn search(&mut self, time_limit_ms: u64) -> String {
let (opt_move, _score) = alpha_beta(&mut self.board, depth, 0, -i32::MAX, i32::MAX); 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 { if let Some(mv) = opt_move {
mv.to_algebraic() mv.to_algebraic()
@ -42,4 +85,4 @@ impl Engine {
"null".to_string() "null".to_string()
} }
} }
} }

View file

@ -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::eval::basic::evaluate_board;
use crate::movegen::generate_pseudo_legal_moves; use crate::movegen::generate_pseudo_legal_moves;
use crate::movegen::legal_check::*; use crate::movegen::legal_check::*;
use crate::r#move::{Move, MoveList}; 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 // 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; const MATE_SCORE: i32 = 1_000_000;
@ -21,7 +22,20 @@ pub fn alpha_beta(
ply: u8, ply: u8,
mut alpha: i32, mut alpha: i32,
beta: i32, beta: i32,
start_time: Instant,
time_limit: Duration,
nodes: &mut u64,
) -> (Option<Move>, i32) { ) -> (Option<Move>, 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 { if depth == 0 {
return (None, evaluate_board_relative(board)); return (None, evaluate_board_relative(board));
} }
@ -42,7 +56,18 @@ pub fn alpha_beta(
legal_moves_found = true; legal_moves_found = true;
// Recursive call with negated and swapped alpha/beta // 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; let current_score = -score;
if current_score > best_score { if current_score > best_score {

View file

@ -42,7 +42,7 @@ pub fn uci_mainloop(engine: &mut Engine) {
} }
"go" => { "go" => {
// TODO add a lot functionality // TODO add a lot functionality
println!("bestmove {}", engine.search(6)); println!("bestmove {}", engine.search(1000_u64));
} }
"stop" => { "stop" => {
// TODO stop search as soon as possible // TODO stop search as soon as possible