67 lines
2.1 KiB
Rust
67 lines
2.1 KiB
Rust
//! Transposition table for deduplicating identical states.
|
|
|
|
// trans rights are human rights :D
|
|
|
|
use mino::srs::{PieceType, Queue};
|
|
use mino::Mat;
|
|
|
|
use crate::bot::node::{Node, RawNodePtr};
|
|
use crate::bot::raw::RawMatPtr;
|
|
use crate::HashMap;
|
|
|
|
pub struct TransTable {
|
|
lookup: HashMap<QueueKey, HashMap<RawMatPtr, RawNodePtr>>,
|
|
}
|
|
|
|
impl TransTable {
|
|
/// Constructs a new empty transposition table.
|
|
pub fn new() -> Self {
|
|
Self {
|
|
lookup: HashMap::with_capacity(128),
|
|
}
|
|
}
|
|
|
|
/// Looks up a node in the transposition table matching the given state. If one is
|
|
/// found, returns `Err(existing_node_ptr)`. If not found, a node is created and
|
|
/// inserted by calling the given closure, and returns `Ok(newly_inserted_node)`.
|
|
pub fn get_or_insert<'a>(
|
|
&mut self,
|
|
matrix: &Mat,
|
|
queue: Queue<'_>,
|
|
mut alloc: impl FnMut() -> &'a Node,
|
|
) -> Result<&'a Node, RawNodePtr> {
|
|
// two-phase lookup: first key off queue info, then matrix info
|
|
let map = self
|
|
.lookup
|
|
.entry(QueueKey::new(queue))
|
|
.or_insert_with(HashMap::new);
|
|
|
|
if let Some(&node_ptr) = map.get(&matrix) {
|
|
return Err(node_ptr);
|
|
}
|
|
|
|
let node = alloc();
|
|
debug_assert_eq!(*node.matrix(), *matrix);
|
|
debug_assert_eq!(node.queue(), queue);
|
|
|
|
// IMPORTANT: it would be invalid to insert `matrix.into()` since we don't have
|
|
// guaruntees about its lifetime. we have to use a pointer to the matrix in the
|
|
// newly allocated `node`.
|
|
map.insert(node.matrix().into(), node.into());
|
|
|
|
Ok(node)
|
|
}
|
|
}
|
|
|
|
// Rather than storing the actual next pieces of each queue, only store the number of
|
|
// next pieces. This is because the suffixes will always be the same if they have the
|
|
// same number of pieces, so its more efficient to only store & compare the length.
|
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
|
struct QueueKey(u16, Option<PieceType>);
|
|
|
|
impl QueueKey {
|
|
fn new(queue: Queue<'_>) -> Self {
|
|
Self(queue.next.len() as u16, queue.hold)
|
|
}
|
|
}
|