use super::piece_board::PieceBoard;
use super::zobrist_values::*;
use super::{Piece, Square, map_bit_board_to_squares};
#[derive(Hash, Eq, PartialEq, Clone, Copy, Debug)]
pub struct Zobrist {
hash: u64,
}
impl Zobrist {
pub fn initial() -> Self {
Zobrist { hash: INITIAL }
}
pub fn from_piece_board(piece_board: &PieceBoard, is_p1_turn_to_move: bool, step_num: Option<usize>) -> Self {
let mut hash = INITIAL;
if !is_p1_turn_to_move {
hash ^= PLAYER_TO_MOVE;
}
if let Some(step_num) = step_num {
hash ^= STEP_VALUES[step_num];
}
for is_p1 in [true, false].iter() {
for piece in [Piece::Elephant, Piece::Camel, Piece::Horse, Piece::Dog, Piece::Cat, Piece::Rabbit].iter() {
let piece_bits = piece_board.get_bits_for_piece(*piece, *is_p1);
for square in map_bit_board_to_squares(piece_bits) {
hash ^= get_piece_value(square, *piece, *is_p1);
}
}
}
Zobrist { hash }
}
pub fn update_piece_board(&self, prev_piece_board: &PieceBoard, new_piece_board: &PieceBoard) -> Zobrist {
let piece_board_value = get_piece_board_value(prev_piece_board, new_piece_board);
Zobrist { hash: self.hash ^ piece_board_value }
}
pub fn update_player_to_move(&self, prev_p1_turn_to_move: bool, new_p1_turn_to_move: bool) -> Zobrist {
let player_to_move_value = if prev_p1_turn_to_move != new_p1_turn_to_move { PLAYER_TO_MOVE } else { 0 };
Zobrist {
hash: self.hash ^ player_to_move_value,
}
}
pub fn update_step(&self, prev_step: usize, new_step: usize) -> Zobrist {
let step_value = get_step_value(prev_step, new_step);
Zobrist { hash: self.hash ^ step_value }
}
pub fn place_piece(&self, piece: Piece, square: Square, place_is_p1: bool, switch_players: bool, switch_phases: bool) -> Self {
let player_to_move_value = if switch_players | switch_phases { PLAYER_TO_MOVE } else { 0 };
let place_piece_value = get_piece_value(square, piece, place_is_p1);
let step_value = if switch_phases { STEP_VALUES[0] } else { 0 };
let hash = self.hash ^ player_to_move_value ^ place_piece_value ^ step_value;
Zobrist { hash }
}
pub fn fill_rabbits(&self, piece_board: &PieceBoard, place_is_p1: bool) -> Self {
let mut hash = self.hash;
for square in Square::from_multiple_bits(piece_board.get_bits_for_piece(Piece::Rabbit, place_is_p1)) {
hash ^= get_piece_value(square, Piece::Rabbit, place_is_p1);
}
Zobrist { hash }
}
pub fn pass(&self, step: usize) -> Self {
let hash = self.hash ^ PLAYER_TO_MOVE ^ STEP_VALUES[0] ^ STEP_VALUES[step];
Zobrist { hash }
}
pub fn board_state_hash(&self) -> u64 {
self.hash
}
}
fn get_step_value(prev_step: usize, new_step: usize) -> u64 {
STEP_VALUES[prev_step] ^ STEP_VALUES[new_step]
}
fn get_piece_board_value(prev_piece_board: &PieceBoard, new_piece_board: &PieceBoard) -> u64 {
let mut value = 0;
for is_p1 in [true, false].iter() {
for piece in [Piece::Elephant, Piece::Camel, Piece::Horse, Piece::Dog, Piece::Cat, Piece::Rabbit].iter() {
let prev_piece_bits = prev_piece_board.get_bits_for_piece(*piece, *is_p1);
let new_piece_bits = new_piece_board.get_bits_for_piece(*piece, *is_p1);
let diff_bits = prev_piece_bits ^ new_piece_bits;
if diff_bits != 0 {
for square in map_bit_board_to_squares(diff_bits) {
value ^= get_piece_value(square, *piece, *is_p1);
}
}
}
}
value
}
fn get_piece_value(square: Square, piece: Piece, is_p1: bool) -> u64 {
let piece_idx = match piece {
Piece::Elephant => 0,
Piece::Camel => 1,
Piece::Horse => 2,
Piece::Dog => 3,
Piece::Cat => 4,
Piece::Rabbit => 5,
};
let piece_idx = piece_idx + if is_p1 { 0 } else { 6 };
SQUARE_VALUES[piece_idx][square.get_index()]
}