feat: uci options, quiet position detector
This commit is contained in:
parent
fc8eab4d4b
commit
ede46552fe
@ -165,6 +165,8 @@ pub struct MsgBestmove {
|
|||||||
pub pv: Vec<Move>,
|
pub pv: Vec<Move>,
|
||||||
/// Evaluation of the position
|
/// Evaluation of the position
|
||||||
pub eval: SearchEval,
|
pub eval: SearchEval,
|
||||||
|
/// Extra information (displayed as `info string`).
|
||||||
|
pub info: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interface messages that may be received by main's channel.
|
/// Interface messages that may be received by main's channel.
|
||||||
|
72
src/main.rs
72
src/main.rs
@ -51,8 +51,9 @@ macro_rules! ignore {
|
|||||||
/// UCI engine metadata query.
|
/// UCI engine metadata query.
|
||||||
fn cmd_uci() -> String {
|
fn cmd_uci() -> String {
|
||||||
let str = "id name chess_inator\n\
|
let str = "id name chess_inator\n\
|
||||||
id author dogeystamp\n\
|
id author dogeystamp\n\
|
||||||
uciok";
|
option name NNUETrainInfo type check default false\n\
|
||||||
|
uciok";
|
||||||
str.into()
|
str.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +124,6 @@ fn cmd_go(mut tokens: std::str::SplitWhitespace<'_>, state: &mut MainState) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
while let Some(token) = tokens.next() {
|
while let Some(token) = tokens.next() {
|
||||||
match token {
|
match token {
|
||||||
"wtime" => {
|
"wtime" => {
|
||||||
@ -207,6 +207,50 @@ fn cmd_eval(mut _tokens: std::str::SplitWhitespace<'_>, state: &mut MainState) {
|
|||||||
println!("- total: {}", res.total_eval);
|
println!("- total: {}", res.total_eval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn match_true_false(s: &str) -> Option<bool> {
|
||||||
|
match s {
|
||||||
|
"true" => Some(true),
|
||||||
|
"false" => Some(false),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set engine options via UCI.
|
||||||
|
fn cmd_setoption(mut tokens: std::str::SplitWhitespace<'_>, state: &mut MainState) {
|
||||||
|
while let Some(token) = tokens.next() {
|
||||||
|
fn get_val(mut tokens: std::str::SplitWhitespace<'_>) -> Option<String> {
|
||||||
|
if let Some("value") = tokens.next() {
|
||||||
|
if let Some(value) = tokens.next() {
|
||||||
|
return Some(value.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
match token {
|
||||||
|
"name" => {
|
||||||
|
if let Some(name) = tokens.next() {
|
||||||
|
match name {
|
||||||
|
"NNUETrainInfo" => {
|
||||||
|
if let Some(value) = get_val(tokens) {
|
||||||
|
if let Some(value) = match_true_false(&value) {
|
||||||
|
state.config.nnue_train_info = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("info string Unknown option: {}", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => ignore!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Root UCI parser.
|
/// Root UCI parser.
|
||||||
fn cmd_root(mut tokens: std::str::SplitWhitespace<'_>, state: &mut MainState) {
|
fn cmd_root(mut tokens: std::str::SplitWhitespace<'_>, state: &mut MainState) {
|
||||||
while let Some(token) = tokens.next() {
|
while let Some(token) = tokens.next() {
|
||||||
@ -242,6 +286,9 @@ fn cmd_root(mut tokens: std::str::SplitWhitespace<'_>, state: &mut MainState) {
|
|||||||
state.tx_engine.send(MsgToEngine::Stop).unwrap();
|
state.tx_engine.send(MsgToEngine::Stop).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"setoption" => {
|
||||||
|
cmd_setoption(tokens, state);
|
||||||
|
}
|
||||||
// non-standard command.
|
// non-standard command.
|
||||||
"eval" => {
|
"eval" => {
|
||||||
cmd_eval(tokens, state);
|
cmd_eval(tokens, state);
|
||||||
@ -274,6 +321,10 @@ fn outp_bestmove(bestmove: MsgBestmove) {
|
|||||||
panic!("info string ERROR: stopped search")
|
panic!("info string ERROR: stopped search")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for line in bestmove.info {
|
||||||
|
println!("info string {line}");
|
||||||
|
}
|
||||||
|
|
||||||
match chosen {
|
match chosen {
|
||||||
Some(mv) => println!("bestmove {}", mv.to_uci_algebraic()),
|
Some(mv) => println!("bestmove {}", mv.to_uci_algebraic()),
|
||||||
None => println!("bestmove 0000"),
|
None => println!("bestmove 0000"),
|
||||||
@ -315,8 +366,21 @@ fn task_engine(tx_main: Sender<MsgToMain>, rx_engine: Receiver<MsgToEngine>) {
|
|||||||
state.config = msg_box.config;
|
state.config = msg_box.config;
|
||||||
state.time_lims = msg_box.time_lims;
|
state.time_lims = msg_box.time_lims;
|
||||||
let (pv, eval) = best_line(&mut board, &mut state);
|
let (pv, eval) = best_line(&mut board, &mut state);
|
||||||
|
|
||||||
|
let mut info: Vec<String> = Vec::new();
|
||||||
|
if state.config.nnue_train_info {
|
||||||
|
let is_quiet = chess_inator::search::is_quiescent_position(&board, eval);
|
||||||
|
|
||||||
|
let is_quiet = if is_quiet {"quiet"} else {"non-quiet"};
|
||||||
|
info.push(format!("NNUETrainInfo {}", is_quiet))
|
||||||
|
}
|
||||||
|
|
||||||
tx_main
|
tx_main
|
||||||
.send(MsgToMain::Bestmove(MsgBestmove { pv, eval }))
|
.send(MsgToMain::Bestmove(MsgBestmove {
|
||||||
|
pv,
|
||||||
|
eval,
|
||||||
|
info,
|
||||||
|
}))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
MsgToEngine::Stop => {}
|
MsgToEngine::Stop => {}
|
||||||
|
@ -128,6 +128,8 @@ pub struct SearchConfig {
|
|||||||
pub enable_trans_table: bool,
|
pub enable_trans_table: bool,
|
||||||
/// Transposition table size (2^n where this is n)
|
/// Transposition table size (2^n where this is n)
|
||||||
pub transposition_size: usize,
|
pub transposition_size: usize,
|
||||||
|
/// Print machine-readable information about the position during NNUE training data generation.
|
||||||
|
pub nnue_train_info: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SearchConfig {
|
impl Default for SearchConfig {
|
||||||
@ -139,6 +141,7 @@ impl Default for SearchConfig {
|
|||||||
contempt: 0,
|
contempt: 0,
|
||||||
enable_trans_table: true,
|
enable_trans_table: true,
|
||||||
transposition_size: 24,
|
transposition_size: 24,
|
||||||
|
nnue_train_info: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -450,11 +453,7 @@ impl TimeLimits {
|
|||||||
/// Make time limits based on wtime, btime (but color-independent).
|
/// Make time limits based on wtime, btime (but color-independent).
|
||||||
///
|
///
|
||||||
/// Also takes in eval metrics, for instance to avoid wasting too much time in the opening.
|
/// Also takes in eval metrics, for instance to avoid wasting too much time in the opening.
|
||||||
pub fn from_ourtime_theirtime(
|
pub fn from_ourtime_theirtime(ourtime_ms: u64, _theirtime_ms: u64, eval: EvalMetrics) -> Self {
|
||||||
ourtime_ms: u64,
|
|
||||||
_theirtime_ms: u64,
|
|
||||||
eval: EvalMetrics,
|
|
||||||
) -> Self {
|
|
||||||
// hard timeout (max)
|
// hard timeout (max)
|
||||||
let mut hard_ms = 100_000;
|
let mut hard_ms = 100_000;
|
||||||
// soft timeout (default max)
|
// soft timeout (default max)
|
||||||
@ -550,3 +549,27 @@ pub fn best_move(board: &mut Board, engine_state: &mut EngineState) -> Option<Mo
|
|||||||
let (line, _eval) = best_line(board, engine_state);
|
let (line, _eval) = best_line(board, engine_state);
|
||||||
line.last().copied()
|
line.last().copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Utility for NNUE training set generation to determine if a position is quiet or not.
|
||||||
|
///
|
||||||
|
/// Our definition of "quiet" is that there are no checks, and the static and quiescence search
|
||||||
|
/// evaluations are similar. (See https://arxiv.org/html/2412.17948v1.)
|
||||||
|
///
|
||||||
|
/// It is the caller's responsibility to get the search evaluation and pass it to this function.
|
||||||
|
pub fn is_quiescent_position(board: &Board, eval: SearchEval) -> bool {
|
||||||
|
// max centipawn value difference to call "similar"
|
||||||
|
const THRESHOLD: EvalInt = 170;
|
||||||
|
|
||||||
|
if board.is_check(board.turn) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches!(eval, SearchEval::Checkmate(_)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// white perspective
|
||||||
|
let abs_eval = EvalInt::from(eval) * EvalInt::from(board.turn.sign());
|
||||||
|
|
||||||
|
(board.eval() - EvalInt::from(abs_eval)).abs() <= THRESHOLD.abs()
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user