Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: open-bench crashes #17

Merged
merged 26 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5a29b33
chore: make repetition count check use a constant
DeveloperPaul123 Nov 13, 2024
4b8dd33
chore: change repetition check back to 2
DeveloperPaul123 Nov 13, 2024
57f8000
fix: remove useless info print
DeveloperPaul123 Nov 13, 2024
40486ee
fix: improve uci command parse handling
DeveloperPaul123 Nov 13, 2024
363ea50
chore: update dependencies
DeveloperPaul123 Nov 14, 2024
c8f123e
chore: add script to recreate open bench crash
DeveloperPaul123 Nov 14, 2024
ef01e66
chore: add script to translate PGN files to uci moves
DeveloperPaul123 Nov 14, 2024
bc45158
feat: add "max_nodes" for search config
DeveloperPaul123 Nov 14, 2024
e39b3b3
wip: use multiple threads for engine search
DeveloperPaul123 Nov 14, 2024
126d147
chore: clippy suggestions
DeveloperPaul123 Nov 14, 2024
73e42cf
fix: bug getting best move with no time
DeveloperPaul123 Nov 15, 2024
258219a
fix: bug with make_uci_move
DeveloperPaul123 Nov 16, 2024
ad10618
chore: fix minor issues in vs code launch config
DeveloperPaul123 Nov 16, 2024
6af1bde
chore: update dependencies
DeveloperPaul123 Nov 16, 2024
f6f9210
chore: remove unused import
DeveloperPaul123 Nov 16, 2024
df22f31
feat: worker thread helper
DeveloperPaul123 Nov 20, 2024
843bad7
feat: add an input handler
DeveloperPaul123 Nov 20, 2024
22168c0
feat: move search to new thread
DeveloperPaul123 Nov 20, 2024
f45fedd
chore: update and fixes based on search changes
DeveloperPaul123 Nov 20, 2024
86b6b83
chore: show engine banner + info before starting loop
DeveloperPaul123 Nov 21, 2024
28c682d
chore: add missing comment headers
DeveloperPaul123 Nov 21, 2024
27102fb
fix: add way for other threads to exit
DeveloperPaul123 Nov 21, 2024
b546bba
Merge pull request #14 from DeveloperPaul123/feature/multiple-threads
DeveloperPaul123 Nov 21, 2024
0e7767e
chore: update how tt-move scoring is done.
DeveloperPaul123 Nov 21, 2024
b1ac062
chore: update gitignore
DeveloperPaul123 Nov 21, 2024
e69b463
Merge pull request #16 from DeveloperPaul123/feature/tt-move-scoring
DeveloperPaul123 Nov 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
chore: update and fixes based on search changes
Now use `engine.run()` in main and let the engine handle the loop of processing commands and reacting to them.

bench: 1994757
  • Loading branch information
DeveloperPaul123 committed Nov 20, 2024
commit f45fedd57f224a63e96a674391bcc83d0236e450
4 changes: 2 additions & 2 deletions src/bin/byte-knight/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,13 @@ pub(crate) fn bench(depth: u8, epd_file: &Option<String>) {
};

let mut nodes = 0u64;
let mut search = Search::new(config);
let mut search = Search::new(&config);

for bench in benchmark_strings {
let fen: &str = bench.split(";").next().unwrap();
let mut board = Board::from_fen(fen).unwrap();

let result = search.search(&mut board);
let result = search.search(&mut board, None);
nodes += result.nodes;
}

Expand Down
111 changes: 23 additions & 88 deletions src/bin/byte-knight/engine.rs
Original file line number Diff line number Diff line change
@@ -1,74 +1,35 @@
use std::{
io::{self, BufRead, Write},
str::FromStr,
sync::mpsc::{self, Receiver, Sender},
};

use chess::{board::Board, move_generation::MoveGenerator, moves::Move, pieces::SQUARE_NAME};
use uci_parser::{UciCommand, UciInfo, UciMove, UciOption, UciResponse};

use crate::{defs::About, search::SearchParameters};

use super::search;

fn square_index_to_uci_square(square: u8) -> uci_parser::Square {
uci_parser::Square::from_str(SQUARE_NAME[square as usize]).unwrap()
}
use std::io::{self, Write};

fn move_to_uci_move(mv: &Move) -> UciMove {
let promotion = mv.promotion_piece().map(|p| p.as_char());
use chess::board::Board;
use uci_parser::{UciCommand, UciInfo, UciOption, UciResponse};

match promotion {
Some(promotion) => UciMove {
src: square_index_to_uci_square(mv.from()),
dst: square_index_to_uci_square(mv.to()),
promote: Some(uci_parser::Piece::from_str(&promotion.to_string()).unwrap()),
},
None => UciMove {
src: square_index_to_uci_square(mv.from()),
dst: square_index_to_uci_square(mv.to()),
promote: None,
},
}
}
use crate::{
defs::About, input_handler::InputHandler, search::SearchParameters, search_thread::SearchThread,
};

pub struct ByteKnight {
sender: Sender<UciCommand>,
receiver: Receiver<UciCommand>,
move_gen: MoveGenerator,
input_handler: InputHandler,
search_thread: SearchThread,
}

impl ByteKnight {
pub fn new() -> ByteKnight {
let (sender, receiver) = mpsc::channel();
ByteKnight {
sender,
receiver,
move_gen: MoveGenerator::new(),
input_handler: InputHandler::new(),
search_thread: SearchThread::new(),
}
}

pub fn run(self: &mut Self, stdin: io::Stdin) -> anyhow::Result<()> {
pub fn run(&mut self) -> anyhow::Result<()> {
// spawn thread to handle UCI commands
let sender = self.sender.clone();
std::thread::spawn(move || {
let mut input = stdin.lock().lines();
loop {
if let Some(Ok(line)) = input.next() {
let command = UciCommand::from_str(line.as_str());
if let Ok(command) = command {
sender.send(command).unwrap();
}
}
}
});
let stdout = io::stdout();
let mut board = Board::default_board();
while let Ok(command) = self.receiver.recv() {
'engine_loop: while let Ok(command) = &self.input_handler.receiver().recv() {
let mut stdout = stdout.lock();
match command {
UciCommand::Quit => {
!todo!("Implement a way to interupt the engine if we're searching");
self.input_handler.stop();
break 'engine_loop;
}
UciCommand::IsReady => {
writeln!(stdout, "{}", UciResponse::<String>::ReadyOk).unwrap();
Expand Down Expand Up @@ -108,49 +69,23 @@ impl ByteKnight {
}
}
UciCommand::Go(search_options) => {
let search_params = SearchParameters::new(&search_options, &board);

let board_info =
UciInfo::default().string(format!("searching {}", board.to_fen()));
writeln!(
stdout,
"{}",
UciResponse::<String>::Info(Box::new(board_info))
)
.unwrap();

let best_move = self.think(&mut board, &search_params);
let info = UciInfo::default().string(format!("searching {}", board.to_fen()));
writeln!(stdout, "{}", UciResponse::info(info)).unwrap();

if let Some(bot_move) = best_move {
writeln!(
stdout,
"{}",
// TODO: Ponder
UciResponse::BestMove {
bestmove: Some(move_to_uci_move(&bot_move).to_string()),
ponder: None
}
)
.unwrap();
} else {
// TODO Handle the case when best_move is None.
}
// create the search parameters
let search_params = SearchParameters::new(&search_options, &board);
// send them and the current board to the search thread
self.search_thread.start_search(&board, search_params);
}
UciCommand::Stop => {
self.search_thread.stop_search();
}
_ => {}
}
}

Ok(())
}
pub(crate) fn think(
&mut self,
board: &mut Board,
search_params: &SearchParameters,
) -> Option<Move> {
let mut search = search::Search::new(*search_params);
let result = search.search(board);
result.best_move
}
}

impl Default for ByteKnight {
Expand Down
115 changes: 11 additions & 104 deletions src/bin/byte-knight/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,21 @@ mod bench;
mod defs;
mod engine;
mod evaluation;
mod input_handler;
mod psqt;
mod score;
mod search;
mod search_thread;
mod tt_table;
mod worker_thread;

use defs::About;
use engine::ByteKnight;
use search::SearchParameters;
use uci_parser::{UciCommand, UciInfo, UciMove, UciOption, UciResponse};

use std::{process::exit, str::FromStr};
use std::process::exit;

use chess::{board::Board, moves::Move, pieces::SQUARE_NAME};
use clap::{Parser, Subcommand};
use std::io::{self, BufRead, Write};
use std::io::{self, Write};

#[derive(Parser)]
#[command(
Expand All @@ -54,107 +54,14 @@ enum Command {
},
}

fn square_index_to_uci_square(square: u8) -> uci_parser::Square {
uci_parser::Square::from_str(SQUARE_NAME[square as usize]).unwrap()
}

fn move_to_uci_move(mv: &Move) -> UciMove {
let promotion = mv.promotion_piece().map(|p| p.as_char());

match promotion {
Some(promotion) => UciMove {
src: square_index_to_uci_square(mv.from()),
dst: square_index_to_uci_square(mv.to()),
promote: Some(uci_parser::Piece::from_str(&promotion.to_string()).unwrap()),
},
None => UciMove {
src: square_index_to_uci_square(mv.from()),
dst: square_index_to_uci_square(mv.to()),
promote: None,
},
}
}

fn run_uci() {
let stdin: io::Stdin = io::stdin();
let stdout = io::stdout();
let mut stdout = stdout.lock();
let mut input = stdin.lock().lines();
let mut board = Board::default_board();

let mut engine = ByteKnight::new();
writeln!(stdout, "{}", About::BANNER).unwrap();

loop {
if let Some(Ok(line)) = input.next() {
let command = UciCommand::from_str(line.as_str());
match command {
Ok(UciCommand::Uci) => {
let id = UciResponse::Id {
name: About::NAME,
author: About::AUTHORS,
};

let options = vec![
UciOption::spin("Hash", 16, 1, 1024),
UciOption::spin("Threads", 1, 1, 1),
];
// TODO: Actually implement the hash option
for option in options {
writeln!(stdout, "{}", UciResponse::Option(option)).unwrap();
}
writeln!(stdout, "{}", id).unwrap();
writeln!(stdout, "{}", UciResponse::<String>::UciOk).unwrap();
}
Ok(UciCommand::Quit) => {
exit(0);
}
Ok(UciCommand::UciNewGame) => {
board = Board::default_board();
}
Ok(UciCommand::IsReady) => {
writeln!(stdout, "{}", UciResponse::<String>::ReadyOk).unwrap();
}
Ok(UciCommand::Position { fen, moves }) => {
match fen {
None => {
board = Board::default_board();
}
Some(fen) => {
board = Board::from_fen(fen.as_str()).unwrap();
}
}

for mv in moves {
board.make_uci_move(&mv.to_string()).unwrap();
}

// TODO: String output of board
// writeln!(stdout, "{}", Board::to_string(&board)).unwrap();
}
Ok(UciCommand::Go(search_options)) => {
let info = UciInfo::default().string(format!("searching {}", board.to_fen()));
writeln!(stdout, "{}", UciResponse::info(info)).unwrap();
let search_params = SearchParameters::new(&search_options, &board);
let best_move = engine.think(&mut board, &search_params);
let move_output = UciResponse::BestMove {
bestmove: best_move.map_or(None, |bot_move| {
Some(move_to_uci_move(&bot_move).to_string())
}),
ponder: None,
};
writeln!(
stdout,
"{}",
// TODO: Ponder
move_output
)
.unwrap();
}
_ => (),
}

stdout.flush().unwrap();
let engine_run_result = engine.run();
match engine_run_result {
Ok(_) => (),
Err(e) => {
writeln!(io::stderr(), "Error running engine: {}", e).unwrap();
exit(1);
}
}
}
Expand Down