use core::{fmt, hash, mem, ops, ptr, str};
#[cfg(feature = "simd")]
use core::simd::u8x64;
use consts::PTR_SIZE;
use misc::Contained;
use piece::Piece;
use prelude::*;
use uncon::*;
use util::*;
mod entry;
pub use self::entry::*;
mod iter;
pub use self::iter::*;
#[cfg(all(test, nightly))]
mod benches;
#[cfg(test)]
mod tests;
mod tables {
use super::*;
macro_rules! def_pieces {
($($n:ident => $p:ident),+ $(,)*) => {
$(const $n: u8 = Piece::$p as u8;)+
}
}
def_pieces! {
WP => WhitePawn, BP => BlackPawn,
WN => WhiteKnight, BN => BlackKnight,
WB => WhiteBishop, BB => BlackBishop,
WR => WhiteRook, BR => BlackRook,
WK => WhiteKing, BK => BlackKing,
WQ => WhiteQueen, BQ => BlackQueen,
}
pub const STANDARD: [u8; SQUARE_NUM] = [
WR, WN, WB, WQ, WK, WB, WN, WR,
WP, WP, WP, WP, WP, WP, WP, WP,
NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
BP, BP, BP, BP, BP, BP, BP, BP,
BR, BN, BB, BQ, BK, BB, BN, BR,
];
}
const NONE: u8 = 12;
const SQUARE_NUM: usize = 64;
#[repr(C)]
pub struct PieceMap(Inner);
#[derive(Copy, Clone)]
#[repr(C)]
union Inner {
#[cfg(feature = "simd")]
simd: u8x64,
array: [u8; SQUARE_NUM],
}
impl FromUnchecked<[u8; SQUARE_NUM]> for PieceMap {
#[inline]
unsafe fn from_unchecked(array: [u8; SQUARE_NUM]) -> PieceMap {
PieceMap(Inner { array: array })
}
}
#[cfg(feature = "simd")]
impl FromUnchecked<u8x64> for PieceMap {
#[inline]
unsafe fn from_unchecked(vector: u8x64) -> PieceMap {
PieceMap(Inner { simd: vector })
}
}
impl Clone for PieceMap {
#[inline]
fn clone(&self) -> PieceMap { PieceMap(self.0) }
}
impl PartialEq for PieceMap {
#[inline]
fn eq(&self, other: &PieceMap) -> bool {
#[cfg(feature = "simd")]
{
self as *const _ == other as *const _ ||
self.as_vector() == other.as_vector()
}
#[cfg(not(feature = "simd"))]
{
self.as_bytes()[..] == other.as_bytes()[..]
}
}
}
impl Eq for PieceMap {}
impl Default for PieceMap {
#[inline]
fn default() -> PieceMap {
PieceMap::EMPTY
}
}
impl hash::Hash for PieceMap {
#[inline]
fn hash<H: hash::Hasher>(&self, state: &mut H) {
state.write(self.as_bytes());
}
}
static INDEX_ERR: &'static str = "no piece found for square";
impl ops::Index<Square> for PieceMap {
type Output = Piece;
#[inline]
fn index(&self, sq: Square) -> &Piece {
self.get(sq).expect(INDEX_ERR)
}
}
impl ops::IndexMut<Square> for PieceMap {
#[inline]
fn index_mut(&mut self, sq: Square) -> &mut Piece {
self.get_mut(sq).expect(INDEX_ERR)
}
}
impl fmt::Debug for PieceMap {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_map().entries(self.iter()).finish()
}
}
impl fmt::Display for PieceMap {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.map_str(|s| s.fmt(f))
}
}
impl ::core::iter::FromIterator<(Square, Piece)> for PieceMap {
#[inline]
fn from_iter<T: IntoIterator<Item=(Square, Piece)>>(iter: T) -> PieceMap {
let mut map = PieceMap::new();
map.extend(iter);
map
}
}
impl Extend<(Square, Piece)> for PieceMap {
#[inline]
fn extend<T: IntoIterator<Item=(Square, Piece)>>(&mut self, iter: T) {
for (s, p) in iter.into_iter() {
self.insert(s, p);
}
}
}
impl PieceMap {
pub const EMPTY: PieceMap = PieceMap(Inner { array: [NONE; SQUARE_NUM] });
pub const STANDARD: PieceMap = PieceMap(Inner { array: tables::STANDARD });
#[inline]
pub fn new() -> PieceMap {
PieceMap::default()
}
pub fn from_fen(fen: &str) -> Option<PieceMap> {
let mut map = PieceMap::EMPTY;
let bytes = fen.as_bytes();
let mut rank: usize = 7;
let mut file: usize = 0;
for &byte in bytes {
match byte {
b'/' => {
if file != 8 || rank == 0 {
return None;
}
file = 0;
rank -= 1;
},
b'1'...b'8' => {
file += (byte - b'0') as usize;
if file > 8 {
return None;
}
},
_ => if let Some(pc) = Piece::from_char(byte as char) {
let sq = Square::new(File::from(file),
Rank::from(rank));
map.insert(sq, pc);
file += 1;
} else {
return None;
},
}
}
if rank == 0 && file == 8 {
Some(map)
} else {
None
}
}
#[inline]
pub fn filled(piece: Piece) -> PieceMap {
PieceMap(Inner { array: [piece as u8; SQUARE_NUM] })
}
#[inline]
pub fn from_init<F>(mut init: F) -> PieceMap
where F: FnMut(Square) -> Option<Piece>
{
let mut map: PieceMap = unsafe { mem::uninitialized() };
for i in 0..SQUARE_NUM {
let val = init(i.into()).map(|p| p as u8).unwrap_or(NONE);
unsafe { ptr::write(&mut map.0.array[i], val) };
}
map
}
#[cfg(feature = "simd")]
#[inline]
fn inner(&self) -> &u8x64 { self.as_vector() }
#[cfg(not(feature = "simd"))]
#[inline]
fn inner(&self) -> &[u8; SQUARE_NUM] { self.as_bytes() }
#[inline]
pub fn reverse(&mut self) {
unsafe { self.0.array.reverse() };
}
#[cfg(any(test, feature = "rand"))]
pub fn shuffle<R: ::rand::Rng>(&mut self, rng: &mut R) {
rng.shuffle(unsafe { self.as_bytes_mut() });
}
#[inline]
#[cfg_attr(feature = "simd", allow(dead_code))]
fn inner_ptr_sized(&self) -> &[usize; SQUARE_NUM / PTR_SIZE] {
unsafe { (&self.0).into_unchecked() }
}
#[inline]
fn inner_u64(&self) -> &[u64; 8] {
unsafe { (&self.0).into_unchecked() }
}
#[inline]
fn inner_2d_mut(&mut self) -> &mut [[u8; 8]; 8] {
unsafe { (&mut self.0).into_unchecked() }
}
pub fn mirror_horizontal(&mut self) {
self.inner_2d_mut().reverse();
}
pub fn mirror_vertical(&mut self) {
self.reverse();
self.mirror_horizontal();
}
#[inline]
pub fn first(&self) -> Option<(Square, &Piece)> {
self.iter().next()
}
#[inline]
pub fn first_mut(&mut self) -> Option<(Square, &mut Piece)> {
self.iter_mut().next()
}
#[inline]
pub fn last(&self) -> Option<(Square, &Piece)> {
self.iter().next_back()
}
#[inline]
pub fn last_mut(&mut self) -> Option<(Square, &mut Piece)> {
self.iter_mut().next_back()
}
#[inline]
pub fn fill_rank(&mut self, r: Rank, pc: Piece) {
self.inner_2d_mut()[r as usize] = [pc as u8; 8];
}
#[inline]
unsafe fn replace(&mut self, sq: Square, pc: u8) -> Option<Piece> {
match mem::replace(&mut self.as_bytes_mut()[sq as usize], pc) {
NONE => None,
p => Some(p.into_unchecked())
}
}
#[inline]
pub fn insert(&mut self, sq: Square, pc: Piece) -> Option<Piece> {
unsafe { self.replace(sq, pc as u8) }
}
#[inline]
pub fn remove(&mut self, sq: Square) -> Option<Piece> {
unsafe { self.replace(sq, NONE) }
}
#[inline]
pub fn swap<T: Swap>(&mut self, i: T, j: T) {
T::swap(i, j, self);
}
#[inline]
pub fn relocate(&mut self, from: Square, to: Square) {
let buf = unsafe { self.as_bytes_mut() };
buf[to as usize] = mem::replace(&mut buf[from as usize], NONE);
}
#[inline]
pub fn capture(&mut self, from: Square, to: Square) -> Option<Piece> {
let pc = self.remove(to);
self.swap(from, to);
pc
}
#[inline]
pub fn en_passant(&mut self, from: Square, to: Square) -> Option<Piece> {
let ep = to.combine(from);
let pc = self.remove(ep);
self.relocate(from, to);
pc
}
#[inline]
pub fn castle(&mut self, right: Right) {
use piece::Piece::*;
use square::Square::*;
macro_rules! quad {
($a:expr, $b:expr, $c:expr, $d:expr) => {
(($d as u32) << 24) |
(($c as u32) << 16) |
(($b as u32) << 8) |
( $a as u32)
}
}
struct Values { value: [u32; 4], pairs: [(Square, Square); 4] }
static VALUES: Values = Values {
value: [
quad!(NONE, WhiteRook, WhiteKing, NONE),
quad!(NONE, NONE, WhiteKing, WhiteRook),
quad!(NONE, BlackRook, BlackKing, NONE),
quad!(NONE, NONE, BlackKing, BlackRook),
],
pairs: [(E1, E1), (E1, A1), (E8, E8), (E8, A8)],
};
let (king_sq, start_sq) = VALUES.pairs[right as usize];
self.remove(king_sq);
let buf = unsafe { self.as_bytes_mut() };
let ptr = &mut buf[start_sq as usize] as *mut u8 as *mut u32;
unsafe { *ptr = VALUES.value[right as usize] };
}
#[inline]
pub fn extend_from<F>(&mut self, mut f: F)
where F: FnMut(Square) -> Option<Piece>
{
self.extend(Square::ALL.filter_map(|s| f(s).map(|p| (s, p))));
}
#[inline]
pub fn retain<F>(&mut self, mut f: F)
where F: FnMut(Square, &mut Piece) -> bool
{
let iter = unsafe { self.0.array.iter_mut() };
for (i, slot) in iter.enumerate() {
if *slot != NONE && !f(i.into(), unsafe { slot.into_unchecked() }) {
*slot = NONE;
}
}
}
#[inline]
pub fn clear(&mut self) {
self.0.array = [NONE; SQUARE_NUM];
}
#[inline]
pub fn clear_file(&mut self, file: File) {
for rank in self.inner_2d_mut() {
rank[file as usize] = NONE;
}
}
#[inline]
pub fn clear_rank(&mut self, rank: Rank) {
self.inner_2d_mut()[rank as usize] = [NONE; 8];
}
#[inline]
pub fn is_empty(&self) -> bool {
#[cfg(feature = "simd")]
{
*self.as_vector() == u8x64::splat(NONE)
}
#[cfg(not(feature = "simd"))]
{
let empty = usize::splat(NONE);
for &slot in self.inner_ptr_sized() {
if slot != empty {
return false;
}
}
true
}
}
#[inline]
pub fn len(&self) -> usize {
SQUARE_NUM - self.inner().count(NONE)
}
#[inline]
pub fn count(&self, piece: Piece) -> usize {
self.inner().count(piece as u8)
}
#[inline]
pub fn contains<'a, T: Contained<&'a Self>>(&'a self, value: T) -> bool {
value.contained_in(self)
}
#[inline]
pub fn rank_contains(&self, rank: Rank, pc: Piece) -> bool {
self.inner_u64()[rank as usize].contains_byte(pc as u8)
}
#[inline]
pub fn find(&self, pc: Piece) -> Option<Square> {
::memchr::memchr(pc as u8, self.as_bytes()).map(|index| unsafe {
index.into_unchecked()
})
}
#[inline]
pub fn rfind(&self, pc: Piece) -> Option<Square> {
::memchr::memrchr(pc as u8, self.as_bytes()).map(|index| unsafe {
index.into_unchecked()
})
}
#[inline]
pub fn entry(&mut self, sq: Square) -> Entry {
Entry::from_map(self, sq)
}
#[inline]
pub fn get(&self, sq: Square) -> Option<&Piece> {
match self.as_bytes()[sq as usize] {
NONE => None,
ref p => unsafe { Some(p.into_unchecked()) }
}
}
#[inline]
pub fn get_mut(&mut self, sq: Square) -> Option<&mut Piece> {
unsafe {
match self.0.array[sq as usize] {
NONE => None,
ref mut p => Some(p.into_unchecked())
}
}
}
#[inline]
pub unsafe fn get_unchecked(&self, sq: Square) -> &Piece {
(&self.as_bytes()[sq as usize]).into_unchecked()
}
#[inline]
pub unsafe fn get_unchecked_mut(&mut self, sq: Square) -> &mut Piece {
(&mut self.as_bytes_mut()[sq as usize]).into_unchecked()
}
#[inline]
pub fn color_at(&self, sq: Square) -> Option<Color> {
self.get(sq).map(|p| p.color())
}
#[inline]
pub fn color_at_unchecked(&self, sq: Square) -> Color {
(self.as_bytes()[sq as usize] & 1).into()
}
#[inline]
pub fn role_at(&self, sq: Square) -> Option<Role> {
const NO_PIECE: u8 = NONE >> 1;
match self.as_bytes()[sq as usize] >> 1 {
NO_PIECE => None,
pc => unsafe { Some(mem::transmute(pc)) },
}
}
#[inline]
pub fn map_str<T, F: FnOnce(&mut str) -> T>(&self, f: F) -> T {
let mut buf = *::consts::BOARD_DOTS;
for square in Square::ALL {
if let Some(&piece) = self.get(square) {
let ch = char::from(piece) as u8;
let idx = (0b111000 ^ square as usize) << 1;
unsafe { *buf.get_unchecked_mut(idx) = ch };
}
}
unsafe { f(str::from_utf8_unchecked_mut(&mut buf)) }
}
#[inline]
pub fn map_fen<T, F: FnOnce(&mut str) -> T>(&self, f: F) -> T {
const NUM: usize = 8;
const MAX: usize = NUM * NUM + 7;
let mut len: usize = 0;
unsafe {
let mut buf: [u8; MAX] = mem::uninitialized();
macro_rules! write_buf {
($val:expr) => {
ptr::write(buf.get_unchecked_mut(len), $val);
len += 1;
}
}
for rank in (0..NUM).rev().map(Rank::from) {
let mut n: u8 = 0;
for file in (0..NUM).map(File::from) {
let square = Square::new(file, rank);
if let Some(&pc) = self.get(square) {
if n != 0 {
write_buf!(b'0' + n);
n = 0;
}
write_buf!(char::from(pc) as u8);
} else {
n += 1;
}
}
if n != 0 {
write_buf!(b'0' + n);
}
if rank != Rank::One {
write_buf!(b'/');
}
}
f(str::from_utf8_unchecked_mut(buf.get_unchecked_mut(..len)))
}
}
#[cfg(feature = "std")]
pub fn to_fen(&self) -> String {
self.map_fen(|s| String::from(s as &str))
}
#[inline]
pub fn iter(&self) -> Iter { self.into_iter() }
#[inline]
pub fn iter_mut(&mut self) -> IterMut { self.into_iter() }
#[inline]
pub fn as_bytes(&self) -> &[u8; 64] {
unsafe { &self.0.array }
}
#[inline]
pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8; 64] {
&mut self.0.array
}
#[cfg(feature = "simd")]
#[inline]
pub fn as_vector(&self) -> &u8x64 {
unsafe { &self.0.simd }
}
#[cfg(feature = "simd")]
#[inline]
pub unsafe fn as_vector_mut(&mut self) -> &mut u8x64 {
&mut self.0.simd
}
}
impl<'a> Contained<&'a PieceMap> for Square {
#[inline]
fn contained_in(self, map: &PieceMap) -> bool {
map.as_bytes()[self as usize] != NONE
}
}
impl<'a> Contained<&'a PieceMap> for Piece {
#[inline]
fn contained_in(self, map: &PieceMap) -> bool {
#[cfg(feature = "simd")]
{
(*map.as_vector()).eq(u8x64::splat(self as u8)).any()
}
#[cfg(not(feature = "simd"))]
{
map.find(self).is_some()
}
}
}
pub trait Swap {
fn swap(i: Self, j: Self, map: &mut PieceMap);
}
impl Swap for Square {
#[inline]
fn swap(i: Square, j: Square, map: &mut PieceMap) {
unsafe { map.0.array.swap(i as usize, j as usize) };
}
}
impl Swap for Rank {
#[inline]
fn swap(i: Rank, j: Rank, map: &mut PieceMap) {
map.inner_2d_mut().swap(i as usize, j as usize);
}
}