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::game::Game;
|
||||||
use crate::board::state::{Castle, State};
|
use crate::board::state::{Castle, State};
|
||||||
use crate::search::transposition_table::TranspositionTable;
|
use crate::search::transposition_table::TranspositionTable;
|
||||||
use crate::search::{MAX_DEPTH, MAX_TT_SIZE};
|
use crate::search::MAX_DEPTH;
|
||||||
use String as FenError;
|
use String as FenError;
|
||||||
|
|
||||||
pub fn from_fen(fen: &str) -> Result<Game, 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(),
|
history: History::new(),
|
||||||
mailbox,
|
mailbox,
|
||||||
hash,
|
hash,
|
||||||
tt: TranspositionTable::new(MAX_TT_SIZE),
|
tt: TranspositionTable::new(),
|
||||||
killer: [None; MAX_DEPTH as usize],
|
killer: [None; MAX_DEPTH as usize],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
board::fen::from_fen,
|
board::fen::from_fen,
|
||||||
movegen::r#move::{Move, MoveType},
|
movegen::r#move::{Move, MoveType},
|
||||||
search::{transposition_table::TranspositionTable, MAX_DEPTH, MAX_TT_SIZE},
|
search::{transposition_table::TranspositionTable, MAX_DEPTH},
|
||||||
};
|
};
|
||||||
use String as FenError;
|
use String as FenError;
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ impl Game {
|
|||||||
history: History::new(),
|
history: History::new(),
|
||||||
mailbox: Mailbox::from_board(&Board::startpos()),
|
mailbox: Mailbox::from_board(&Board::startpos()),
|
||||||
hash: zobrist_keys().calculate_hash(&Board::startpos()),
|
hash: zobrist_keys().calculate_hash(&Board::startpos()),
|
||||||
tt: TranspositionTable::new(MAX_TT_SIZE),
|
tt: TranspositionTable::new(),
|
||||||
killer: [None; MAX_DEPTH as usize],
|
killer: [None; MAX_DEPTH as usize],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
io::{BufRead, Write},
|
io::{BufRead, Write},
|
||||||
str::SplitWhitespace,
|
str::SplitWhitespace,
|
||||||
};
|
};
|
||||||
@@ -13,7 +14,7 @@ use crate::{
|
|||||||
movegen::r#move::Move,
|
movegen::r#move::Move,
|
||||||
search::{
|
search::{
|
||||||
iterative_deepening, time::TimeInfo, transposition_table::TranspositionTable, INC,
|
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,
|
Go,
|
||||||
Stop,
|
Stop,
|
||||||
Quit,
|
Quit,
|
||||||
|
SetOption,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_command(parts: &mut SplitWhitespace) -> anyhow::Result<Command> {
|
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("go") => Ok(Command::Go),
|
||||||
Some("stop") => Ok(Command::Stop),
|
Some("stop") => Ok(Command::Stop),
|
||||||
Some("quit") => Ok(Command::Quit),
|
Some("quit") => Ok(Command::Quit),
|
||||||
|
Some("setoption") => Ok(Command::SetOption),
|
||||||
_ => bail!("Unrecognised command"),
|
_ => bail!("Unrecognised command"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,10 +78,11 @@ struct UciParameters {
|
|||||||
btime: Option<u128>,
|
btime: Option<u128>,
|
||||||
winc: Option<u128>,
|
winc: Option<u128>,
|
||||||
binc: Option<u128>,
|
binc: Option<u128>,
|
||||||
|
options: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UciParameters {
|
impl UciParameters {
|
||||||
const fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
depth: None,
|
depth: None,
|
||||||
game: None,
|
game: None,
|
||||||
@@ -86,9 +90,15 @@ impl UciParameters {
|
|||||||
btime: None,
|
btime: None,
|
||||||
winc: None,
|
winc: None,
|
||||||
binc: 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) {
|
fn add_depth(&mut self, depth: u8) {
|
||||||
self.depth = Some(depth);
|
self.depth = Some(depth);
|
||||||
}
|
}
|
||||||
@@ -112,6 +122,10 @@ impl UciParameters {
|
|||||||
fn add_game(&mut self, game: Game) {
|
fn add_game(&mut self, game: Game) {
|
||||||
self.game = Some(game);
|
self.game = Some(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_option(&mut self, name: String, value: String) {
|
||||||
|
self.options.insert(name, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for UciParameters {
|
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")?),
|
"winc" => params.add_winc(parse_next(go_iter, "winc")?),
|
||||||
"binc" => params.add_binc(parse_next(go_iter, "binc")?),
|
"binc" => params.add_binc(parse_next(go_iter, "binc")?),
|
||||||
"depth" => params.add_depth(parse_next(go_iter, "depth")?),
|
"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"))
|
.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> {
|
fn uci_option(option_iter: &mut SplitWhitespace, game: &mut Game) -> anyhow::Result<()> {
|
||||||
go_iter
|
let mut params = UciParameters::new();
|
||||||
.next()
|
|
||||||
|
//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}"))
|
.ok_or_else(|| anyhow!("Expected {val}"))
|
||||||
.and_then(|v| v.parse::<T>().map_err(|_| anyhow!("Invalid {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::IsReady => write_response(&mut output, &Response::ReadyOk)?,
|
||||||
Command::UciNewGame => {
|
Command::UciNewGame => {
|
||||||
if let Some(game) = params.game.as_mut() {
|
if let Some(game) = params.game.as_mut() {
|
||||||
game.tt = TranspositionTable::new(MAX_TT_SIZE);
|
game.tt = TranspositionTable::new();
|
||||||
game.board = Board::startpos();
|
game.board = Board::startpos();
|
||||||
} else {
|
} else {
|
||||||
let game = Game::new();
|
let game = Game::new();
|
||||||
params.add_game(game);
|
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 => {
|
Command::Go => {
|
||||||
if let Some(game) = params.game.as_mut() {
|
if let Some(game) = params.game.as_mut() {
|
||||||
match uci_go(&mut parts, game) {
|
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::Stop => break,
|
||||||
Command::Quit => 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))?
|
output.flush().map_err(|e| anyhow!(e))?
|
||||||
@@ -299,7 +343,6 @@ mod tests {
|
|||||||
let expected_response = "id name zeal\n\
|
let expected_response = "id name zeal\n\
|
||||||
id author stefiosif\n\
|
id author stefiosif\n\
|
||||||
uciok\n\
|
uciok\n\
|
||||||
Clear cache\n\
|
|
||||||
bestmove e3f2\n";
|
bestmove e3f2\n";
|
||||||
let actual_response = String::from_utf8(output).expect("Invalid UTF-8 in output");
|
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 INC: u128 = 80;
|
||||||
pub const HARD_LIMIT_DIVISION: u128 = 3;
|
pub const HARD_LIMIT_DIVISION: u128 = 3;
|
||||||
pub const SOFT_LIMIT_DIVISION: u128 = 20;
|
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 crate::{board::zobrist::ZobristHash, movegen::r#move::Move};
|
||||||
|
|
||||||
|
use super::TT_SIZE_IN_MB;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct TranspositionTable {
|
pub struct TranspositionTable {
|
||||||
positions: Vec<Option<TTEntry>>,
|
positions: Vec<Option<TTEntry>>,
|
||||||
@@ -7,10 +9,17 @@ pub struct TranspositionTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
Self {
|
||||||
positions: vec![None; size as usize],
|
positions: vec![None; num_of_entries],
|
||||||
size,
|
size: num_of_entries as u64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +75,7 @@ mod tests {
|
|||||||
board::fen::from_fen,
|
board::fen::from_fen,
|
||||||
evaluation::{MAX_SCORE, MIN_SCORE},
|
evaluation::{MAX_SCORE, MIN_SCORE},
|
||||||
movegen::attack_generator::init_attacks,
|
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";
|
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<()> {
|
fn test_transposition_table() -> anyhow::Result<()> {
|
||||||
init_attacks();
|
init_attacks();
|
||||||
let mut game = from_fen(FEN).unwrap();
|
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(
|
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");
|
.expect("Expected a search result");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user