use std::fmt;
use board::*;
pub type MoveType = usize;
pub const MOVE_ENPASSANT: MoveType = 0;
pub const MOVE_PROMOTION: MoveType = 1;
pub const MOVE_CASTLING: MoveType = 2;
pub const MOVE_NORMAL: MoveType = 3;
#[derive(Debug)]
#[derive(Clone, Copy)]
#[derive(PartialEq, Eq)]
pub struct MoveDigest(u16);
impl MoveDigest {
#[inline]
pub fn invalid() -> MoveDigest {
MoveDigest(0)
}
#[inline]
pub fn move_type(&self) -> MoveType {
(self.0 >> SHIFT_MOVE_TYPE & 3) as MoveType
}
#[inline]
pub fn orig_square(&self) -> Square {
(self.0 >> SHIFT_ORIG_SQUARE & 63) as Square
}
#[inline]
pub fn dest_square(&self) -> Square {
(self.0 >> SHIFT_DEST_SQUARE & 63) as Square
}
#[inline]
pub fn aux_data(&self) -> usize {
(self.0 >> SHIFT_AUX_DATA & 3) as usize
}
pub fn notation(&self) -> String {
format!("{}{}{}",
notation(self.orig_square()),
notation(self.dest_square()),
match self.move_type() {
MOVE_PROMOTION => ["q", "r", "b", "n"][self.aux_data()],
_ => "",
})
}
#[inline]
pub fn is_null(&self) -> bool {
self.orig_square() == self.dest_square() && self.move_type() == MOVE_NORMAL
}
}
impl fmt::Display for MoveDigest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.notation())
}
}
#[derive(Debug)]
#[derive(Clone, Copy)]
#[derive(PartialOrd, Ord, PartialEq, Eq)]
pub struct Move(u64);
impl Move {
#[inline(always)]
pub fn new(move_type: MoveType,
orig_square: Square,
dest_square: Square,
aux_data: usize,
captured_piece: PieceType,
played_piece: PieceType,
castling_rights: CastlingRights,
enpassant_file: usize,
score: u32)
-> Move {
debug_assert!(move_type <= 0x11);
debug_assert!(played_piece < PIECE_NONE);
debug_assert!(orig_square <= 63);
debug_assert!(dest_square <= 63);
debug_assert!(captured_piece != KING && captured_piece <= PIECE_NONE);
debug_assert!(enpassant_file <= 8);
debug_assert!(aux_data <= 3);
debug_assert!(move_type == MOVE_PROMOTION || aux_data == 0);
debug_assert!(orig_square != dest_square ||
move_type == MOVE_NORMAL && captured_piece == PIECE_NONE);
Move( (score as u64) << SHIFT_SCORE |
(move_type << SHIFT_MOVE_TYPE | aux_data << SHIFT_AUX_DATA) as u64 |
(
castling_rights.value() << SHIFT_CASTLING_RIGHTS |
enpassant_file << SHIFT_ENPASSANT_FILE |
played_piece << SHIFT_PIECE |
orig_square << SHIFT_ORIG_SQUARE |
(!captured_piece & 7) << SHIFT_CAPTURED_PIECE |
dest_square << SHIFT_DEST_SQUARE) as u64)
}
#[inline]
pub fn invalid() -> Move {
Move(((!PIECE_NONE & 7) << SHIFT_CAPTURED_PIECE | KING << SHIFT_PIECE) as u64)
}
#[inline]
pub fn piece_from_aux_data(pp_code: usize) -> PieceType {
debug_assert!(pp_code <= 3);
QUEEN + pp_code
}
#[inline]
pub fn set_score(&mut self, score: u32) {
const MASK_SCORE: u64 = (::std::u32::MAX as u64) << SHIFT_SCORE;
self.0 &= !MASK_SCORE;
self.0 |= (score as u64) << SHIFT_SCORE;
}
#[inline]
pub fn score(&self) -> u32 {
(self.0 >> SHIFT_SCORE) as u32
}
#[inline]
pub fn move_type(&self) -> MoveType {
self.0 as usize >> SHIFT_MOVE_TYPE & 3
}
#[inline]
pub fn played_piece(&self) -> PieceType {
self.0 as usize >> SHIFT_PIECE & 7
}
#[inline]
pub fn orig_square(&self) -> Square {
self.0 as usize >> SHIFT_ORIG_SQUARE & 63
}
#[inline]
pub fn dest_square(&self) -> Square {
self.0 as usize >> SHIFT_DEST_SQUARE & 63
}
#[inline]
pub fn captured_piece(&self) -> PieceType {
!(self.0 as usize) >> SHIFT_CAPTURED_PIECE & 7
}
#[inline]
pub fn enpassant_file(&self) -> usize {
self.0 as usize >> SHIFT_ENPASSANT_FILE & 15
}
#[inline]
pub fn castling_rights(&self) -> CastlingRights {
CastlingRights::new(self.0 as usize >> SHIFT_CASTLING_RIGHTS)
}
#[inline]
pub fn aux_data(&self) -> usize {
self.0 as usize >> SHIFT_AUX_DATA & 3
}
#[inline]
pub fn digest(&self) -> MoveDigest {
MoveDigest(self.0 as u16)
}
pub fn notation(&self) -> String {
format!("{}{}{}",
notation(self.orig_square()),
notation(self.dest_square()),
match self.move_type() {
MOVE_PROMOTION => ["q", "r", "b", "n"][self.aux_data()],
_ => "",
})
}
#[inline]
pub fn is_pawn_advance_or_capure(&self) -> bool {
const MASK_PIECE: usize = 7 << SHIFT_PIECE;
const MASK_CAPTURED_PIECE: usize = 7 << SHIFT_CAPTURED_PIECE;
const P: usize = (!PAWN & 7) << SHIFT_PIECE;
const C: usize = (!PIECE_NONE & 7) << SHIFT_CAPTURED_PIECE;
let v = self.0 as usize;
(v & MASK_PIECE | C) ^ (v & MASK_CAPTURED_PIECE | P) >= MASK_PIECE
}
#[inline]
pub fn is_null(&self) -> bool {
debug_assert!(self.orig_square() != self.dest_square() || self.played_piece() == KING);
self.orig_square() == self.dest_square() && self.move_type() == MOVE_NORMAL
}
}
impl fmt::Display for Move {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.notation())
}
}
pub trait AddMove {
fn add_move(&mut self, m: Move);
}
impl AddMove for Vec<Move> {
#[inline]
fn add_move(&mut self, m: Move) {
self.push(m);
}
}
const SHIFT_SCORE: usize = 32;
const SHIFT_CAPTURED_PIECE: usize = 27;
const SHIFT_PIECE: usize = 24;
const SHIFT_CASTLING_RIGHTS: usize = 20;
const SHIFT_ENPASSANT_FILE: usize = 16;
const SHIFT_MOVE_TYPE: usize = 14;
const SHIFT_ORIG_SQUARE: usize = 8;
const SHIFT_DEST_SQUARE: usize = 2;
const SHIFT_AUX_DATA: usize = 0;
fn notation(square: Square) -> &'static str {
lazy_static! {
static ref NOTATION: Vec<String> = (0..64).map(|i| format!("{}{}",
["a", "b", "c", "d", "e", "f", "g", "h"][Board::file(i)],
["1", "2", "3", "4", "5", "6", "7", "8"][Board::rank(i)])
).collect();
}
NOTATION[square].as_str()
}
#[cfg(test)]
mod tests {
use super::*;
use squares::*;
#[test]
fn moves() {
let cr = CastlingRights::new(0b1011);
let mut m = Move::new(MOVE_NORMAL, E2, E4, 0, PIECE_NONE, PAWN, cr, 8, 0);
let m1 = Move::new(MOVE_NORMAL, F3, E4, 0, KNIGHT, PAWN, cr, 8, ::std::u32::MAX);
let m2 = Move::new(MOVE_NORMAL, F3, E4, 0, PIECE_NONE, KING, cr, 8, 0);
let m3 = Move::new(MOVE_PROMOTION, F2, F1, 1, PIECE_NONE, PAWN, cr, 8, 0);
let m4 = Move::new(MOVE_NORMAL, F2, E3, 0, KNIGHT, BISHOP, cr, 8, 0);
let m5 = Move::new(MOVE_NORMAL, F2, F2, 0, PIECE_NONE, KING, cr, 8, 0);
assert!(m1 > m);
assert!(m2 < m);
assert_eq!(m.move_type(), MOVE_NORMAL);
assert_eq!(m.played_piece(), PAWN);
assert_eq!(m.captured_piece(), PIECE_NONE);
assert_eq!(m.orig_square(), E2);
assert_eq!(m.dest_square(), E4);
assert_eq!(m.enpassant_file(), 8);
assert_eq!(m.aux_data(), 0);
assert_eq!(m.castling_rights().value(), 0b1011);
assert_eq!(m.notation(), "e2e4");
assert!(!m.is_null());
assert_eq!(m3.aux_data(), 1);
assert_eq!(Move::piece_from_aux_data(0), QUEEN);
assert_eq!(Move::piece_from_aux_data(1), ROOK);
assert_eq!(Move::piece_from_aux_data(2), BISHOP);
assert_eq!(Move::piece_from_aux_data(3), KNIGHT);
let m_copy = m;
assert_eq!(m, m_copy);
assert_eq!(m.score(), 0);
m.set_score(::std::u32::MAX);
assert_eq!(m.score(), ::std::u32::MAX);
assert!(m > m_copy);
m.set_score(0);
assert_eq!(m.score(), 0);
assert!(m.is_pawn_advance_or_capure());
assert!(m1.is_pawn_advance_or_capure());
assert!(!m2.is_pawn_advance_or_capure());
assert!(m3.is_pawn_advance_or_capure());
assert!(m4.is_pawn_advance_or_capure());
assert!(!m5.is_pawn_advance_or_capure());
assert!(m5.is_null());
assert!(MOVE_NORMAL != 0);
assert!(!Move::invalid().is_null());
assert_eq!(Move::invalid().digest(), MoveDigest::invalid());
assert_eq!(m.digest().move_type(), m.move_type());
assert_eq!(m.digest().orig_square(), m.orig_square());
assert_eq!(m.digest().dest_square(), m.dest_square());
assert_eq!(m.digest().aux_data(), m.aux_data());
}
}