diff --git a/src/main.rs b/src/main.rs index e79da86..e8e754e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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, rx_engine: Receiver) { .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(); } } } diff --git a/src/search.rs b/src/search.rs index 6fd47d9..490b515 100644 --- a/src/search.rs +++ b/src/search.rs @@ -170,7 +170,7 @@ fn minmax( beta: Option, ) -> (Vec, 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; /// Iteratively deepen search until it is stopped. fn iter_deep(board: &mut Board, state: &mut EngineState) -> (Vec, 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, 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.