use std::alloc::{self, Layout};
use std::cell::UnsafeCell;
use std::fmt::Display;
use std::mem;
use std::ptr::NonNull;
use crate::moves::*;
const CLUSTER_SIZE: usize = 3;
const BYTES_PER_KB: f64 = 1000.0;
const BYTES_PER_MB: f64 = BYTES_PER_KB * 1000.0;
const BYTES_PER_GB: f64 = BYTES_PER_MB * 1000.0;
pub static mut TT_SAFE_INSERTS: usize = 0;
pub static mut TT_UNSAFE_INSERTS: usize = 0;
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum NodeBound {
ExactValue,
UpperBound,
LowerBound,
}
#[derive(Clone, Copy, Debug)]
pub struct Entry {
pub key: u64,
pub score: f64,
pub bestmove: Move,
pub depth: i8,
pub bound: NodeBound,
pub used: bool,
}
impl Entry {
pub fn new(key: u64, score: f64, depth: i8, bestmove: Move, flag: NodeBound) -> Entry {
Entry {
key,
score,
bestmove,
depth,
bound: flag,
used: true
}
}
pub fn replace(&mut self, entry: Entry) {
self.key = entry.key;
self.score = entry.score;
self.bestmove = entry.bestmove;
self.depth = entry.depth;
self.bound = entry.bound;
self.used = entry.used;
}
}
#[derive(Clone, Copy, Debug)]
pub struct Cluster {
pub entrys: [Entry; CLUSTER_SIZE],
}
pub struct TranspositionTable {
pub clusters: UnsafeCell<NonNull<Cluster>>,
pub cap: UnsafeCell<usize>,
}
impl TranspositionTable {
pub fn new(size: usize) -> TranspositionTable {
TranspositionTable {
clusters: UnsafeCell::new(alloc_room(size)),
cap: UnsafeCell::new(size),
}
}
pub fn size_kilobytes(&self) -> f64 {
(mem::size_of::<Cluster>() * self.num_clusters()) as f64 / BYTES_PER_KB
}
pub fn size_megabytes(&self) -> f64 {
(mem::size_of::<Cluster>() * self.num_clusters()) as f64 / BYTES_PER_MB
}
pub fn size_gigabytes(&self) -> f64 {
(mem::size_of::<Cluster>() * self.num_clusters()) as f64 / BYTES_PER_GB
}
pub fn num_clusters(&self) -> usize {
unsafe { *self.cap.get() }
}
pub fn num_entrys(&self) -> usize {
self.num_clusters() * CLUSTER_SIZE
}
fn get_cluster(&self, i: usize) -> *mut Cluster {
unsafe{ (*self.clusters.get()).as_ptr().add(i) }
}
pub unsafe fn probe(&self, key: u64) -> (bool, &mut Entry) {
let index = key as usize % self.num_clusters();
let cluster = self.get_cluster(index);
for entry_idx in 0..CLUSTER_SIZE {
let entry_ptr = get_entry(cluster, entry_idx);
let entry = &mut (*entry_ptr);
if entry.key == key {
return (true, entry);
}
}
(false, &mut (*cluster_first_entry(cluster)))
}
pub unsafe fn insert(&self, new_entry: Entry) -> bool {
let index = new_entry.key as usize % self.num_clusters();
let cluster = self.get_cluster(index);
for entry_idx in 0..CLUSTER_SIZE {
let entry_ptr: *mut Entry = get_entry(cluster, entry_idx);
let entry = &mut (*entry_ptr);
if !entry.used {
TT_SAFE_INSERTS += 1;
entry.replace(new_entry);
return true;
}
if entry.key == new_entry.key && new_entry.depth >= entry.depth {
TT_SAFE_INSERTS += 1;
entry.replace(new_entry);
return true;
}
}
let mut replacement_ptr = cluster_first_entry(cluster);
for entry_idx in 0..CLUSTER_SIZE {
let entry_ptr = get_entry(cluster, entry_idx);
if (*entry_ptr).depth <= (*replacement_ptr).depth {
replacement_ptr = entry_ptr;
}
}
let replacement_entry = &mut (*replacement_ptr);
TT_UNSAFE_INSERTS += 1;
replacement_entry.replace(new_entry);
false
}
pub unsafe fn de_alloc(&self) {
let layout = Layout::from_size_align(*self.cap.get(), 2).unwrap();
let ptr: *mut u8 = mem::transmute(*self.clusters.get());
alloc::dealloc(ptr, layout);
}
pub unsafe fn reset(&self) {
self.de_alloc();
*self.clusters.get() = alloc_room(*self.cap.get());
}
}
impl Display for TranspositionTable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
unsafe {
for cluster_idx in 0..self.num_clusters() {
let cluster = self.get_cluster(cluster_idx);
writeln!(f, "cluster {}", cluster_idx)?;
for entry_idx in 0..CLUSTER_SIZE {
let entry = get_entry(cluster, entry_idx);
if (*entry).used {
writeln!(f, " - {:?}", *entry)?;
} else {
writeln!(f, " - NONE")?;
}
}
}
}
Result::Ok(())
}
}
fn get_entry(cluster: *mut Cluster, i: usize) -> *mut Entry {
unsafe { ((*cluster).entrys).as_ptr().add(i) as *mut Entry }
}
unsafe fn cluster_first_entry(cluster: *mut Cluster) -> *mut Entry {
(*cluster).entrys.get_unchecked_mut(0) as *mut Entry
}
fn alloc_room(size: usize) -> NonNull<Cluster> {
let size = size * mem::size_of::<Cluster>();
let layout = Layout::from_size_align(size, 8).unwrap();
let ptr: *mut u8 = unsafe { alloc::alloc_zeroed(layout) };
let new_ptr: *mut Cluster = ptr.cast();
NonNull::new(new_ptr).unwrap()
}