Skip to content

Commit

Permalink
decouple PV from TranspositionTable
Browse files Browse the repository at this point in the history
  • Loading branch information
brunocodutra committed Sep 2, 2023
1 parent 7adca76 commit dbe1484
Show file tree
Hide file tree
Showing 11 changed files with 399 additions and 315 deletions.
2 changes: 1 addition & 1 deletion benches/ttm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn ttm(c: &mut Criterion, edps: &[(&str, &str)]) {
|| (Searcher::with_options(options), positions.next().unwrap()),
|(s, (pos, m))| {
let timer = Instant::now();
for d in 1..=DepthBounds::UPPER {
for d in 0..=DepthBounds::UPPER {
let pv = s.search::<1>(pos, Limits::Depth(Depth::new(d)));
if pv.first() == Some(m) || timer.elapsed() >= Duration::from_millis(80) {
break;
Expand Down
26 changes: 15 additions & 11 deletions bin/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ trait Searcher {
impl MockSearcher {
fn search<const N: usize>(&mut self, pos: &Position, limits: Limits) -> Pv<N> {
let pv = Searcher::search(self, pos, limits);
Pv::new(pv.depth(), pv.score(), pv.iter().copied().collect())
Pv::new(pv.score(), pv.depth(), pv.ply(), pv)
}

fn with_options(_: Options) -> Self {
Expand Down Expand Up @@ -72,7 +72,7 @@ impl Ai for Engine {
{
Box::pin(stream! {
let timer = Instant::now();
for d in 1..=limits.depth().get() {
for d in 0..=limits.depth().get() {
let elapsed = timer.elapsed();
if elapsed < limits.time() / 2 {
let depth = Depth::new(d);
Expand All @@ -93,7 +93,7 @@ impl Ai for Engine {
mod tests {
use super::*;
use futures_util::StreamExt;
use lib::search::{Line, Score};
use lib::search::{Ply, Score};
use mockall::predicate::eq;
use proptest::sample::size_range;
use std::time::Duration;
Expand All @@ -110,11 +110,15 @@ mod tests {

#[proptest(async = "tokio")]
#[should_panic]
async fn play_panics_if_there_are_no_legal_moves(l: Limits, pos: Position, d: Depth, s: Score) {
async fn play_panics_if_there_are_no_legal_moves(
l: Limits,
pos: Position,
s: Score,
d: Depth,
p: Ply,
) {
let mut strategy = Strategy::new();
strategy
.expect_search()
.return_const(Pv::new(d, s, Line::default()));
strategy.expect_search().return_const(Pv::new(s, d, p, []));

let mut engine = Engine { strategy };
engine.play(&pos, l).await;
Expand All @@ -123,19 +127,19 @@ mod tests {
#[proptest(async = "tokio")]
async fn analyze_returns_sequence_of_principal_variations(
pos: Position,
#[any(size_range(0..=3).lift())] pvs: Vec<Pv>,
#[any(size_range(1..=3).lift())] pvs: Vec<Pv>,
) {
let mut strategy = Strategy::new();

for (d, pv) in pvs.iter().enumerate() {
strategy
.expect_search()
.with(eq(pos.clone()), eq(Limits::Depth(Depth::saturate(d + 1))))
.with(eq(pos.clone()), eq(Limits::Depth(Depth::saturate(d))))
.return_const(pv.clone());
}

let mut engine = Engine { strategy };
let l = Limits::Depth(Depth::saturate(pvs.len()));
let l = Limits::Depth(Depth::saturate(pvs.len() - 1));
assert_eq!(engine.analyze(&pos, l).collect::<Vec<_>>().await, pvs);
}

Expand All @@ -149,7 +153,7 @@ mod tests {

assert_eq!(
engine.analyze(&pos, l).collect::<Vec<_>>().await,
Vec::new()
Vec::<Pv>::new()
);
}
}
15 changes: 15 additions & 0 deletions lib/chess/bitboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,26 @@ mod tests {
use super::*;
use test_strategy::proptest;

#[proptest]
fn empty_constructs_board_with_no_squares() {
assert_eq!(Bitboard::empty().into_iter().count(), 0);
}

#[proptest]
fn full_constructs_board_with_all_squares() {
assert_eq!(Bitboard::full().into_iter().count(), 64);
}

#[proptest]
fn len_returns_number_of_squares_on_the_board(bb: Bitboard) {
assert_eq!(bb.len(), bb.into_iter().count());
}

#[proptest]
fn is_empty_returns_whether_there_are_squares_on_the_board(bb: Bitboard) {
assert_eq!(bb.is_empty(), bb.into_iter().count() == 0);
}

#[proptest]
fn contains_checks_whether_square_is_on_the_board(bb: Bitboard) {
for s in bb {
Expand Down
38 changes: 14 additions & 24 deletions lib/chess/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub struct ImpossibleExchange(#[error(not(source))] pub Square);
/// The current position on the chess board.
///
/// This type guarantees that it only holds valid positions.
#[derive(DebugCustom, Display, Default, Clone, Eq)]
#[derive(DebugCustom, Display, Default, Clone)]
#[debug(fmt = "Position({self})")]
#[display(
fmt = "{}",
Expand All @@ -50,6 +50,8 @@ impl Hash for Position {
}
}

impl Eq for Position {}

impl PartialEq for Position {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
Expand Down Expand Up @@ -577,30 +579,18 @@ mod tests {
}

#[proptest]
fn exchange_finds_attacker_of_least_value(pos: Position, s: Square) {
match pos.clone().exchange(s) {
Ok(m) => {
let attackers = pos.attackers(s, pos.turn());
assert!(attackers.contains(m.whence()));

assert_eq!(
attackers.into_iter().filter_map(|a| pos.role_on(a)).min(),
Some(m.role()),
);
}

Err(_) => {
if pos[s].is_some() {
for a in pos.attackers(s, pos.turn()) {
let m = Move(a, s, Promotion::None);
assert_eq!(pos.clone().play(m), Err(IllegalMove(m)));
fn exchange_finds_attacker_of_least_value(
pos: Position,
#[filter(#pos.clone().exchange(#s).is_ok())] s: Square,
) {
let m = pos.clone().exchange(s)?;
let attackers = pos.attackers(s, pos.turn());
assert!(attackers.contains(m.whence()));

let m = Move(a, s, Promotion::Queen);
assert_eq!(pos.clone().play(m), Err(IllegalMove(m)));
}
}
}
}
assert_eq!(
attackers.into_iter().filter_map(|a| pos.role_on(a)).min(),
Some(m.role()),
);
}

#[proptest]
Expand Down
7 changes: 0 additions & 7 deletions lib/chess/role.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,6 @@ pub enum Role {
King,
}

impl Role {
/// Returns an iterator over [`Role`]s.
pub fn iter() -> impl DoubleEndedIterator<Item = Self> + ExactSizeIterator {
sm::Role::ALL.into_iter().map(Role::from)
}
}

#[doc(hidden)]
impl From<Role> for sm::Role {
fn from(r: Role) -> Self {
Expand Down
1 change: 0 additions & 1 deletion lib/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#![allow(clippy::arc_with_non_send_sync)]
#![feature(const_maybe_uninit_write, const_mut_refs, const_transmute_copy)]

/// Chess domain types.
Expand Down
19 changes: 13 additions & 6 deletions lib/search/line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@ pub struct Line<const N: usize>(
);

impl<const N: usize> Line<N> {
/// Returns an empty sequence.
pub fn empty() -> Self {
Self::default()
}

/// The number of moves in this sequence.
pub fn len(&self) -> usize {
self.0.len()
Expand All @@ -42,12 +37,24 @@ impl<const N: usize> Line<N> {
}
}

/// Extends a [`Line`] with an iterator of [`Move`]s.
///
/// The sequence might be truncated if the number of moves exceeds the internal capacity.
impl<const N: usize> Extend<Move> for Line<N> {
fn extend<T: IntoIterator<Item = Move>>(&mut self, moves: T) {
let limit = N - self.len();
self.0.extend(moves.into_iter().take(limit));
}
}

/// Create a [`Line`] from an iterator of [`Move`]s.
///
/// The sequence might be truncated if the number of moves exceeds the internal capacity.
impl<const N: usize> FromIterator<Move> for Line<N> {
fn from_iter<I: IntoIterator<Item = Move>>(moves: I) -> Self {
Line(moves.into_iter().take(N).collect())
let mut line = Line::default();
line.extend(moves);
line
}
}

Expand Down
4 changes: 2 additions & 2 deletions lib/search/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ pub struct Options {
/// The size of the transposition table in bytes.
///
/// This is an upper limit, the actual memory allocation may be smaller.
#[strategy(0usize..=1024)]
#[strategy(..=1024usize)]
pub hash: usize,

/// The number of threads to use while searching.
#[strategy((1usize..=4).prop_filter_map("zero", |t| NonZeroUsize::new(t)))]
#[strategy((1..=4usize).prop_filter_map("zero", |t| NonZeroUsize::new(t)))]
pub threads: NonZeroUsize,
}

Expand Down
Loading

0 comments on commit dbe1484

Please sign in to comment.