send uci info

This commit is contained in:
Moritz 2025-11-20 10:20:56 +01:00
parent d5b679b6bf
commit 711569b8e5
8 changed files with 118 additions and 59 deletions

View file

@ -1,5 +1,5 @@
use chess_engine::board::Board; use chess_engine::board::Board;
use chess_engine::movegen::picker::MoveGenerator; use chess_engine::movegen::picker::MovePicker;
use chess_engine::movegen::legal_check::is_other_king_attacked; use chess_engine::movegen::legal_check::is_other_king_attacked;
use criterion::{criterion_group, criterion_main, Criterion}; use criterion::{criterion_group, criterion_main, Criterion};
@ -8,7 +8,7 @@ fn count_legal_moves_recursive(board: &mut Board, depth: u8) -> u64 {
return 1_u64; return 1_u64;
} }
let mut generator = MoveGenerator::new(); let mut generator = MovePicker::new();
let mut leaf_nodes = 0_u64; let mut leaf_nodes = 0_u64;
while let Some(mv) = generator.next(board) { while let Some(mv) = generator.next(board) {

View file

@ -99,7 +99,7 @@ def main():
try: try:
# The suite_score is still a raw float, e.g., 95.5 # The suite_score is still a raw float, e.g., 95.5
suite_score = float(suite_result.stdout.strip()) suite_score = float(suite_result.stdout.strip().splitlines()[-1].strip())
except ValueError: except ValueError:
print(f"Error: Could not convert suite output to a number.") print(f"Error: Could not convert suite output to a number.")
print(f"Received: '{suite_result.stdout}'") print(f"Received: '{suite_result.stdout}'")

View file

@ -3,6 +3,7 @@ use crate::r#move::Move;
use crate::search::alpha_beta; use crate::search::alpha_beta;
use crate::tt::TranspositionTable; // Import TT use crate::tt::TranspositionTable; // Import TT
use std::time::{Instant, Duration}; use std::time::{Instant, Duration};
use crate::uci::UCI;
pub struct Engine { pub struct Engine {
pub name: String, pub name: String,
@ -44,6 +45,7 @@ impl Engine {
let mut nodes = 0; let mut nodes = 0;
// Initial search at depth 1
let (mut opt_move, mut _score) = alpha_beta( let (mut opt_move, mut _score) = alpha_beta(
&mut self.board, &mut self.board,
1, 1,
@ -56,8 +58,12 @@ impl Engine {
&mut self.tt &mut self.tt
); );
// If we timed out immediately at depth 1 (very rare), opt_move might be None.
// But usually, we have at least one move here.
let mut depth = 2; let mut depth = 2;
// Iterative Deepening
while start_time.elapsed() < time_limit { while start_time.elapsed() < time_limit {
let (new_move, new_score) = alpha_beta( let (new_move, new_score) = alpha_beta(
&mut self.board, &mut self.board,
@ -71,19 +77,27 @@ impl Engine {
&mut self.tt &mut self.tt
); );
if start_time.elapsed() > time_limit { // FIX: Only update if we actually got a move back (didn't timeout)
if let Some(mv) = new_move {
opt_move = Some(mv);
_score = new_score;
// Optional: Move send_info here to update the GUI after every completed depth
UCI::send_info(Some(depth as usize), Some(nodes), None, None, None, None);
} else {
// If new_move is None, the search was aborted due to time.
// We discard the partial results and stop increasing depth.
break; break;
} }
opt_move = new_move;
_score = new_score;
depth += 1; depth += 1;
} }
if let Some(mv) = opt_move { if let Some(mv) = opt_move {
mv.to_algebraic() mv.to_algebraic()
} else { } else {
// Fallback: If even depth 1 failed (e.g. 0ms time limit), try to return *any* legal move
// or just return null if truly nothing works.
"null".to_string() "null".to_string()
} }
} }

View file

@ -1,7 +1,8 @@
use chess_engine::engine::Engine; use chess_engine::engine::Engine;
use chess_engine::uci::uci_mainloop; use chess_engine::uci::UCI;
fn main() { fn main() {
let mut engine = Engine::new("Yakari".to_string(), "EiSiMo".to_string()); let mut engine = Engine::new("Yakari".to_string(), "EiSiMo".to_string());
uci_mainloop(&mut engine); let mut uci = UCI { engine };
uci.uci_mainloop();
} }

View file

@ -1,6 +1,6 @@
use crate::board::Board; use crate::board::Board;
use crate::movegen::non_sliders::{generate_king_moves, generate_knight_moves}; use crate::movegen::non_sliders::*;
use crate::movegen::pawns::generate_pawn_moves; use crate::movegen::pawns::*;
use crate::movegen::sliders::*; use crate::movegen::sliders::*;
use crate::r#move::{Move, MoveList}; use crate::r#move::{Move, MoveList};
@ -30,12 +30,12 @@ impl GenStage {
} }
} }
pub struct MoveGenerator { pub struct MovePicker {
buffer: MoveList, buffer: MoveList,
stage: GenStage, stage: GenStage,
} }
impl MoveGenerator { impl MovePicker {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
buffer: MoveList::new(), buffer: MoveList::new(),

View file

@ -2,7 +2,7 @@ use crate::board::{Board, Color};
use crate::eval::evaluate_board; use crate::eval::evaluate_board;
use crate::movegen::legal_check::*; use crate::movegen::legal_check::*;
use crate::r#move::Move; use crate::r#move::Move;
use crate::movegen::picker::MoveGenerator; use crate::movegen::picker::MovePicker;
use crate::tt::{TranspositionTable, NodeType}; use crate::tt::{TranspositionTable, NodeType};
use std::time::{Instant, Duration}; use std::time::{Instant, Duration};
@ -90,7 +90,7 @@ pub fn alpha_beta(
let mut legal_moves_found = false; let mut legal_moves_found = false;
let alpha_orig = alpha; let alpha_orig = alpha;
let mut picker = MoveGenerator::new(); let mut picker = MovePicker::new();
let mut moves_tried = 0; let mut moves_tried = 0;
loop { loop {

View file

@ -1,7 +1,13 @@
use std::io::{self, BufRead}; use std::io::{self, BufRead};
use crate::engine::Engine; use crate::engine::Engine;
pub fn uci_mainloop(engine: &mut Engine) { pub struct UCI {
// TODO lifetime specifier
pub engine: Engine,
}
impl UCI {
pub fn uci_mainloop(&mut self) {
loop { loop {
for line in io::stdin().lock().lines() { for line in io::stdin().lock().lines() {
let input = line.unwrap_or_else(|_| "quit".to_string()); let input = line.unwrap_or_else(|_| "quit".to_string());
@ -13,8 +19,8 @@ pub fn uci_mainloop(engine: &mut Engine) {
match tokens[0] { match tokens[0] {
"uci" => { "uci" => {
println!("id name {}", engine.name); println!("id name {}", self.engine.name);
println!("id author {}", engine.author); println!("id author {}", self.engine.author);
println!("uciok"); println!("uciok");
} }
"isready" => { "isready" => {
@ -27,18 +33,18 @@ pub fn uci_mainloop(engine: &mut Engine) {
if tokens.len() > 1 { if tokens.len() > 1 {
if tokens[1] == "fen" { if tokens[1] == "fen" {
let fen = tokens[2..].join(" "); let fen = tokens[2..].join(" ");
engine.setpos_fen(&fen); self.engine.setpos_fen(&fen);
} else if tokens[1] == "startpos" { } else if tokens[1] == "startpos" {
if tokens.len() > 2 && tokens[2] == "moves" { if tokens.len() > 2 && tokens[2] == "moves" {
engine.setpos_startpos(&tokens[3..]); self.engine.setpos_startpos(&tokens[3..]);
} else { } else {
engine.setpos_startpos(&[]); self.engine.setpos_startpos(&[]);
} }
} }
} }
} }
"go" => { "go" => {
println!("bestmove {}", engine.search(1000_u64)); println!("bestmove {}", self.engine.search(1000_u64));
} }
"stop" => { "stop" => {
// TODO stop search as soon as possible // TODO stop search as soon as possible
@ -53,3 +59,41 @@ pub fn uci_mainloop(engine: &mut Engine) {
} }
} }
} }
pub fn send_info(
depth: Option<usize>,
nodes: Option<u64>,
time: Option<u64>,
nps: Option<u64>,
score_cp: Option<i32>,
pv: Option<&str>
) {
let mut output = String::from("info");
if let Some(d) = depth {
output.push_str(&format!(" depth {}", d));
}
if let Some(s) = score_cp {
output.push_str(&format!(" score cp {}", s));
}
if let Some(n) = nodes {
output.push_str(&format!(" nodes {}", n));
}
if let Some(n) = nps {
output.push_str(&format!(" nps {}", n));
}
if let Some(t) = time {
output.push_str(&format!(" time {}", t));
}
if let Some(p) = pv {
output.push_str(&format!(" pv {}", p));
}
println!("{}", output);
}
}

View file

@ -1,13 +1,13 @@
use chess_engine::board::Board; use chess_engine::board::Board;
use chess_engine::movegen::legal_check::is_other_king_attacked; use chess_engine::movegen::legal_check::is_other_king_attacked;
use chess_engine::movegen::picker::MoveGenerator; use chess_engine::movegen::picker::MovePicker;
fn count_legal_moves_recursive(board: &mut Board, depth: u8) -> u64 { fn count_legal_moves_recursive(board: &mut Board, depth: u8) -> u64 {
if depth == 0 { if depth == 0 {
return 1_u64; return 1_u64;
} }
let mut generator = MoveGenerator::new(); let mut generator = MovePicker::new();
let mut leaf_nodes = 0_u64; let mut leaf_nodes = 0_u64;
while let Some(mv) = generator.next(board) { while let Some(mv) = generator.next(board) {