use std::convert::TryFrom;
use std::time::Instant;
use crate::coretypes::{Color, PlyKind};
use crate::error::{self, ErrorKind};
use crate::uci::SearchControls;
const TIME_RATIO: u32 = 15; const OVERHEAD: u128 = 10;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Mode {
Infinite, Standard(Standard), Depth(Depth), MoveTime(MoveTime), }
impl Mode {
pub fn stop(&self, root_player: Color, ply: PlyKind) -> bool {
match self {
Mode::Infinite => Infinite::stop(),
Mode::Depth(depth_mode) => depth_mode.stop(ply),
Mode::MoveTime(movetime_mode) => movetime_mode.stop(ply),
Mode::Standard(standard_mode) => standard_mode.stop(root_player, ply),
}
}
pub fn infinite() -> Self {
Self::Infinite
}
pub fn depth(ply: PlyKind, movetime: Option<u32>) -> Self {
Self::Depth(Depth {
depth: ply,
instant: Instant::now(),
movetime,
})
}
pub fn movetime(movetime: u32, ply: Option<PlyKind>) -> Self {
Self::MoveTime(MoveTime {
movetime,
instant: Instant::now(),
depth: ply,
})
}
pub fn standard(
wtime: i32,
btime: i32,
winc: Option<u32>,
binc: Option<u32>,
moves_to_go: Option<u32>,
ply: Option<PlyKind>,
) -> Self {
Self::Standard(Standard {
wtime,
btime,
winc,
binc,
moves_to_go,
depth: ply,
instant: Instant::now(),
})
}
}
impl TryFrom<SearchControls> for Mode {
type Error = error::Error;
fn try_from(controls: SearchControls) -> error::Result<Self> {
if Infinite::satisfied(&controls) {
Ok(Mode::Infinite)
} else if Standard::satisfied(&controls) {
Ok(Mode::standard(
controls.wtime.unwrap(),
controls.btime.unwrap(),
controls.winc,
controls.binc,
controls.moves_to_go,
controls.depth,
))
} else if MoveTime::satisfied(&controls) {
Ok(Mode::movetime(controls.move_time.unwrap(), controls.depth))
} else if Depth::satisfied(&controls) {
Ok(Mode::depth(controls.depth.unwrap(), controls.move_time))
} else {
Err(ErrorKind::ModeNotSatisfied.into())
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Infinite;
impl Infinite {
fn stop() -> bool {
false
}
fn satisfied(search_controls: &SearchControls) -> bool {
search_controls.infinite
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Depth {
pub depth: PlyKind,
instant: Instant,
movetime: Option<u32>,
}
impl Depth {
fn stop(&self, ply: PlyKind) -> bool {
if ply > self.depth {
return true;
}
if let Some(movetime) = self.movetime {
let elapsed_ms = self.instant.elapsed().as_millis();
if elapsed_ms >= (movetime as u128).saturating_sub(OVERHEAD) {
return true;
}
}
return false;
}
fn satisfied(search_controls: &SearchControls) -> bool {
search_controls.depth.is_some()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct MoveTime {
movetime: u32,
instant: Instant,
depth: Option<PlyKind>,
}
impl MoveTime {
fn stop(&self, ply: PlyKind) -> bool {
let elapsed_ms = self.instant.elapsed().as_millis();
if elapsed_ms >= (self.movetime as u128).saturating_sub(OVERHEAD) {
return true;
}
if let Some(depth) = self.depth {
if ply > depth {
return true;
}
}
return false;
}
fn satisfied(search_controls: &SearchControls) -> bool {
search_controls.move_time.is_some()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Standard {
instant: Instant,
wtime: i32,
btime: i32,
winc: Option<u32>,
binc: Option<u32>,
moves_to_go: Option<u32>,
depth: Option<PlyKind>,
}
impl Standard {
fn stop(&self, root_player: Color, ply: PlyKind) -> bool {
let target_elapsed = self.target_elapsed_ms(root_player);
let elapsed_ms = self.instant.elapsed().as_millis();
if elapsed_ms >= target_elapsed {
return true;
}
if let Some(depth) = self.depth {
if ply > depth {
return true;
}
}
false
}
fn target_elapsed_ms(&self, root_player: Color) -> u128 {
let remaining_time = match root_player {
Color::White => self.wtime,
Color::Black => self.btime,
};
let remaining_time: u128 = if remaining_time.is_negative() {
0
} else {
remaining_time as u128
};
(remaining_time / TIME_RATIO as u128).saturating_sub(OVERHEAD)
}
fn satisfied(search_controls: &SearchControls) -> bool {
search_controls.wtime.is_some() && search_controls.btime.is_some()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn standard() {
let mut controls = SearchControls::default();
controls.wtime = Some(5000);
controls.btime = Some(5000);
let mode = Mode::try_from(controls);
assert!(mode.is_ok());
let mode = mode.unwrap();
assert!(matches!(mode, Mode::Standard(_)));
}
}