send uci info
This commit is contained in:
parent
d5b679b6bf
commit
711569b8e5
8 changed files with 118 additions and 59 deletions
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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}'")
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
126
src/uci.rs
126
src/uci.rs
|
|
@ -1,55 +1,99 @@
|
||||||
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 {
|
||||||
loop {
|
// TODO lifetime specifier
|
||||||
for line in io::stdin().lock().lines() {
|
pub engine: Engine,
|
||||||
let input = line.unwrap_or_else(|_| "quit".to_string());
|
}
|
||||||
let tokens: Vec<&str> = input.split_whitespace().collect();
|
|
||||||
|
|
||||||
if tokens.is_empty() {
|
impl UCI {
|
||||||
continue;
|
pub fn uci_mainloop(&mut self) {
|
||||||
}
|
loop {
|
||||||
|
for line in io::stdin().lock().lines() {
|
||||||
|
let input = line.unwrap_or_else(|_| "quit".to_string());
|
||||||
|
let tokens: Vec<&str> = input.split_whitespace().collect();
|
||||||
|
|
||||||
match tokens[0] {
|
if tokens.is_empty() {
|
||||||
"uci" => {
|
continue;
|
||||||
println!("id name {}", engine.name);
|
|
||||||
println!("id author {}", engine.author);
|
|
||||||
println!("uciok");
|
|
||||||
}
|
}
|
||||||
"isready" => {
|
|
||||||
println!("readyok");
|
match tokens[0] {
|
||||||
}
|
"uci" => {
|
||||||
"ucinewgame" => {
|
println!("id name {}", self.engine.name);
|
||||||
// not yet implemented
|
println!("id author {}", self.engine.author);
|
||||||
}
|
println!("uciok");
|
||||||
"position" => {
|
}
|
||||||
if tokens.len() > 1 {
|
"isready" => {
|
||||||
if tokens[1] == "fen" {
|
println!("readyok");
|
||||||
let fen = tokens[2..].join(" ");
|
}
|
||||||
engine.setpos_fen(&fen);
|
"ucinewgame" => {
|
||||||
} else if tokens[1] == "startpos" {
|
// not yet implemented
|
||||||
if tokens.len() > 2 && tokens[2] == "moves" {
|
}
|
||||||
engine.setpos_startpos(&tokens[3..]);
|
"position" => {
|
||||||
} else {
|
if tokens.len() > 1 {
|
||||||
engine.setpos_startpos(&[]);
|
if tokens[1] == "fen" {
|
||||||
|
let fen = tokens[2..].join(" ");
|
||||||
|
self.engine.setpos_fen(&fen);
|
||||||
|
} else if tokens[1] == "startpos" {
|
||||||
|
if tokens.len() > 2 && tokens[2] == "moves" {
|
||||||
|
self.engine.setpos_startpos(&tokens[3..]);
|
||||||
|
} else {
|
||||||
|
self.engine.setpos_startpos(&[]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
"go" => {
|
||||||
"go" => {
|
println!("bestmove {}", self.engine.search(1000_u64));
|
||||||
println!("bestmove {}", engine.search(1000_u64));
|
}
|
||||||
}
|
"stop" => {
|
||||||
"stop" => {
|
// TODO stop search as soon as possible
|
||||||
// TODO stop search as soon as possible
|
}
|
||||||
}
|
"quit" => {
|
||||||
"quit" => {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
_ => {
|
||||||
_ => {
|
// Unknown command, just ignore
|
||||||
// Unknown command, just ignore
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue