c4_e5_chess/engine/
store.rs

1use crate::misc::types::*;
2use cozy_chess::{Board, Move};
3use hashbrown::hash_map::Entry::Occupied;
4use hashbrown::hash_map::Entry::Vacant;
5use hashbrown::HashMap;
6
7/// A transposition table.
8#[derive(Clone)]
9pub struct Item {
10    depth: Depth,
11    value: MoveScore,
12    chessmove: Move,
13}
14
15/// A hashmap for use in the transposition table.
16pub struct Store {
17    pub h: HashMap<u64, Item>,
18}
19
20impl Store {
21    /// Constructor
22    pub fn new() -> Self {
23        Self { h: HashMap::new() }
24    }
25
26    /// Put a position, its score and depth and the best move into the transposition table.
27    /// Update the score only if depth is greater than already stored depth.
28    pub fn put(&mut self, depth: Depth, value: MoveScore, b: &Board, chessmove: &Move) {
29        let key = b.hash_without_ep();
30        let item = Item {
31            depth,
32            value,
33            chessmove: *chessmove,
34        };
35        match self.h.entry(key) {
36            Occupied(mut entry) => {
37                if entry.get().depth <= depth {
38                    entry.insert(item);
39                }
40            }
41            Vacant(entry) => {
42                entry.insert(item);
43            }
44        }
45    }
46
47    /// Get a move and its score for the given position.
48    pub fn get(&self, depth: Depth, b: &Board) -> Option<(Move, MoveScore, bool)> {
49        let key = b.hash_without_ep();
50        match self.h.get(&key) {
51            Some(item) => {
52                if item.depth < depth {
53                    Some((item.chessmove, item.value, false))
54                } else {
55                    Some((item.chessmove, item.value, true))
56                }
57            }
58            None => None,
59        }
60    }
61}
62
63impl Default for Store {
64    fn default() -> Self {
65        Self::new()
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use std::str::FromStr;
72
73    use super::*;
74    use crate::engine::game::Game;
75
76    #[test]
77    fn test_store() {
78        let g = Game::new("".to_string(), 10, 10000);
79        let mut store = Store::new();
80
81        let result = store.get(5, &g.board);
82        assert_eq!(result, None);
83
84        store.put(5, 300, &g.board, &Move::from_str("c2c4").unwrap());
85
86        let (m, v, fresh) = store.get(5, &g.board).unwrap();
87        assert_eq!(v, 300);
88        assert_eq!(m.to_string(), "c2c4");
89        assert_eq!(fresh, true);
90
91        let (m, _, fresh) = store.get(6, &g.board).unwrap();
92        assert_eq!(m.to_string(), "c2c4");
93        assert_eq!(fresh, false);
94
95        let (m, v, fresh) = store.get(4, &g.board).unwrap();
96        assert_eq!(v, 300);
97        assert_eq!(m.to_string(), "c2c4");
98        assert_eq!(fresh, true);
99
100        store.put(5, 305, &g.board, &Move::from_str("e2e4").unwrap());
101
102        let (m, v, fresh) = store.get(4, &g.board).unwrap();
103        assert_eq!(v, 305);
104        assert_eq!(m.to_string(), "e2e4");
105        assert_eq!(fresh, true);
106    }
107}