use std::{convert::TryFrom, iter::zip};
use bulletformat::ChessBoard;
#[repr(u8)]
#[allow(dead_code)]
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum PieceType{
Pawn,
Knight,
Bishop,
Rook,
Queen,
King,
Castle,
Void
}
#[repr(u8)]
#[allow(dead_code)]
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum Pieces{
WPawn, BPawn,
WKnight, BKnight,
WBishop, BBishop,
WRook, BRook,
WQueen, BQueen,
WKing, BKing,
WCASTLE, BCASLTE,
Void
}
#[allow(dead_code)]
pub enum Color{White, Black}
impl TryFrom<u8> for Color {
type Error = ();
fn try_from(v: u8) -> Result<Self, Self::Error> {
unsafe { std::mem::transmute(v) }
}
}
impl TryFrom<u8> for PieceType {
type Error = ();
fn try_from(v: u8) -> Result<Self, Self::Error> {
unsafe { std::mem::transmute(v) }
}
}
impl Pieces {
pub fn color(&self) -> Color {
(*self as u8 % 2).try_into().unwrap()
}
pub fn piecetype(&self) -> PieceType {
(*self as u8 / 2).try_into().unwrap()
}
pub fn from_viri(id: u8) -> Pieces {
((id >> 3) | (id & 0b111) << 1).try_into().unwrap()
}
pub fn from_pc_color(pc: PieceType, c: Color) -> Pieces {
((pc as u8) << 1 | c as u8).try_into().unwrap()
}
}
impl TryFrom<u8> for Pieces {
type Error = ();
fn try_from(v: u8) -> Result<Self, Self::Error> {
unsafe { std::mem::transmute(v) }
}
}
const ZOBR_CASTLE: usize = 12*64;
const ZOBR_PASSANT: usize = ZOBR_CASTLE;
const ZOBR_TURN: usize = ZOBR_PASSANT+65;
#[cfg(feature = "hll")]
const ZOBR_TOT: usize = ZOBR_TURN+1;
#[cfg(feature = "hll")]
const ZOBRISTS: [u64; ZOBR_TOT] = {
let mut res: [u64; ZOBR_TOT] = [0; ZOBR_TOT];
let mut state: u64 = 42;
cfor!(let mut idz: usize = 0; idz < ZOBR_TOT; idz += 1; {
state = state.wrapping_add(0x9E3779B97F4A7C15);
let mut z: u64 = state;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58476D1CE4E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D049BB133111EB);
res[idz] = z ^ (z >> 31);
});
res[ZOBR_CASTLE+64] = 0;
res[ZOBR_PASSANT+64] = 0;
res
};
#[derive(Clone, Copy)]
pub struct Position{
pub bbs: [u64; 8],
pub mailbox: [u8; 64],
pub score: i16,
pub bound: u8,
pub rule50: u8,
pub bm: u16,
pub stm: bool,
pub depth: u8,
pub ep: u8,
pub castle_mask: u64,
#[cfg(feature = "hll")]
pub hash: u64,
}
impl Default for Position {
fn default() -> Self {
Self {
bbs: [0; 8],
mailbox: [Pieces::Void as u8; 64],
score: 0,
bound: 0,
rule50: 0,
bm: 0,
stm: true,
depth: 0,
ep: 64,
castle_mask: 0,
#[cfg(feature = "hll")]
hash: 0,
}
}
}
const KINGPOS_CASTLE: [u8; 2] = [2, 6];
const ROOKPOS_CASTLE: [u8; 2] = [3, 5];
impl Position {
pub fn occupancy(&self) -> u64 {
return self.bbs[0] | self.bbs[1];
}
pub fn piece(&self, idx: usize) -> Pieces {
self.mailbox[idx].try_into().unwrap()
}
fn update_zobr(&mut self, _idx: usize) {
#[cfg(feature = "hll")]
{
self.hash ^= ZOBRISTS[_idx];
}
}
#[allow(dead_code)]
pub fn print(&self) {
for i in 0..64 {
if self.mailbox[i]/2 == 7 {
print!("## ");
}else {
print!("{:2} ", self.mailbox[i]);
}
if i % 8 == 7 {
print!("\n");
}
}
}
pub fn push(&mut self, idx: u32, piece: Pieces) {
assert!(self.mailbox[idx as usize] == Pieces::Void as u8);
self.mailbox[idx as usize] = piece as u8;
self.bbs[piece.color() as usize] |= 1_u64 << idx;
self.bbs[piece.piecetype() as usize + 2] |= 1_u64 << idx;
self.update_zobr((piece as u32 * 64 + idx) as usize);
}
pub fn update_ep(&mut self, newep: usize) {
self.update_zobr(newep + ZOBR_PASSANT);
self.ep = newep as u8;
}
pub fn update_stm(&mut self, newstm: usize) {
#[cfg(feature = "hll")]
if newstm != 0 {
self.hash ^= ZOBRISTS[ZOBR_TURN];
}
self.stm = newstm != 0;
}
pub fn turn_stm(&mut self) {
self.update_zobr(ZOBR_TURN);
self.stm = !self.stm;
}
pub fn update_castle_rights(&mut self, castle_idx: usize) {
self.update_zobr(castle_idx + ZOBR_CASTLE);
self.castle_mask |= 1 << castle_idx as u64;
}
fn erase(&mut self, idx: usize){
let erased: Pieces = self.piece(idx);
if erased != Pieces::Void {
self.bbs[erased.piecetype() as usize + 2] ^= 1_u64 << idx;
self.bbs[erased.color() as usize] ^= 1_u64 << idx;
self.update_zobr(erased as usize * 64 + idx);
}
self.mailbox[idx] = Pieces::Void as u8;
}
fn erasesure(&mut self, idx: usize){
let erased: Pieces = self.piece(idx);
self.bbs[erased.piecetype() as usize + 2] ^= 1_u64 << idx;
self.bbs[erased.color() as usize] ^= 1_u64 << idx;
self.update_zobr(erased as usize * 64 + idx);
self.mailbox[idx] = Pieces::Void as u8;
}
#[allow(dead_code)]
fn verify(&self) -> bool { #[cfg(feature = "hll")]
{
let mut zobr: u64 = 0;
for i in 0..64 {
if self.mailbox[i] != Pieces::Void as u8 {
zobr ^= ZOBRISTS[i + 64 * self.mailbox[i] as usize];
}
}
zobr ^= ZOBRISTS[ZOBR_TURN] * self.stm as u64;
let mut mask = self.castle_mask;
while mask != 0 {
zobr ^= ZOBRISTS[ZOBR_CASTLE + mask.trailing_zeros() as usize];
mask &= mask-1;
}
zobr ^= ZOBRISTS[ZOBR_PASSANT + self.ep as usize];
if zobr != self.hash {
return false;
}
}
let mut newbbs: [u64; 8] = [0; 8];
for i in 0..64 {
if self.mailbox[i] != Pieces::Void as u8 {
newbbs[self.mailbox[i] as usize % 2] |= 1_u64 << i;
newbbs[self.mailbox[i] as usize / 2 + 2] |= 1_u64 << i;
}
}
for (n, o) in zip(newbbs, self.bbs) {
if n != o {
return false;
}
}
true
}
pub fn play(&mut self, mv: u16) {
let from = (mv & 0x3f) as u8;
let to = ((mv >> 6) & 0x3f) as u8;
let promo = (mv >> 12) & 0b11;
let flag = mv >> 14;
let piece: Pieces = self.piece(from as usize);
assert!(piece != Pieces::Void);
let capture: Pieces = self.piece(to as usize);
let _reset: bool = (capture != Pieces::Void && flag != 2) || piece.piecetype() == PieceType::Pawn;
if flag == 0 {
let masktofrom: u64 =
(1_u64 << to as u64) | (1_u64 << from as u64) |
(piece.piecetype() == PieceType::King) as u64 * (0xff_u64 << piece.color() as u64 * 56);
let mut mask: u64 = self.castle_mask & masktofrom;
self.castle_mask ^= mask;
self.update_zobr(ZOBR_CASTLE + mask.trailing_zeros() as usize);
mask &= mask.wrapping_sub(1);
self.update_zobr(ZOBR_CASTLE + mask.trailing_zeros() as usize);
self.update_zobr(self.ep as usize + ZOBR_PASSANT);
self.ep = (piece.piecetype() == PieceType::Pawn && ((to ^ from) & 0b10_000) != 0) as u8 * to as u8;
self.ep += 64*(self.ep == 0) as u8;
self.update_zobr(self.ep as usize + ZOBR_PASSANT);
self.erasesure(from as usize);
self.erase(to as usize);
self.push(to as u32, piece);
} else if flag == 1 {
assert!(self.ep < 64);
assert!(self.mailbox[self.ep as usize]/2 == 0);
self.erasesure(from as usize);
self.erasesure(self.ep as usize);
self.push(to as u32, piece);
self.update_zobr(self.ep as usize + ZOBR_PASSANT);
self.ep = 64;
} else if flag == 2 {
let to_king = (from&56) | KINGPOS_CASTLE[(from < to) as usize];
let to_rook = (from&56) | ROOKPOS_CASTLE[(from < to) as usize];
let mut mask: u64 = (0xff << (from & 56) as u64) & self.castle_mask;
self.castle_mask ^= mask;
self.update_zobr(ZOBR_CASTLE + mask.trailing_zeros() as usize);
mask &= mask.wrapping_sub(1);
self.update_zobr(ZOBR_CASTLE + mask.trailing_zeros() as usize);
self.erasesure(from as usize);
self.erasesure(to as usize);
self.push(to_king as u32, piece);
self.push(to_rook as u32, Pieces::from_pc_color(PieceType::Rook, piece.color()));
self.update_zobr(self.ep as usize + ZOBR_PASSANT);
self.ep = 64;
} else {
assert!(flag == 3);
let to_piece = Pieces::from_pc_color(((promo+1) as u8).try_into().unwrap(), piece.color());
if capture.piecetype() == PieceType::Rook && (self.castle_mask & (1 << to as u64) != 0){
self.update_zobr(ZOBR_CASTLE + to as usize);
self.castle_mask &= !(1 << to as u64);
}
self.update_zobr(self.ep as usize + ZOBR_PASSANT);
self.ep = 64;
self.erasesure(from as usize);
self.erase(to as usize);
self.push(to as u32, to_piece);
}
self.turn_stm();
}
pub fn tobullet(&self) -> ChessBoard {
let occ;
if self.stm {
occ = self.occupancy().swap_bytes();
} else {
occ = self.occupancy();
}
let mut pcs = [0; 16];
let mut idx = 0;
let mut occ2 = occ;
while occ2 > 0 {
let sq = occ2.trailing_zeros();
occ2 &= occ2 - 1;
let fullpiece = self.mailbox[sq as usize ^ 56 * self.stm as usize];
let colour = (fullpiece % 2) ^ self.stm as u8;
let piece = fullpiece / 2;
let pc = (colour << 3) | piece as u8;
pcs[idx / 2] |= pc << (4 * (idx & 1));
idx += 1;
}
let ksq = (self.bbs[ self.stm as usize] & self.bbs[7]).trailing_zeros() as u8 ^ (56 * self.stm as u8);
let opp_ksq = (self.bbs[!self.stm as usize] & self.bbs[7]).trailing_zeros() as u8 ^ (56 * !self.stm as u8);
ChessBoard {
occ,
pcs,
score:self.score,
result:self.bound,
ksq,
opp_ksq,
extra: [0; 3],
}
}
}