Use increment from uci winc/binc in time management
This commit is contained in:
@@ -9,52 +9,38 @@ use crate::{
|
||||
|
||||
use super::{
|
||||
negamax,
|
||||
time::{soft_limit, TimeInfo},
|
||||
time::{hard_limit, TimeInfo},
|
||||
};
|
||||
|
||||
pub fn iterative_deepening(
|
||||
game: &mut Game,
|
||||
max_depth: u8,
|
||||
remaining_time: u128,
|
||||
time_info: &TimeInfo,
|
||||
) -> anyhow::Result<Option<Move>> {
|
||||
let (mut best_move, mut best_score) = (None, MIN_SCORE);
|
||||
let time = std::time::Instant::now();
|
||||
let mut best_move = None;
|
||||
|
||||
for depth in 1..=max_depth {
|
||||
if soft_limit(&time, remaining_time, best_score) {
|
||||
if hard_limit(time_info.instant, time_info.time, time_info.inc) {
|
||||
return Ok(best_move);
|
||||
}
|
||||
|
||||
let mut total_nodes_searched = 0;
|
||||
|
||||
let score = negamax::negamax(
|
||||
game,
|
||||
MIN_SCORE,
|
||||
MAX_SCORE,
|
||||
depth,
|
||||
0,
|
||||
&TimeInfo::new(time, remaining_time),
|
||||
&mut total_nodes_searched,
|
||||
);
|
||||
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_score = score?;
|
||||
best_move = game.tt.lookup(game.hash).and_then(|entry| entry.mv);
|
||||
|
||||
let nps = ((total_nodes_searched * 1_000_000) as u128).div_ceil(time.elapsed().as_micros())
|
||||
as u64;
|
||||
|
||||
write_response(
|
||||
&mut io::stdout(),
|
||||
&search_info(
|
||||
&log_depth_results(
|
||||
depth,
|
||||
time.elapsed().as_millis() as u64,
|
||||
total_nodes_searched,
|
||||
nps,
|
||||
best_score,
|
||||
time_info.instant.elapsed().as_millis() as u64,
|
||||
nodes,
|
||||
time_info.nps(nodes),
|
||||
score?,
|
||||
best_move,
|
||||
),
|
||||
)?;
|
||||
@@ -62,22 +48,22 @@ pub fn iterative_deepening(
|
||||
Ok(best_move)
|
||||
}
|
||||
|
||||
fn search_info(
|
||||
fn log_depth_results(
|
||||
depth: u8,
|
||||
time: u64,
|
||||
total_nodes_searched: u64,
|
||||
seconds: u64,
|
||||
nodes: u64,
|
||||
nps: u64,
|
||||
best_score: i32,
|
||||
best_move: Option<Move>,
|
||||
) -> Response {
|
||||
Response::Info(format!(
|
||||
"info depth {} time {} nodes {} nps {} eval {} pv {}",
|
||||
"info depth {} seconds {} nodes {} nps {} eval {} bestmove {}",
|
||||
depth,
|
||||
time,
|
||||
total_nodes_searched,
|
||||
seconds,
|
||||
nodes,
|
||||
nps,
|
||||
best_score,
|
||||
best_move.expect("msg: No best move found")
|
||||
best_move.expect("No best move found")
|
||||
))
|
||||
}
|
||||
|
||||
@@ -86,7 +72,7 @@ mod tests {
|
||||
use crate::{
|
||||
board::fen::from_fen,
|
||||
movegen::attack_generator::init_attacks,
|
||||
search::{iterative_deepening, MAX_DEPTH, REMAINING_TIME_DEFAULT},
|
||||
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";
|
||||
@@ -95,11 +81,15 @@ mod tests {
|
||||
fn test_iterative_deepening() -> anyhow::Result<()> {
|
||||
init_attacks();
|
||||
let mut game = from_fen(FEN).unwrap();
|
||||
let time_now = std::time::Instant::now();
|
||||
let instant = std::time::Instant::now();
|
||||
|
||||
iterative_deepening::iterative_deepening(&mut game, MAX_DEPTH, REMAINING_TIME_DEFAULT)?;
|
||||
iterative_deepening::iterative_deepening(
|
||||
&mut game,
|
||||
MAX_DEPTH,
|
||||
&TimeInfo::new(instant, TIME, INC),
|
||||
)?;
|
||||
|
||||
dbg!(time_now.elapsed());
|
||||
dbg!(instant.elapsed());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ pub mod quiescence;
|
||||
pub mod time;
|
||||
pub mod transposition_table;
|
||||
|
||||
pub const MAX_DEPTH: u8 = 7;
|
||||
pub const REMAINING_TIME_DEFAULT: u128 = 100000; // in ms
|
||||
pub const HARD_LIMIT_DIVISION: u128 = 10; // % of the remaining time
|
||||
pub const SOFT_LIMIT_DIVISION: u128 = HARD_LIMIT_DIVISION / 2;
|
||||
pub const SOFT_EVAL_THRESHOLD: i32 = 500;
|
||||
pub const MAX_TT_SIZE: u64 = 1000000;
|
||||
pub const MAX_DEPTH: u8 = 50;
|
||||
pub const TIME: u128 = 1000;
|
||||
pub const INC: u128 = 1000;
|
||||
pub const HARD_LIMIT_DIVISION: u128 = 10;
|
||||
pub const SOFT_LIMIT_DIVISION: u128 = HARD_LIMIT_DIVISION * 2;
|
||||
pub const MAX_TT_SIZE: u64 = 1500000;
|
||||
|
||||
@@ -19,9 +19,9 @@ pub fn negamax(
|
||||
depth: u8,
|
||||
plies: u8,
|
||||
time_info: &TimeInfo,
|
||||
total_nodes_searched: &mut u64,
|
||||
nodes: &mut u64,
|
||||
) -> Result<i32> {
|
||||
if hard_limit(&time_info.time, time_info.remaining_time_in_ms) {
|
||||
if hard_limit(time_info.instant, time_info.time, time_info.inc) {
|
||||
bail!("Time is up! In Negamax");
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ pub fn negamax(
|
||||
}
|
||||
|
||||
if depth == 0 {
|
||||
let q_score = quiescence(game, alpha, beta, time_info, total_nodes_searched)
|
||||
.map_err(|e| anyhow!("{e}"))?;
|
||||
let q_score =
|
||||
quiescence(game, alpha, beta, time_info, nodes).map_err(|e| anyhow!("{e}"))?;
|
||||
return Ok(q_score);
|
||||
}
|
||||
|
||||
@@ -52,18 +52,10 @@ pub fn negamax(
|
||||
continue;
|
||||
}
|
||||
legal_moves += 1;
|
||||
*total_nodes_searched += 1;
|
||||
*nodes += 1;
|
||||
|
||||
let score = if legal_moves == 1 {
|
||||
-negamax(
|
||||
game,
|
||||
-beta,
|
||||
-alpha,
|
||||
depth - 1,
|
||||
plies + 1,
|
||||
time_info,
|
||||
total_nodes_searched,
|
||||
)?
|
||||
-negamax(game, -beta, -alpha, depth - 1, plies + 1, time_info, nodes)?
|
||||
} else {
|
||||
let mut score = -negamax(
|
||||
game,
|
||||
@@ -72,18 +64,10 @@ pub fn negamax(
|
||||
depth - 1,
|
||||
plies + 1,
|
||||
time_info,
|
||||
total_nodes_searched,
|
||||
nodes,
|
||||
)?;
|
||||
if score > alpha && score < beta {
|
||||
score = -negamax(
|
||||
game,
|
||||
-beta,
|
||||
-alpha,
|
||||
depth - 1,
|
||||
plies + 1,
|
||||
time_info,
|
||||
total_nodes_searched,
|
||||
)?;
|
||||
score = -negamax(game, -beta, -alpha, depth - 1, plies + 1, time_info, nodes)?;
|
||||
}
|
||||
score
|
||||
};
|
||||
@@ -123,6 +107,7 @@ mod tests {
|
||||
use crate::movegen::r#move::Move;
|
||||
use crate::search::negamax::negamax;
|
||||
use crate::search::time::TimeInfo;
|
||||
use crate::search::{INC, TIME};
|
||||
|
||||
const FEN_MATE_IN_1: [&str; 2] = [
|
||||
"8/8/8/8/8/4q1k1/8/5K2 b - - 0 1",
|
||||
@@ -135,7 +120,7 @@ mod tests {
|
||||
let mut game = from_fen(FEN_MATE_IN_1[0]).unwrap();
|
||||
|
||||
let e3f2 = Move::new(Square::E3, Square::F2);
|
||||
let time_info = TimeInfo::new(std::time::Instant::now(), 1000);
|
||||
let time_info = TimeInfo::new(std::time::Instant::now(), TIME, INC);
|
||||
negamax(&mut game, MIN_SCORE, MAX_SCORE, 2, 0, &time_info, &mut 0)
|
||||
.expect("Expected a search result");
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ pub fn quiescence(
|
||||
time_info: &TimeInfo,
|
||||
total_nodes_searched: &mut u64,
|
||||
) -> Result<i32> {
|
||||
if hard_limit(&time_info.time, time_info.remaining_time_in_ms) {
|
||||
if hard_limit(time_info.instant, time_info.time, time_info.inc) {
|
||||
bail!("Time is up! In Quiescence");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
use std::time::Instant;
|
||||
|
||||
use super::{HARD_LIMIT_DIVISION, SOFT_EVAL_THRESHOLD, SOFT_LIMIT_DIVISION};
|
||||
use super::HARD_LIMIT_DIVISION;
|
||||
|
||||
pub struct TimeInfo {
|
||||
pub time: Instant,
|
||||
pub remaining_time_in_ms: u128,
|
||||
pub instant: Instant,
|
||||
pub time: u128,
|
||||
pub inc: u128,
|
||||
}
|
||||
|
||||
impl TimeInfo {
|
||||
pub const fn new(time: Instant, remaining_time_in_ms: u128) -> Self {
|
||||
Self {
|
||||
time,
|
||||
remaining_time_in_ms,
|
||||
}
|
||||
pub const fn new(instant: Instant, time: u128, inc: u128) -> Self {
|
||||
Self { instant, time, inc }
|
||||
}
|
||||
|
||||
pub fn nps(&self, nodes: u64) -> u64 {
|
||||
((nodes * 1_000_000) as u128).div_ceil(self.instant.elapsed().as_micros()) as u64
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hard_limit(time_now: &Instant, remaining_time: u128) -> bool {
|
||||
time_now.elapsed().as_millis() >= remaining_time / HARD_LIMIT_DIVISION
|
||||
}
|
||||
|
||||
pub fn soft_limit(time: &Instant, remaining_time: u128, eval: i32) -> bool {
|
||||
time.elapsed().as_millis() >= remaining_time / SOFT_LIMIT_DIVISION && eval > SOFT_EVAL_THRESHOLD
|
||||
pub fn hard_limit(time_now: Instant, time: u128, inc: u128) -> bool {
|
||||
time_now.elapsed().as_millis() >= time / HARD_LIMIT_DIVISION + inc / 2
|
||||
}
|
||||
|
||||
@@ -58,12 +58,12 @@ mod tests {
|
||||
fn test_transposition_table() -> anyhow::Result<()> {
|
||||
init_attacks();
|
||||
let mut game = from_fen(FEN).unwrap();
|
||||
let time_info = TimeInfo::new(std::time::Instant::now(), 30000);
|
||||
let time_info = TimeInfo::new(std::time::Instant::now(), 30000, 100);
|
||||
|
||||
negamax(&mut game, MIN_SCORE, MAX_SCORE, 2, 0, &time_info, &mut 0)
|
||||
.expect("Expected a search result");
|
||||
|
||||
dbg!(time_info.time.elapsed());
|
||||
dbg!(time_info.instant.elapsed());
|
||||
|
||||
let will_be_hash = from_fen(FEN_POSSIBLE).unwrap().hash;
|
||||
let tt_entry = game.tt.lookup(will_be_hash);
|
||||
|
||||
Reference in New Issue
Block a user