Add uci movetime, hash and initialize TT based on size in MBs
This commit is contained in:
@@ -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],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user