board_game/games/go/
hash.rs

1use std::fmt::{Debug, Formatter};
2use std::hash::{Hash, Hasher};
3
4use lazy_static::lazy_static;
5use rand::distributions::{Distribution, Standard};
6use rand::Rng;
7
8use crate::board::Player;
9use crate::games::go::{FlatTile, State, GO_MAX_AREA};
10use crate::util::tiny::consistent_rng;
11
12type Inner = u128;
13
14#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
15pub struct Zobrist(Inner);
16
17pub struct HashData {
18    color_tile: [Vec<Zobrist>; 2],
19    color_turn: [Zobrist; 2],
20    pass_state: [Zobrist; 3],
21}
22
23impl Zobrist {
24    pub fn for_color_tile(color: Player, tile: FlatTile) -> Zobrist {
25        HASH_DATA.color_tile[color.index() as usize][tile.index() as usize]
26    }
27
28    pub fn for_color_turn(color: Player) -> Zobrist {
29        HASH_DATA.color_turn[color.index() as usize]
30    }
31
32    pub fn for_pass_state(state: State) -> Zobrist {
33        // don't include outcome, that is implicit from the other tiles anyway
34        let state_index = match state {
35            State::Normal => 0,
36            State::Passed => 1,
37            State::Done(_) => 2,
38        };
39        HASH_DATA.pass_state[state_index]
40    }
41}
42
43// TODO generate this at compile-time?
44lazy_static! {
45    static ref HASH_DATA: HashData = HashData::new();
46}
47
48impl HashData {
49    #[allow(clippy::new_without_default)]
50    #[inline(never)]
51    pub fn new() -> HashData {
52        let mut rng = consistent_rng();
53        let vec_len = GO_MAX_AREA as usize;
54        HashData {
55            color_tile: [gen_vec(vec_len, &mut rng), gen_vec(vec_len, &mut rng)],
56            color_turn: gen_array(&mut rng),
57            pass_state: gen_array(&mut rng),
58        }
59    }
60}
61
62impl Debug for HashData {
63    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
64        f.debug_struct("HashData").finish_non_exhaustive()
65    }
66}
67
68impl Distribution<Zobrist> for Standard {
69    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Zobrist {
70        Zobrist(rng.gen())
71    }
72}
73
74fn gen_array<const N: usize>(rng: &mut impl Rng) -> [Zobrist; N] {
75    let mut array = [Zobrist::default(); N];
76    for x in &mut array {
77        *x = rng.gen();
78    }
79    array
80}
81
82fn gen_vec(len: usize, rng: &mut impl Rng) -> Vec<Zobrist> {
83    Standard.sample_iter(rng).take(len).collect()
84}
85
86impl Debug for Zobrist {
87    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
88        // print hex, full-width with leading 0x
89        write!(
90            f,
91            "Zobrist({:#0width$x})",
92            self.0,
93            width = (Inner::BITS / 8 + 2) as usize
94        )
95    }
96}
97
98impl std::ops::BitXor for Zobrist {
99    type Output = Self;
100
101    fn bitxor(self, rhs: Self) -> Self::Output {
102        Zobrist(self.0 ^ rhs.0)
103    }
104}
105
106impl std::ops::BitXorAssign for Zobrist {
107    fn bitxor_assign(&mut self, rhs: Self) {
108        self.0 ^= rhs.0;
109    }
110}
111
112impl nohash_hasher::IsEnabled for Zobrist {}
113
114impl Hash for Zobrist {
115    fn hash<H: Hasher>(&self, state: &mut H) {
116        state.write_u64((self.0 as u64) ^ ((self.0 >> 64) as u64));
117    }
118}