1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

pub mod pawn_table;
pub mod material;
pub mod counter_move;
pub mod continuation;
pub mod capture_piece_history;
pub mod butterfly;

use std::ptr::NonNull;
use std::alloc::{Alloc, Layout, Global, handle_alloc_error};
use std::mem;
use std::ptr;
use std::ops::*;

pub mod prelude {
    // easier exporting :)
    pub use super::counter_move::CounterMoveHistory;
    pub use super::continuation::{ContinuationHistory,PieceToHistory};
    pub use super::butterfly::ButterflyHistory;
    pub use super::capture_piece_history::CapturePieceToHistory;
    pub use super::{StatBoard,NumStatBoard,NumStatCube};
}


// TODO: Create StatBoards using const generics: https://github.com/rust-lang/rust/issues/44580
// TODO: Create 3DBoard using const generics: https://github.com/rust-lang/rust/issues/44580


pub trait StatBoard<T, IDX>: Sized + IndexMut<IDX, Output=T>
    where T: Copy + Clone + Sized, {

    const FILL: T;

    fn new() -> Self {
        unsafe {mem::zeroed()}
    }

    fn clear(&mut self) {
        self.fill(Self::FILL);
    }

    fn fill(&mut self, val: T) {
        let num: usize = mem::size_of::<Self>() / mem::size_of::<T>();

        unsafe {
            let ptr: *mut T = mem::transmute(self as *mut Self);
            for i in 0..num {
                ptr::write(ptr.add(i), val);
            }
        }
    }
}

pub trait NumStatBoard<IDX>: StatBoard<i16,IDX>
{
    const D: i16;
    fn update(&mut self, idx: IDX, bonus: i16) {
        assert!(bonus.abs() <= Self::D); // Ensure range is [-32 * D, 32 * D]
        let entry = self.index_mut(idx);
        *entry += bonus * 32 - (*entry) * bonus.abs() / Self::D;
    }
}


pub trait NumStatCube<IDX>: StatBoard<i16,IDX> {
    const D: i32;
    const W: i32;

    fn update(&mut self, idx: IDX, bonus: i32) {
        assert!(bonus.abs() <= Self::D);
        let entry = self.index_mut(idx);
        *entry += (bonus * Self::W - (*entry) as i32 * bonus.abs() / Self::D) as i16;
        assert!(((*entry) as i32).abs() <= Self::D * Self::W);
    }
}

// TODO: Performance increase awaiting with const generics: https://github.com/rust-lang/rust/issues/44580

/// Generic Heap-stored array of entries. Used for building more specific abstractions.
///
/// Objects placed inside must not implement `Drop`, or else undefined behavior follows. Indexing is done
/// with `u64`s, and returns a value using a mask of the lower log<sub>2</sub>(table size) bits. Collisions
/// are possible using this structure, although very rare.
pub struct TableBase<T: Sized + TableBaseConst> {
    table: NonNull<T>,
}

pub trait TableBaseConst {
    const ENTRY_COUNT: usize;
}

impl<T: Sized + TableBaseConst> TableBase<T> {
    /// Constructs a new `TableBase`. The size must be a power of 2, or else `None` is
    /// returned.
    ///
    /// # Safety
    ///
    /// Size must be a power of 2/
    pub fn new() -> Option<TableBase<T>> {
        if T::ENTRY_COUNT.count_ones() != 1 {
            None
        } else {
            unsafe  {
                let table = TableBase {
                    table: TableBase::alloc(),
                };
                Some(table)
            }
        }
    }

    /// Gets a mutable reference to an entry with a certain key.
    #[inline(always)]
    pub fn get_mut(&mut self, key: u64) -> &mut T {
        unsafe {
            &mut *self.get_ptr(key)
        }
    }

    /// Gets a mutable pointer to an entry with a certain key.
    ///
    /// # Safety
    ///
    /// Unsafe due to returning a raw pointer that may dangle if the `TableBase` is
    /// dropped prematurely.
    #[inline(always)]
    pub unsafe fn get_ptr(&self, key: u64) -> *mut T {
        let index: usize = (key & (T::ENTRY_COUNT as u64 - 1)) as usize;
        self.table.as_ptr().offset(index as isize)
    }

    pub fn clear(&mut self) {
        unsafe {
            let t_ptr = self.get_ptr(0);
            ptr::write_bytes(t_ptr, 0, T::ENTRY_COUNT);
        }
    }

    // allocates space.
    unsafe fn alloc() -> NonNull<T> {
        let layout = Layout::array::<T>(T::ENTRY_COUNT).unwrap();
        let ptr = Global.alloc_zeroed(layout);
        let new_ptr = match ptr {
            Ok(ptr) => ptr.cast().as_ptr(),
            Err(_err) => handle_alloc_error(layout),
        };
        NonNull::new(new_ptr as *mut T).unwrap()
    }

    /// de-allocates the current table.
    unsafe fn de_alloc(&mut self) {
        let ptr: NonNull<u8> = mem::transmute(self.table);
        Global.dealloc(ptr,Layout::array::<T>(T::ENTRY_COUNT).unwrap());
    }
}

impl<T: Sized + TableBaseConst> Drop for TableBase<T> {
    fn drop(&mut self) {
        unsafe {
            self.de_alloc();
        }
    }
}