Add uci movetime, hash and initialize TT based on size in MBs

This commit is contained in:
stefiosif
2025-02-04 23:53:18 +02:00
parent 908a87bd26
commit b98100aa71
5 changed files with 75 additions and 23 deletions

View File

@@ -2,7 +2,7 @@ use crate::board::board::{Board, Color, PieceType};
use crate::board::game::Game;
use crate::board::state::{Castle, State};
use crate::search::transposition_table::TranspositionTable;
use crate::search::{MAX_DEPTH, MAX_TT_SIZE};
use crate::search::MAX_DEPTH;
use String as FenError;
pub fn from_fen(fen: &str) -> Result<Game, FenError> {
@@ -36,7 +36,7 @@ pub fn from_fen(fen: &str) -> Result<Game, FenError> {
history: History::new(),
mailbox,
hash,
tt: TranspositionTable::new(MAX_TT_SIZE),
tt: TranspositionTable::new(),
killer: [None; MAX_DEPTH as usize],
})
}

View File

@@ -1,7 +1,7 @@
use crate::{
board::fen::from_fen,
movegen::r#move::{Move, MoveType},
search::{transposition_table::TranspositionTable, MAX_DEPTH, MAX_TT_SIZE},
search::{transposition_table::TranspositionTable, MAX_DEPTH},
};
use String as FenError;
@@ -39,7 +39,7 @@ impl Game {
history: History::new(),
mailbox: Mailbox::from_board(&Board::startpos()),
hash: zobrist_keys().calculate_hash(&Board::startpos()),
tt: TranspositionTable::new(MAX_TT_SIZE),
tt: TranspositionTable::new(),
killer: [None; MAX_DEPTH as usize],
}
}

View File

@@ -1,4 +1,5 @@
use std::{
collections::HashMap,
io::{BufRead, Write},
str::SplitWhitespace,
};
@@ -13,7 +14,7 @@ use crate::{
movegen::r#move::Move,
search::{
iterative_deepening, time::TimeInfo, transposition_table::TranspositionTable, INC,
MAX_DEPTH, MAX_TT_SIZE, TIME,
MAX_DEPTH, TIME,
},
};
@@ -26,6 +27,7 @@ pub enum Command {
Go,
Stop,
Quit,
SetOption,
}
fn parse_command(parts: &mut SplitWhitespace) -> anyhow::Result<Command> {
@@ -37,6 +39,7 @@ fn parse_command(parts: &mut SplitWhitespace) -> anyhow::Result<Command> {
Some("go") => Ok(Command::Go),
Some("stop") => Ok(Command::Stop),
Some("quit") => Ok(Command::Quit),
Some("setoption") => Ok(Command::SetOption),
_ => bail!("Unrecognised command"),
}
}
@@ -75,10 +78,11 @@ struct UciParameters {
btime: Option<u128>,
winc: Option<u128>,
binc: Option<u128>,
options: HashMap<String, String>,
}
impl UciParameters {
const fn new() -> Self {
fn new() -> Self {
Self {
depth: None,
game: None,
@@ -86,9 +90,15 @@ impl UciParameters {
btime: None,
winc: None,
binc: None,
options: HashMap::new(),
}
}
fn add_move_time(&mut self, movetime: u128) {
self.wtime = Some(movetime);
self.btime = Some(movetime);
}
fn add_depth(&mut self, depth: u8) {
self.depth = Some(depth);
}
@@ -112,6 +122,10 @@ impl UciParameters {
fn add_game(&mut self, game: Game) {
self.game = Some(game);
}
fn add_option(&mut self, name: String, value: String) {
self.options.insert(name, value);
}
}
impl Default for UciParameters {
@@ -156,6 +170,7 @@ pub fn uci_go(go_iter: &mut SplitWhitespace, game: &mut Game) -> anyhow::Result<
"winc" => params.add_winc(parse_next(go_iter, "winc")?),
"binc" => params.add_binc(parse_next(go_iter, "binc")?),
"depth" => params.add_depth(parse_next(go_iter, "depth")?),
"movetime" => params.add_move_time(parse_next(go_iter, "movetime")?),
_ => (),
}
}
@@ -173,9 +188,34 @@ pub fn uci_go(go_iter: &mut SplitWhitespace, game: &mut Game) -> anyhow::Result<
.ok_or_else(|| anyhow!("No stored best move found"))
}
fn parse_next<T: std::str::FromStr>(go_iter: &mut SplitWhitespace, val: &str) -> anyhow::Result<T> {
go_iter
.next()
fn uci_option(option_iter: &mut SplitWhitespace, game: &mut Game) -> anyhow::Result<()> {
let mut params = UciParameters::new();
//TODO: Implement the rest of the options and refactor
while let Some(subcommand) = option_iter.next() {
if subcommand == "name" {
let name: String = parse_next(option_iter, "name")?;
option_iter.next();
let value: String = parse_next(option_iter, "value")?;
params.add_option(name, value);
}
}
if let Some(name) = params.options.get("name") {
if name == "Hash" {
let value = params.options.get("value").expect("Expected key value");
let size = value
.parse::<usize>()
.map_err(|_| anyhow!("Invalid value for Hash"))?;
game.tt = TranspositionTable::new_with_mb_size(size);
}
}
Ok(())
}
fn parse_next<T: std::str::FromStr>(iter: &mut SplitWhitespace, val: &str) -> anyhow::Result<T> {
iter.next()
.ok_or_else(|| anyhow!("Expected {val}"))
.and_then(|v| v.parse::<T>().map_err(|_| anyhow!("Invalid {val}")))
}
@@ -192,18 +232,14 @@ pub fn uci_loop<R: BufRead, W: Write>(input: R, mut output: W) -> anyhow::Result
Command::IsReady => write_response(&mut output, &Response::ReadyOk)?,
Command::UciNewGame => {
if let Some(game) = params.game.as_mut() {
game.tt = TranspositionTable::new(MAX_TT_SIZE);
game.tt = TranspositionTable::new();
game.board = Board::startpos();
} else {
let game = Game::new();
params.add_game(game);
}
write_response(&mut output, &Response::Info("Clear cache".to_string()))?;
}
Command::Position => {
//TODO: doesnt have to create a new game every time, we can just update the game
params.add_game(uci_position(&mut parts)?);
}
Command::Position => params.add_game(uci_position(&mut parts)?),
Command::Go => {
if let Some(game) = params.game.as_mut() {
match uci_go(&mut parts, game) {
@@ -222,6 +258,14 @@ pub fn uci_loop<R: BufRead, W: Write>(input: R, mut output: W) -> anyhow::Result
}
Command::Stop => break,
Command::Quit => break,
Command::SetOption => {
if let Some(game) = params.game.as_mut() {
uci_option(&mut parts, game)?;
} else {
let game = Game::new();
params.add_game(game);
};
}
};
output.flush().map_err(|e| anyhow!(e))?
@@ -299,7 +343,6 @@ mod tests {
let expected_response = "id name zeal\n\
id author stefiosif\n\
uciok\n\
Clear cache\n\
bestmove e3f2\n";
let actual_response = String::from_utf8(output).expect("Invalid UTF-8 in output");

View File

@@ -11,4 +11,4 @@ pub const TIME: u128 = 8000;
pub const INC: u128 = 80;
pub const HARD_LIMIT_DIVISION: u128 = 3;
pub const SOFT_LIMIT_DIVISION: u128 = 20;
pub const MAX_TT_SIZE: u64 = 1500000;
pub const TT_SIZE_IN_MB: usize = 64;

View File

@@ -1,5 +1,7 @@
use crate::{board::zobrist::ZobristHash, movegen::r#move::Move};
use super::TT_SIZE_IN_MB;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TranspositionTable {
positions: Vec<Option<TTEntry>>,
@@ -7,10 +9,17 @@ pub struct TranspositionTable {
}
impl TranspositionTable {
pub fn new(size: u64) -> Self {
pub fn new() -> Self {
Self::new_with_mb_size(TT_SIZE_IN_MB)
}
pub fn new_with_mb_size(size_in_mega_bytes: usize) -> Self {
let size_in_bytes = size_in_mega_bytes * 1024 * 1024;
let num_of_entries = size_in_bytes / std::mem::size_of::<TTEntry>();
Self {
positions: vec![None; size as usize],
size,
positions: vec![None; num_of_entries],
size: num_of_entries as u64,
}
}
@@ -66,7 +75,7 @@ mod tests {
board::fen::from_fen,
evaluation::{MAX_SCORE, MIN_SCORE},
movegen::attack_generator::init_attacks,
search::{negamax::negamax, time::TimeInfo},
search::{negamax::negamax, time::TimeInfo, INC, TIME},
};
const FEN: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R w KQk g6 0 1";
@@ -77,10 +86,10 @@ mod tests {
fn test_transposition_table() -> anyhow::Result<()> {
init_attacks();
let mut game = from_fen(FEN).unwrap();
let time_info = TimeInfo::new(std::time::Instant::now(), 30000, 100);
let time_info = TimeInfo::new(std::time::Instant::now(), TIME, INC);
negamax(
&mut game, MIN_SCORE, MAX_SCORE, 2, 0, &time_info, &mut 0, true,
&mut game, MIN_SCORE, MAX_SCORE, 3, 0, &time_info, &mut 0, true,
)
.expect("Expected a search result");