arimaa-engine 0.1.0

A bitboard-based Arimaa game engine: move generation, game state, and Zobrist hashing.
Documentation
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()]
}