stub: soft limit working

it seems to not make horrible blunders
This commit is contained in:
dogeystamp 2024-12-20 12:55:55 -05:00
parent 656f026e93
commit b25d21f462
2 changed files with 42 additions and 21 deletions

View File

@ -113,7 +113,7 @@ fn cmd_position(mut tokens: std::str::SplitWhitespace<'_>, state: &mut MainState
/// Play the game.
fn cmd_go(mut tokens: std::str::SplitWhitespace<'_>, state: &mut MainState) {
// hard timeout
let mut hard_ms = 15_000;
let mut hard_ms = 100_000;
// soft timeout
let mut soft_ms = 1_200;
@ -230,7 +230,7 @@ fn outp_bestmove(bestmove: MsgBestmove) {
println!("info score cp {}", eval,)
}
SearchEval::Stopped => {
println!("info string ERROR: stopped search")
panic!("info string ERROR: stopped search")
}
}
match chosen {
@ -278,12 +278,9 @@ fn task_engine(tx_main: Sender<MsgToMain>, rx_engine: Receiver<MsgToEngine>) {
.send(MsgToMain::Bestmove(MsgBestmove { pv, eval }))
.unwrap();
}
MsgToEngine::Stop => {
// Main keeps track of state, so this should not happen.
panic!("Received stop while idle.");
}
MsgToEngine::Stop => {}
MsgToEngine::NewGame => {
state.cache = TranspositionTable::new(state.config.transposition_size);
state.wipe_state();
}
}
}

View File

@ -170,7 +170,7 @@ fn minmax(
beta: Option<EvalInt>,
) -> (Vec<Move>, SearchEval) {
// these operations are relatively expensive, so only run them occasionally
if state.node_count % ((1 << 31) - 1) == 0 {
if state.node_count % (1 << 16) == 0 {
// respect the hard stop if given
match state.rx_engine.try_recv() {
Ok(msg) => match msg {
@ -192,7 +192,6 @@ fn minmax(
}
}
}
state.node_count += 1;
// default to worst, then gradually improve
let mut alpha = alpha.unwrap_or(EVAL_WORST);
@ -280,6 +279,7 @@ fn minmax(
}
}
state.node_count += 1;
(best_continuation, abs_best)
}
@ -297,26 +297,42 @@ pub type TranspositionTable = ZobristTable<TranspositionEntry>;
/// Iteratively deepen search until it is stopped.
fn iter_deep(board: &mut Board, state: &mut EngineState) -> (Vec<Move>, SearchEval) {
let (mut prev_line, mut prev_eval) = minmax(board, state, 1, None, None);
// always preserve two lines (1 is most recent)
let (mut line1, mut eval1) = minmax(board, state, 1, None, None);
let (mut line2, mut eval2) = (line1.clone(), eval1);
for depth in 2..=state.config.depth {
let (line, eval) = minmax(board, state, depth, None, None);
if let Some(soft_lim) = state.time_lims.soft {
if Instant::now() > soft_lim {
if depth & 1 == 1 && (EvalInt::from(eval) - EvalInt::from(prev_eval) > 300) {
// be skeptical if we move last and we suddenly earn a lot of
// centipawns. this may be a sign of horizon problem
return (prev_line, prev_eval);
} else {
return (line, eval);
let mut have_to_ret = false;
// depth of the line we're about to return.
// our knock-off "quiescence" is skeptical of odd depths, so we need to know this.
let mut ret_depth = depth;
if matches!(eval, SearchEval::Stopped) {
ret_depth -= 1;
have_to_ret = true;
} else {
(line2, eval2) = (line1, eval1);
(line1, eval1) = (line, eval);
if let Some(soft_lim) = state.time_lims.soft {
if Instant::now() > soft_lim {
have_to_ret = true;
}
}
}
(prev_line, prev_eval) = (line, eval);
if have_to_ret {
if ret_depth & 1 == 1 && (EvalInt::from(eval1) - EvalInt::from(eval2) > 300) {
// be skeptical if we move last and we suddenly earn a lot of
// centipawns. this may be a sign of horizon problem
return (line2, eval2);
} else {
return (line1, eval1);
}
}
}
(prev_line, prev_eval)
(line1, eval1)
}
/// Deadlines for the engine to think of a move.
@ -337,7 +353,7 @@ pub struct EngineState {
pub rx_engine: mpsc::Receiver<MsgToEngine>,
pub cache: TranspositionTable,
/// Nodes traversed (i.e. number of times minmax called)
node_count: usize,
pub node_count: usize,
pub time_lims: TimeLimits,
}
@ -356,6 +372,14 @@ impl EngineState {
time_lims,
}
}
/// Wipe state between different games.
///
/// Configuration is preserved.
pub fn wipe_state(&mut self) {
self.cache = TranspositionTable::new(self.config.transposition_size);
self.node_count = 0;
}
}
/// Find the best line (in reverse order) and its evaluation.