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}