littlewing 0.8.0

A chess engine rated at 2050+ ELO, compatible with both UCI and XBoard protocols, with a nice CLI, and a documented library.
Documentation
use std::prelude::v1::*;
use std::fmt;

use crate::color::*;
use crate::piece::*;
use crate::square::*;
use crate::common::*;
use crate::piece::PieceChar;
use crate::square::SquareExt;

pub const BEST_MOVE_SCORE:    Score = 255;
pub const KILLER_MOVE_SCORE:  Score = 254;
pub const GOOD_CAPTURE_SCORE: Score = 64;
pub const CAPTURE_SCORE:      Score = 2;
pub const QUIET_MOVE_SCORE:   Score = 0;

#[derive(Copy, Clone, Eq, PartialEq)]
pub struct PieceMove(u16);

impl PieceMove {
    pub fn new(from: Square, to: Square, mt: PieceMoveType) -> PieceMove {
        // from: 6 bits
        // to:   6 bits
        // mt:   4 bits
        PieceMove(((from as u16) << 10) | ((to as u16) << 4) | mt as u16)
    }

    // TODO: Rename this `null()` and use NULL_MOVE instead of QUIET_MOVE for
    // the move type and OUT instead of A1 for the from and to squares.
    // That way we can differentiate between a null move that we created and
    // a zeroed move. We could call the later an empty move if needed.
    pub fn new_null() -> PieceMove {
        PieceMove(0)
    }

    pub fn from(self) -> Square {
        (self.0 >> 10) as Square
    }

    pub fn to(self) -> Square {
        ((self.0 >> 4) & 0b111111) as Square
    }

    pub fn kind(self) -> PieceMoveType {
        (self.0 & 0b1111) as PieceMoveType
    }

    // TODO: Compare to `OUT << 10 | OUT << 4 | NULL_MOVE`
    pub fn is_null(self) -> bool {
        self.0 == 0
    }

    pub fn is_capture(self) -> bool {
        // Include three kinds of captures: generic, en passant, and promotion
        // TODO: This also include NULL_MOVE that is not currently used
        self.kind() & CAPTURE_MASK != 0 // && self.kind() != NULL_MOVE
    }

    pub fn is_promotion(self) -> bool {
        self.kind() & PROMOTION_MASK != 0
    }

    pub fn is_noisy(self) -> bool {
        self.kind() & (CAPTURE_MASK | PROMOTION_MASK) != 0
    }

    pub fn is_quiet(self) -> bool {
        !self.is_noisy()
    }

    pub fn is_en_passant(self) -> bool {
        self.kind() == EN_PASSANT
    }

    pub fn is_castle(self) -> bool {
        self.kind() == KING_CASTLE || self.kind() == QUEEN_CASTLE
    }

    pub fn castle_kind(self) -> Piece {
        QUEEN_CASTLE << self.kind() - 1
    }

    pub fn promotion_kind(self) -> Piece {
        PROMOTION_KINDS[(self.kind() & (PROMOTION_KIND_MASK >> 2)) as usize]
    }

    pub fn to_lan(self) -> String {
        self.to_string()
    }

    pub fn to_u16(self) -> u16 {
        self.0
    }

    pub fn from_u16(value: u16) -> Self {
        Self(value)
    }
}

impl fmt::Display for PieceMove {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut out = String::new();
        out.push_str(self.from().to_coord().as_str());
        out.push_str(self.to().to_coord().as_str());
        if self.is_promotion() {
            out.push((BLACK | self.promotion_kind()).to_char());
        }
        write!(f, "{}", out)
    }
}

impl fmt::Debug for PieceMove {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self)
    }
}


#[cfg(test)]
mod tests {
    use crate::piece::*;
    use crate::common::*;
    use super::*;

    #[test]
    fn test_move_is_castle() {
        assert_eq!(PieceMove::new(E2, E3, QUIET_MOVE).is_castle(), false);
        assert_eq!(PieceMove::new(E1, G1, KING_CASTLE).is_castle(), true);
        assert_eq!(PieceMove::new(E1, C1, QUEEN_CASTLE).is_castle(), true);
    }

    #[test]
    fn test_move_castle_kind() {
        assert_eq!(PieceMove::new(E1, G1, KING_CASTLE).castle_kind(), KING);
        assert_eq!(PieceMove::new(E1, C1, QUEEN_CASTLE).castle_kind(), QUEEN);
    }

    #[test]
    fn test_move_is_promotion() {
        assert_eq!(PieceMove::new(E2, E3, QUIET_MOVE).is_promotion(), false);
        assert_eq!(PieceMove::new(E7, E8, QUEEN_PROMOTION).is_promotion(), true);
        assert_eq!(PieceMove::new(E7, D8, QUEEN_PROMOTION_CAPTURE).is_promotion(), true);
    }

    #[test]
    fn test_move_promotion_kind() {
        assert_eq!(PieceMove::new(E7, E8, QUEEN_PROMOTION).promotion_kind(), QUEEN);
        assert_eq!(PieceMove::new(E7, D8, ROOK_PROMOTION_CAPTURE).promotion_kind(), ROOK);
    }
}