use crate::types::{Color, Piece, PieceType, Square};
use std::sync::LazyLock;
pub struct Zobrist {
pub side: u64,
pub psq: [[u64; Square::NUM + 1]; 32],
pub hand: [[u64; 8]; Color::NUM],
pub no_pawns: u64,
}
impl Zobrist {
pub const fn init() -> Self {
let mut zobrist = Zobrist {
side: 0,
psq: [[0; Square::NUM + 1]; 32],
hand: [[0; 8]; Color::NUM],
no_pawns: 0,
};
let mut seed: u64 = 20151225;
let (s, key) = next_key(seed);
seed = s;
zobrist.side = key;
let (s, key) = next_key(seed);
seed = s;
zobrist.no_pawns = key;
let mut pc = 1;
while pc < 32 {
let mut sq = 0;
while sq < Square::NUM {
let (s, key) = next_key(seed);
seed = s;
zobrist.psq[pc][sq] = key;
sq += 1;
}
pc += 1;
}
let mut c = 0;
while c < Color::NUM {
let mut pr = 1;
while pr < 8 {
let (s, key) = next_key(seed);
seed = s;
zobrist.hand[c][pr] = key;
pr += 1;
}
c += 1;
}
let _ = seed;
zobrist
}
}
const fn xorshift64_star(mut s: u64) -> (u64, u64) {
s ^= s >> 12;
s ^= s << 25;
s ^= s >> 27;
let value = s.wrapping_mul(2685821657736338717u64);
(s, value)
}
const fn next_key(seed: u64) -> (u64, u64) {
let (s, r1) = xorshift64_star(seed);
let (s, _) = xorshift64_star(s);
let (s, _) = xorshift64_star(s);
let (s, _) = xorshift64_star(s);
(s, r1)
}
const fn xorshift64(mut x: u64) -> u64 {
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
x
}
pub static ZOBRIST: Zobrist = Zobrist::init();
#[inline]
pub fn zobrist_psq(pc: Piece, sq: Square) -> u64 {
ZOBRIST.psq[pc.index()][sq.index()]
}
#[inline]
pub fn zobrist_no_pawns() -> u64 {
ZOBRIST.no_pawns
}
#[inline]
pub fn zobrist_hand(color: Color, pt: PieceType) -> u64 {
ZOBRIST.hand[color.index()][pt.index()]
}
#[inline]
pub fn zobrist_side() -> u64 {
ZOBRIST.side
}
const PASS_RIGHTS_ZOBRIST_SEED: u64 = 0x5A55_0000_0000_0001;
static PASS_RIGHTS_KEYS: LazyLock<[[u64; 16]; 16]> = LazyLock::new(|| {
let mut keys = [[0u64; 16]; 16];
let mut seed = PASS_RIGHTS_ZOBRIST_SEED;
for (black, row) in keys.iter_mut().enumerate() {
for (white, key) in row.iter_mut().enumerate() {
if black == 0 && white == 0 {
continue;
}
seed = xorshift64(seed);
*key = seed;
}
}
keys
});
#[inline]
pub fn zobrist_pass_rights(black_rights: u8, white_rights: u8) -> u64 {
PASS_RIGHTS_KEYS[black_rights.min(15) as usize][white_rights.min(15) as usize]
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{File, Rank};
#[test]
fn test_zobrist_init() {
assert_ne!(ZOBRIST.side, 0);
let sq11 = Square::new(File::File1, Rank::Rank1);
let sq12 = Square::new(File::File1, Rank::Rank2);
assert_ne!(zobrist_psq(Piece::B_PAWN, sq11), zobrist_psq(Piece::B_PAWN, sq12));
assert_ne!(zobrist_psq(Piece::B_PAWN, sq11), zobrist_psq(Piece::W_PAWN, sq11));
}
#[test]
fn test_zobrist_hand() {
assert_ne!(
zobrist_hand(Color::Black, PieceType::Pawn),
zobrist_hand(Color::Black, PieceType::Lance)
);
assert_ne!(
zobrist_hand(Color::Black, PieceType::Pawn),
zobrist_hand(Color::White, PieceType::Pawn)
);
}
#[test]
fn test_zobrist_xor_property() {
let sq = Square::new(File::File5, Rank::Rank5);
let h1 = zobrist_psq(Piece::B_PAWN, sq);
let h2 = zobrist_psq(Piece::B_GOLD, sq);
let combined = h1 ^ h2;
assert_eq!(combined ^ h2, h1);
assert_eq!(combined ^ h1, h2);
}
#[test]
fn test_zobrist_pass_rights_zero_compatible() {
assert_eq!(zobrist_pass_rights(0, 0), 0);
}
#[test]
fn test_zobrist_pass_rights_nonzero() {
assert_ne!(zobrist_pass_rights(1, 0), 0);
assert_ne!(zobrist_pass_rights(0, 1), 0);
assert_ne!(zobrist_pass_rights(2, 2), 0);
assert_ne!(zobrist_pass_rights(15, 15), 0);
}
#[test]
fn test_zobrist_pass_rights_uniqueness() {
assert_ne!(zobrist_pass_rights(1, 0), zobrist_pass_rights(0, 1));
assert_ne!(zobrist_pass_rights(2, 2), zobrist_pass_rights(3, 3));
assert_ne!(zobrist_pass_rights(1, 1), zobrist_pass_rights(2, 2));
}
#[test]
fn test_zobrist_pass_rights_clamp() {
let key15 = zobrist_pass_rights(15, 15);
let key20 = zobrist_pass_rights(20, 20);
assert_eq!(key15, key20);
}
#[test]
fn test_zobrist_pass_rights_xor_property() {
let key1 = zobrist_pass_rights(2, 2);
let key2 = zobrist_pass_rights(3, 3);
let combined = key1 ^ key2;
assert_eq!(combined ^ key2, key1);
assert_eq!(combined ^ key1, key2);
}
}