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

View File

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