Skip to content

Commit

Permalink
fix: Add forgotten files and fix the build
Browse files Browse the repository at this point in the history
  • Loading branch information
kirillbobyrev committed Jun 22, 2024
1 parent 2dc15f6 commit 157c112
Show file tree
Hide file tree
Showing 2 changed files with 247 additions and 0 deletions.
142 changes: 142 additions & 0 deletions src/evaluation/score.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use std::cmp::Ordering;
use std::fmt::Display;
use std::ops::Neg;

/// The score represents the relative value of the position (in centipawns) or
/// checkmate in N moves (if one is found).
///
/// A compact i32 representation is used to store the score in both cases. Since
/// score is stored in [`crate::search::transposition::TranspositionTable`], it
/// is important to keep the size small.
// TODO: Use i16 once the evaluation is NN-based.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Score {
value: i32,
}

impl Score {
pub(crate) const DRAW: Self = Self { value: 0 };
pub(crate) const INFINITY: Self = Self {
value: 2_000_000_000,
};
/// `[-INFINITY, -INFINITY + MATE_RANGE)` and `(INFINITY - MATE_RANGE,
/// INFINITY]` are reserved for mate scores.
/// `[-INFINITY + MATE_RANGE, INFINITY - MATE_RANGE]` if for centipawn
/// evaluations.
const MATE_RANGE: i32 = 1000;

/// Creates a new score in centipawn units. Centipawn units do not mean in
/// terms of NNUE evaluation, but it is convenient for GUIs and UCI
/// purposes, as well as human intepretation.
///
/// The value must be in the range `[-INFINITY + MATE_RANGE, INFINITY -
/// MATE_RANGE]`.
#[must_use]
pub fn cp(value: i32) -> Self {
assert!(value.abs() < Self::INFINITY.value - Self::MATE_RANGE);
Self { value }
}

/// Creates a new score representing player's victory in `moves` *full*
/// moves.
#[must_use]
pub fn mate(moves: u8) -> Self {
Self {
value: Self::INFINITY.value - i32::from(moves),
}
}

/// Returns the number of moves until mate.
///
/// # Panics
///
/// Panics if the score is not a mate score.
#[must_use]
pub fn mate_in(&self) -> i8 {
assert!(self.is_mate());
let moves = Self::INFINITY.value - self.value.abs();
match self.value.cmp(&0) {
Ordering::Greater => moves as i8,
Ordering::Less => -moves as i8,
_ => unreachable!(),
}
}

/// Returns `true` if the score represents a mate, not centipawn evaluation.
#[must_use]
pub fn is_mate(&self) -> bool {
self.value.abs() >= Self::INFINITY.value - Self::MATE_RANGE
}
}

impl Neg for Score {
type Output = Self;

/// Mirrors evaluation to other player's perspective.
fn neg(self) -> Self::Output {
Self { value: -self.value }
}
}

impl Display for Score {
/// Formats the score as centipawn units for UCI interface.
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_mate() {
write!(f, "mate {}", self.mate_in())
} else {
write!(f, "cp {}", self.value)
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn mate() {
assert!(Score::mate(42).is_mate());
assert_eq!(Score::mate(42).mate_in(), 42);
}

#[test]
fn cp() {
let cp = Score::cp(42);
assert_eq!(cp, Score { value: 42 });
assert!(!cp.is_mate());

assert!(Score::cp(42) < Score::cp(43));
assert!(Score::cp(0) > Score::cp(-42));
}

#[test]
fn neg() {
let cp = Score::cp(42);
assert_eq!(-cp, Score { value: -42 });

assert_eq!(Score::mate(42).mate_in(), 42);
assert_eq!(-Score::mate(42).mate_in(), -42);
}

#[test]
fn display() {
let cp = Score::cp(123);
assert_eq!(cp.to_string(), "cp 123");

let mate = Score::mate(3);
assert_eq!(mate.to_string(), "mate 3");
}

#[test]
fn mate_vs_cp() {
assert!(Score::mate(42) > Score::cp(42));
assert!(-Score::mate(1) < Score::cp(42));
assert!(Score::mate(2) > Score::cp(-42));
}

#[test]
#[should_panic]
fn cp_panic() {
let _ = Score::cp(Score::INFINITY.value - Score::MATE_RANGE);
}
}
105 changes: 105 additions & 0 deletions src/search/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use arrayvec::ArrayVec;

use crate::chess::{position::Position, zobrist::RepetitionTable};

use super::Depth;

pub(super) struct State {
position_history: ArrayVec<Position, 256>,
repetitions: RepetitionTable,
searched_nodes: u64,
// TODO: num_pruned for debugging
}

impl State {
pub(super) fn new(root: Position) -> Self {
let mut repetitions = RepetitionTable::new();
let _ = repetitions.record(root.hash());

let mut position_history = ArrayVec::new();
position_history.push(root);

Self {
position_history,
repetitions,
searched_nodes: 1,
}
}

#[must_use]
pub(super) fn push(&mut self, position: Position) -> bool {
let draw = self.repetitions.record(position.hash());
self.position_history.push(position);
self.searched_nodes += 1;
draw
}

pub(super) fn pop(&mut self) {
debug_assert!(!self.position_history.is_empty());
debug_assert!(!self.repetitions.is_empty());

self.repetitions
.remove(self.position_history.last().unwrap().hash());
self.position_history.pop();
}

#[must_use]
pub(super) fn last(&self) -> &Position {
debug_assert!(!self.position_history.is_empty());
self.position_history.last().unwrap()
}

#[must_use]
pub(super) fn searched_nodes(&self) -> u64 {
self.searched_nodes
}

/// Returns the number of full moves since the start of the search.
#[must_use]
pub(super) fn moves(&self) -> u8 {
assert!(!self.position_history.is_empty());
let plies = self.position_history.len() as u8;
// Two plies per move, excluding the root.
plies / 2
}
}

/*
impl PositionHistory {
/// Creates an empty position history for new search.
#[must_use]
pub(crate) fn new() -> Self {
Self {
history: ArrayVec::new(),
repetitions: RepetitionTable::new(),
}
}
#[must_use]
pub(crate) fn current_position(&self) -> &Position {
self.history.last().expect("no positions in history")
}
#[must_use]
pub(crate) fn push(&mut self, position: Position) -> bool {
let hash = position.hash();
self.history.push(position);
self.repetitions.record(hash)
}
/// Removes the last position from the history and reduces the number of
/// times that position is counted for repretitions.
pub(crate) fn pop(&mut self) {
let _ = self.history.pop();
}
pub(crate) fn is_empty(&self) -> bool {
self.history.is_empty()
}
pub(crate) fn last(&self) -> &Position {
debug_assert!(!self.is_empty());
self.history.last().unwrap()
}
}
*/

0 comments on commit 157c112

Please sign in to comment.