//! Thread safe transposition table for game values use crate::short::partizan::canonical_form::CanonicalForm; use append_only_vec::AppendOnlyVec; use dashmap::DashMap; use std::{hash::Hash, marker::PhantomData}; /// Interface of a transposition table pub trait TranspositionTable { /// Lookup a position value if exists fn lookup_position(&self, position: &G) -> Option; /// Save position and its game value fn insert_position(&self, position: G, value: CanonicalForm); } /// Transaction table (cache) of game positions and canonical forms. pub struct ParallelTranspositionTable { values: AppendOnlyVec, positions: DashMap, known_values: DashMap, } impl ParallelTranspositionTable where G: Eq + Hash, { /// Create new empty transposition table. #[inline] pub fn new() -> Self { Self::default() } /// Get number of saved positions #[inline] pub fn len(&self) -> usize { self.positions.len() } /// Check if table stores any position #[inline] pub fn is_empty(&self) -> bool { self.positions.is_empty() } } impl Default for ParallelTranspositionTable where G: Hash + Eq, { #[inline] fn default() -> Self { Self { values: AppendOnlyVec::new(), positions: DashMap::default(), known_values: DashMap::default(), } } } impl TranspositionTable for ParallelTranspositionTable where G: Eq + Hash, { #[cfg_attr(feature = "cargo-clippy", allow(clippy::missing_panics_doc))] #[inline] fn lookup_position(&self, position: &G) -> Option { self.positions .get(position) .map(|id| self.values[*id].clone()) } #[cfg_attr(feature = "cargo-clippy", allow(clippy::missing_panics_doc))] #[inline] fn insert_position(&self, position: G, value: CanonicalForm) { if let Some(known) = self.known_values.get(&value) { self.positions.insert(position, *known); } else { let inserted = self.values.push(value.clone()); self.known_values.insert(value, inserted); self.positions.insert(position, inserted); } } } /// Dummy transposition table that does not store anythning pub struct NoTranspositionTable(PhantomData); impl NoTranspositionTable { #[inline] /// Create new dummy transposition table pub const fn new() -> Self { Self(PhantomData) } } impl Default for NoTranspositionTable { #[inline] fn default() -> Self { Self::new() } } impl TranspositionTable for NoTranspositionTable { #[inline] fn lookup_position(&self, _position: &G) -> Option { None } #[inline] fn insert_position(&self, _position: G, _value: CanonicalForm) {} }