use super::zobrist::zobrist_no_pawns;
use crate::bitboard::Bitboard;
use crate::types::{Color, Hand, Move, Piece, PieceType, RepetitionState, Value};
#[derive(Clone)]
pub struct StateInfo {
pub material_key: u64,
pub pawn_key: u64,
pub minor_piece_key: u64,
pub non_pawn_key: [u64; Color::NUM],
pub plies_from_null: i32,
pub continuous_check: [i32; Color::NUM],
pub game_ply: u16,
pub pass_rights: u8,
pub board_key: u64,
pub hand_key: u64,
pub hand_snapshot: [Hand; Color::NUM],
pub previous: usize,
pub checkers: Bitboard,
pub blockers_for_king: [Bitboard; Color::NUM],
pub pinners: [Bitboard; Color::NUM],
pub check_squares: [Bitboard; PieceType::NUM + 1],
pub captured_piece: Piece,
pub repetition: i32,
pub repetition_times: i32,
pub repetition_type: RepetitionState,
pub material_value: Value,
pub last_move: Move,
}
impl StateInfo {
pub const NO_PREVIOUS: usize = usize::MAX;
pub fn new() -> Self {
StateInfo {
material_key: 0,
pawn_key: zobrist_no_pawns(),
minor_piece_key: 0,
non_pawn_key: [0; Color::NUM],
plies_from_null: 0,
continuous_check: [0; Color::NUM],
game_ply: 0,
pass_rights: 0, board_key: 0,
hand_key: 0,
hand_snapshot: [Hand::EMPTY; Color::NUM],
previous: Self::NO_PREVIOUS,
checkers: Bitboard::EMPTY,
blockers_for_king: [Bitboard::EMPTY; Color::NUM],
pinners: [Bitboard::EMPTY; Color::NUM],
check_squares: [Bitboard::EMPTY; PieceType::NUM + 1],
captured_piece: Piece::NONE,
repetition: 0,
repetition_times: 0,
repetition_type: RepetitionState::None,
material_value: Value::ZERO,
last_move: Move::NONE,
}
}
#[inline]
pub fn get_pass_rights(&self, color: Color) -> u8 {
match color {
Color::Black => (self.pass_rights >> 4) & 0x0F,
Color::White => self.pass_rights & 0x0F,
}
}
#[inline]
pub(crate) fn set_pass_rights_internal(&mut self, color: Color, count: u8) {
let count = count.min(15); match color {
Color::Black => {
self.pass_rights = (self.pass_rights & 0x0F) | ((count & 0x0F) << 4);
}
Color::White => {
self.pass_rights = (self.pass_rights & 0xF0) | (count & 0x0F);
}
}
}
#[inline]
pub fn key(&self) -> u64 {
self.board_key ^ self.hand_key
}
#[inline]
pub const fn has_previous(&self) -> bool {
self.previous != Self::NO_PREVIOUS
}
#[inline]
pub const fn previous_index(&self) -> Option<usize> {
if self.has_previous() {
Some(self.previous)
} else {
None
}
}
pub fn partial_clone(&self) -> Self {
StateInfo {
material_key: self.material_key,
pawn_key: self.pawn_key,
minor_piece_key: self.minor_piece_key,
non_pawn_key: self.non_pawn_key,
plies_from_null: self.plies_from_null,
continuous_check: self.continuous_check,
game_ply: self.game_ply,
pass_rights: self.pass_rights, board_key: self.board_key,
hand_key: self.hand_key,
hand_snapshot: self.hand_snapshot,
previous: Self::NO_PREVIOUS,
checkers: Bitboard::EMPTY,
blockers_for_king: [Bitboard::EMPTY; Color::NUM],
pinners: [Bitboard::EMPTY; Color::NUM],
check_squares: [Bitboard::EMPTY; PieceType::NUM + 1],
captured_piece: Piece::NONE,
repetition: 0,
repetition_times: 0,
repetition_type: RepetitionState::None,
material_value: self.material_value,
last_move: Move::NONE,
}
}
}
impl Default for StateInfo {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_state_info_new() {
let state = StateInfo::new();
assert_eq!(state.board_key, 0);
assert_eq!(state.hand_key, 0);
assert_eq!(state.pawn_key, zobrist_no_pawns());
assert_eq!(state.minor_piece_key, 0);
assert_eq!(state.non_pawn_key, [0; Color::NUM]);
assert_eq!(state.key(), 0);
assert!(state.checkers.is_empty());
assert_eq!(state.previous, StateInfo::NO_PREVIOUS);
}
#[test]
fn test_state_info_key() {
let mut state = StateInfo::new();
state.board_key = 0x1234;
state.hand_key = 0x5678;
assert_eq!(state.key(), 0x1234 ^ 0x5678);
}
#[test]
fn test_state_info_partial_clone() {
let mut state = StateInfo::new();
state.material_key = 100;
state.plies_from_null = 5;
state.continuous_check = [3, 2];
state.minor_piece_key = 42;
state.non_pawn_key = [7, 11];
let cloned = state.partial_clone();
assert_eq!(cloned.material_key, 100);
assert_eq!(cloned.plies_from_null, 5);
assert_eq!(cloned.continuous_check, [3, 2]);
assert_eq!(cloned.minor_piece_key, 42);
assert_eq!(cloned.non_pawn_key, [7, 11]);
assert_eq!(cloned.previous, StateInfo::NO_PREVIOUS);
}
#[test]
fn test_pass_rights_storage() {
let mut state = StateInfo::new();
state.set_pass_rights_internal(Color::Black, 2);
state.set_pass_rights_internal(Color::White, 3);
assert_eq!(state.get_pass_rights(Color::Black), 2);
assert_eq!(state.get_pass_rights(Color::White), 3);
assert_eq!(state.pass_rights, 0x23);
}
#[test]
fn test_pass_rights_clamp() {
let mut state = StateInfo::new();
state.set_pass_rights_internal(Color::Black, 20);
assert_eq!(state.get_pass_rights(Color::Black), 15);
}
#[test]
fn test_pass_rights_independence() {
let mut state = StateInfo::new();
state.set_pass_rights_internal(Color::Black, 5);
assert_eq!(state.get_pass_rights(Color::Black), 5);
assert_eq!(state.get_pass_rights(Color::White), 0);
state.set_pass_rights_internal(Color::White, 7);
assert_eq!(state.get_pass_rights(Color::Black), 5);
assert_eq!(state.get_pass_rights(Color::White), 7);
}
#[test]
fn test_pass_rights_partial_clone() {
let mut state = StateInfo::new();
state.set_pass_rights_internal(Color::Black, 3);
state.set_pass_rights_internal(Color::White, 5);
let cloned = state.partial_clone();
assert_eq!(cloned.get_pass_rights(Color::Black), 3);
assert_eq!(cloned.get_pass_rights(Color::White), 5);
}
}