tanton_engine/tables/
mod.rs

1pub mod butterfly;
2pub mod capture_piece_history;
3pub mod continuation;
4pub mod counter_move;
5pub mod material;
6pub mod pawn_table;
7
8use std::alloc::{alloc_zeroed, dealloc, Layout};
9use std::mem;
10use std::ops::*;
11use std::ptr;
12use std::ptr::NonNull;
13
14pub mod prelude {
15    // easier exporting :)
16    pub use super::butterfly::ButterflyHistory;
17    pub use super::capture_piece_history::CapturePieceToHistory;
18    pub use super::continuation::{ContinuationHistory, PieceToHistory};
19    pub use super::counter_move::CounterMoveHistory;
20    pub use super::{NumStatBoard, NumStatCube, StatBoard};
21}
22
23// TODO: Create StatBoards using const generics: https://github.com/rust-lang/rust/issues/44580
24// TODO: Create 3DBoard using const generics: https://github.com/rust-lang/rust/issues/44580
25
26pub trait StatBoard<T, IDX>: Sized + IndexMut<IDX, Output = T>
27where
28    T: Copy + Clone + Sized,
29{
30    const FILL: T;
31
32    fn new() -> Self {
33        unsafe { mem::zeroed() }
34    }
35
36    fn clear(&mut self) {
37        self.fill(Self::FILL);
38    }
39
40    fn fill(&mut self, val: T) {
41        let num: usize = mem::size_of::<Self>() / mem::size_of::<T>();
42
43        unsafe {
44            let ptr: *mut T = self as *mut Self as *mut T;
45            for i in 0..num {
46                ptr::write(ptr.add(i), val);
47            }
48        }
49    }
50}
51
52pub trait NumStatBoard<IDX>: StatBoard<i16, IDX> {
53    const D: i16;
54    fn update(&mut self, idx: IDX, bonus: i16) {
55        assert!(bonus.abs() <= Self::D); // Ensure range is [-32 * D, 32 * D]
56        let entry = self.index_mut(idx);
57        *entry += bonus * 32 - (*entry) * bonus.abs() / Self::D;
58    }
59}
60
61pub trait NumStatCube<IDX>: StatBoard<i16, IDX> {
62    const D: i32;
63    const W: i32;
64
65    fn update(&mut self, idx: IDX, bonus: i32) {
66        assert!(bonus.abs() <= Self::D);
67        let entry = self.index_mut(idx);
68        *entry += (bonus * Self::W - (*entry) as i32 * bonus.abs() / Self::D) as i16;
69        assert!(((*entry) as i32).abs() <= Self::D * Self::W);
70    }
71}
72
73// TODO: Performance increase awaiting with const generics: https://github.com/rust-lang/rust/issues/44580
74
75/// Generic Heap-stored array of entries. Used for building more specific abstractions.
76///
77/// Objects placed inside must not implement `Drop`, or else undefined behavior follows. Indexing is done
78/// with `u64`s, and returns a value using a mask of the lower log<sub>2</sub>(table size) bits. Collisions
79/// are possible using this structure, although very rare.
80pub struct TableBase<T: Sized + TableBaseConst> {
81    table: NonNull<T>,
82}
83
84pub trait TableBaseConst {
85    const ENTRY_COUNT: usize;
86}
87
88impl<T: Sized + TableBaseConst> TableBase<T> {
89    /// Constructs a new `TableBase`. The size must be a power of 2, or else `None` is
90    /// returned.
91    ///
92    /// # Safety
93    ///
94    /// Size must be a power of 2/
95    pub fn new() -> Option<TableBase<T>> {
96        if T::ENTRY_COUNT.count_ones() != 1 {
97            None
98        } else {
99            unsafe {
100                let table = TableBase {
101                    table: TableBase::alloc(),
102                };
103                Some(table)
104            }
105        }
106    }
107
108    /// Gets a mutable reference to an entry with a certain key.
109    #[inline(always)]
110    pub fn get_mut(&mut self, key: u64) -> &mut T {
111        unsafe { &mut *self.get_ptr(key) }
112    }
113
114    /// Gets a mutable pointer to an entry with a certain key.
115    ///
116    /// # Safety
117    ///
118    /// Unsafe due to returning a raw pointer that may dangle if the `TableBase` is
119    /// dropped prematurely.
120    #[inline(always)]
121    pub unsafe fn get_ptr(&self, key: u64) -> *mut T {
122        let index: usize = (key & (T::ENTRY_COUNT as u64 - 1)) as usize;
123        self.table.as_ptr().offset(index as isize)
124    }
125
126    pub fn clear(&mut self) {
127        unsafe {
128            let t_ptr = self.get_ptr(0);
129            ptr::write_bytes(t_ptr, 0, T::ENTRY_COUNT);
130        }
131    }
132
133    // allocates space.
134    unsafe fn alloc() -> NonNull<T> {
135        let layout = Layout::array::<T>(T::ENTRY_COUNT).unwrap();
136        let ptr = alloc_zeroed(layout);
137        let new_ptr = ptr.cast();
138        NonNull::new(new_ptr as *mut T).unwrap()
139    }
140
141    /// de-allocates the current table.
142    unsafe fn de_alloc(&mut self) {
143        let ptr: NonNull<u8> = mem::transmute(self.table);
144        dealloc(ptr.as_ptr(), Layout::array::<T>(T::ENTRY_COUNT).unwrap());
145    }
146}
147
148impl<T: Sized + TableBaseConst> Drop for TableBase<T> {
149    fn drop(&mut self) {
150        unsafe {
151            self.de_alloc();
152        }
153    }
154}