Skip to main content

arimaa_engine/
zobrist.rs

1use super::piece_board::PieceBoard;
2use super::zobrist_values::*;
3use super::{Piece, Square, map_bit_board_to_squares};
4
5#[derive(Hash, Eq, PartialEq, Clone, Copy, Debug)]
6pub struct Zobrist {
7    hash: u64,
8}
9
10impl Zobrist {
11    pub fn initial() -> Self {
12        Zobrist { hash: INITIAL }
13    }
14
15    pub fn from_piece_board(piece_board: &PieceBoard, is_p1_turn_to_move: bool, step_num: Option<usize>) -> Self {
16        let mut hash = INITIAL;
17        if !is_p1_turn_to_move {
18            hash ^= PLAYER_TO_MOVE;
19        }
20
21        if let Some(step_num) = step_num {
22            hash ^= STEP_VALUES[step_num];
23        }
24
25        for is_p1 in [true, false].iter() {
26            for piece in [Piece::Elephant, Piece::Camel, Piece::Horse, Piece::Dog, Piece::Cat, Piece::Rabbit].iter() {
27                let piece_bits = piece_board.get_bits_for_piece(*piece, *is_p1);
28                for square in map_bit_board_to_squares(piece_bits) {
29                    hash ^= get_piece_value(square, *piece, *is_p1);
30                }
31            }
32        }
33
34        Zobrist { hash }
35    }
36
37    pub fn update_piece_board(&self, prev_piece_board: &PieceBoard, new_piece_board: &PieceBoard) -> Zobrist {
38        let piece_board_value = get_piece_board_value(prev_piece_board, new_piece_board);
39        Zobrist { hash: self.hash ^ piece_board_value }
40    }
41
42    pub fn update_player_to_move(&self, prev_p1_turn_to_move: bool, new_p1_turn_to_move: bool) -> Zobrist {
43        let player_to_move_value = if prev_p1_turn_to_move != new_p1_turn_to_move { PLAYER_TO_MOVE } else { 0 };
44
45        Zobrist {
46            hash: self.hash ^ player_to_move_value,
47        }
48    }
49
50    pub fn update_step(&self, prev_step: usize, new_step: usize) -> Zobrist {
51        let step_value = get_step_value(prev_step, new_step);
52        Zobrist { hash: self.hash ^ step_value }
53    }
54
55    pub fn place_piece(&self, piece: Piece, square: Square, place_is_p1: bool, switch_players: bool, switch_phases: bool) -> Self {
56        let player_to_move_value = if switch_players | switch_phases { PLAYER_TO_MOVE } else { 0 };
57        let place_piece_value = get_piece_value(square, piece, place_is_p1);
58        let step_value = if switch_phases { STEP_VALUES[0] } else { 0 };
59
60        let hash = self.hash ^ player_to_move_value ^ place_piece_value ^ step_value;
61
62        Zobrist { hash }
63    }
64
65    pub fn fill_rabbits(&self, piece_board: &PieceBoard, place_is_p1: bool) -> Self {
66        let mut hash = self.hash;
67
68        for square in Square::from_multiple_bits(piece_board.get_bits_for_piece(Piece::Rabbit, place_is_p1)) {
69            hash ^= get_piece_value(square, Piece::Rabbit, place_is_p1);
70        }
71
72        Zobrist { hash }
73    }
74
75    pub fn pass(&self, step: usize) -> Self {
76        let hash = self.hash ^ PLAYER_TO_MOVE ^ STEP_VALUES[0] ^ STEP_VALUES[step];
77
78        Zobrist { hash }
79    }
80
81    pub fn board_state_hash(&self) -> u64 {
82        self.hash
83    }
84}
85
86fn get_step_value(prev_step: usize, new_step: usize) -> u64 {
87    STEP_VALUES[prev_step] ^ STEP_VALUES[new_step]
88}
89
90fn get_piece_board_value(prev_piece_board: &PieceBoard, new_piece_board: &PieceBoard) -> u64 {
91    let mut value = 0;
92
93    for is_p1 in [true, false].iter() {
94        for piece in [Piece::Elephant, Piece::Camel, Piece::Horse, Piece::Dog, Piece::Cat, Piece::Rabbit].iter() {
95            let prev_piece_bits = prev_piece_board.get_bits_for_piece(*piece, *is_p1);
96            let new_piece_bits = new_piece_board.get_bits_for_piece(*piece, *is_p1);
97            let diff_bits = prev_piece_bits ^ new_piece_bits;
98
99            if diff_bits != 0 {
100                for square in map_bit_board_to_squares(diff_bits) {
101                    value ^= get_piece_value(square, *piece, *is_p1);
102                }
103            }
104        }
105    }
106
107    value
108}
109
110fn get_piece_value(square: Square, piece: Piece, is_p1: bool) -> u64 {
111    let piece_idx = match piece {
112        Piece::Elephant => 0,
113        Piece::Camel => 1,
114        Piece::Horse => 2,
115        Piece::Dog => 3,
116        Piece::Cat => 4,
117        Piece::Rabbit => 5,
118    };
119
120    let piece_idx = piece_idx + if is_p1 { 0 } else { 6 };
121
122    SQUARE_VALUES[piece_idx][square.get_index()]
123}