-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: Add forgotten files and fix the build
- Loading branch information
1 parent
2dc15f6
commit 157c112
Showing
2 changed files
with
247 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} | ||
} | ||
*/ |