shark/fish/src/bot/trans.rs

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)
}
}