Skip to content

Commit

Permalink
make parallel PVS more deterministic
Browse files Browse the repository at this point in the history
  • Loading branch information
brunocodutra committed Sep 14, 2023
1 parent 37aebfa commit 638950f
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 147 deletions.
14 changes: 4 additions & 10 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.score(), pv.depth(), pv.ply(), pv)
Pv::new(pv.score(), pv.depth(), pv)
}

fn with_options(_: Options) -> Self {
Expand Down Expand Up @@ -93,7 +93,7 @@ impl Ai for Engine {
mod tests {
use super::*;
use futures_util::StreamExt;
use lib::search::{Ply, Score};
use lib::search::Score;
use mockall::predicate::eq;
use proptest::sample::size_range;
use std::time::Duration;
Expand All @@ -110,15 +110,9 @@ mod tests {

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

let mut engine = Engine { strategy };
engine.play(&pos, l).await;
Expand Down
6 changes: 6 additions & 0 deletions lib/search/depth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ pub struct DepthBounds;

impl Bounds for DepthBounds {
type Integer = u8;

const LOWER: Self::Integer = 0;

#[cfg(not(test))]
const UPPER: Self::Integer = 31;

#[cfg(test)]
const UPPER: Self::Integer = 3;
}

/// The search depth.
Expand Down
6 changes: 6 additions & 0 deletions lib/search/ply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@ pub struct PlyBounds;

impl Bounds for PlyBounds {
type Integer = i8;

const LOWER: Self::Integer = -Self::UPPER;

#[cfg(not(test))]
const UPPER: Self::Integer = 127;

#[cfg(test)]
const UPPER: Self::Integer = 3;
}

/// The number of half-moves played.
Expand Down
79 changes: 5 additions & 74 deletions lib/search/pv.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::search::{Depth, DepthBounds, Line, Ply, Score};
use crate::search::{Depth, DepthBounds, Line, Score};
use crate::{chess::Move, util::Bounds};
use derive_more::{Deref, IntoIterator};
use std::{cmp::Ordering, iter::once, mem, ops::Neg};
Expand All @@ -11,42 +11,21 @@ use test_strategy::Arbitrary;
pub struct Pv<const N: usize = { DepthBounds::UPPER as _ }> {
score: Score,
depth: Depth,
#[filter(#ply >= 0)]
ply: Ply,
#[deref]
#[into_iterator(owned, ref, ref_mut)]
line: Line<N>,
}

impl<const N: usize> Pv<N> {
/// Constructs a pv.
pub fn new<I>(score: Score, depth: Depth, ply: Ply, line: I) -> Self
where
I: IntoIterator<Item = Move>,
{
pub fn new<I: IntoIterator<Item = Move>>(score: Score, depth: Depth, line: I) -> Self {
Pv {
score,
depth,
ply,
line: line.into_iter().collect(),
line: Line::from_iter(line),
}
}

/// Constructs a pv leaf.
pub fn leaf(score: Score, depth: Depth, ply: Ply) -> Self {
Self::new(score, depth, ply, [])
}

/// Constructs a drawn pv leaf.
pub fn drawn(depth: Depth, ply: Ply) -> Self {
Self::leaf(Score::new(0), depth, ply)
}

/// Constructs a lost pv leaf.
pub fn lost(depth: Depth, ply: Ply) -> Self {
Self::leaf(Score::LOWER.normalize(ply), depth, ply)
}

/// The score from the point of view of the side to move.
pub fn score(&self) -> Score {
self.score
Expand All @@ -57,24 +36,6 @@ impl<const N: usize> Pv<N> {
self.depth
}

/// The ply reached.
pub fn ply(&self) -> Ply {
if self.ply < 0 {
-self.ply
} else {
self.ply
}
}

/// The tempo bonus from the point of view of the side to move.
pub fn tempo(&self) -> Ply {
if self.ply < 0 {
-(self.ply + self.depth)
} else {
-(self.ply - self.depth)
}
}

/// The strongest [`Line`].
pub fn line(&self) -> &Line<N> {
&self.line
Expand All @@ -90,7 +51,7 @@ impl<const N: usize> Pv<N> {

impl<const N: usize> Ord for Pv<N> {
fn cmp(&self, other: &Self) -> Ordering {
(self.score(), self.tempo()).cmp(&(other.score(), other.tempo()))
self.score.cmp(&other.score)
}
}

Expand Down Expand Up @@ -122,7 +83,7 @@ impl<const N: usize> Neg for Pv<N> {
type Output = Self;

fn neg(self) -> Self::Output {
Pv::new(-self.score, self.depth, -self.ply, self.line)
Pv::new(-self.score, self.depth, self.line)
}
}

Expand All @@ -141,11 +102,6 @@ mod tests {
assert_eq!(pv.depth(), pv.depth);
}

#[proptest]
fn ply_returns_ply(pv: Pv<3>) {
assert_eq!(pv.ply().get(), pv.ply.get().abs());
}

#[proptest]
fn line_returns_line(pv: Pv<3>) {
assert_eq!(pv.line(), &pv.line);
Expand All @@ -156,21 +112,11 @@ mod tests {
assert_eq!(pv.clone().neg().score(), -pv.score());
}

#[proptest]
fn negation_changes_tempo(#[filter(#pv.ply() > 0)] pv: Pv<3>) {
assert_eq!(pv.clone().neg().tempo(), -pv.tempo());
}

#[proptest]
fn negation_preserves_depth(pv: Pv<3>) {
assert_eq!(pv.clone().neg().depth(), pv.depth());
}

#[proptest]
fn negation_preserves_ply(pv: Pv<3>) {
assert_eq!(pv.clone().neg().ply(), pv.ply());
}

#[proptest]
fn negation_preserves_line(pv: Pv<3>) {
assert_eq!(pv.clone().neg().line(), pv.line());
Expand All @@ -190,19 +136,4 @@ mod tests {
fn pv_with_larger_score_is_larger(p: Pv<3>, #[filter(#p.score() != #q.score())] q: Pv<3>) {
assert_eq!(p < q, p.score() < q.score());
}

#[proptest]
fn pvs_with_same_score_are_compared_by_tempo(
s: Score,
dp: Depth,
dq: Depth,
pp: Ply,
pq: Ply,
lp: Line<3>,
lq: Line<3>,
) {
let p = Pv::<3>::new(s, dp, pp, lp);
let q = Pv::<3>::new(s, dq, pq, lq);
assert_eq!(p < q, p.tempo() < q.tempo());
}
}
Loading

0 comments on commit 638950f

Please sign in to comment.