myopic_core/pieces/
mod.rs

1use crate::{BitBoard, Side, Square};
2use anyhow::{anyhow, Error, Result};
3use std::fmt::{Display, Formatter};
4use std::str::FromStr;
5use enumset::EnumSetType;
6
7mod kings;
8mod knights;
9mod pawns;
10mod sliding;
11
12/// Value type wrapping a single integer representing one of the 12
13/// different pieces in a game of chess.
14#[derive(Debug, EnumSetType, Ord, PartialOrd, Hash)]
15#[rustfmt::skip]
16pub enum Piece {
17    WP, WN, WB, WR, WQ, WK,
18    BP, BN, BB, BR, BQ, BK,
19}
20
21impl Display for Piece {
22    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
23        write!(f, "{}", format!("{:?}", self).to_lowercase())
24    }
25}
26
27impl FromStr for Piece {
28    type Err = Error;
29
30    fn from_str(s: &str) -> Result<Self, Self::Err> {
31        let lower = s.to_lowercase();
32        Piece::all()
33            .find(|p| p.to_string() == lower)
34            .ok_or(anyhow!("Cannot parse {} as piece", s))
35    }
36}
37
38impl Piece {
39    /// Create an iterator traversing over all pieces in order.
40    pub fn all() -> impl Iterator<Item = Piece> {
41        ALL.iter().cloned()
42    }
43
44    /// Create an iterator traversing over all white pieces in order.
45    pub fn whites() -> impl Iterator<Item = Piece> {
46        WHITE.iter().cloned()
47    }
48
49    /// Create an iterator traversing over all black pieces in order.
50    pub fn blacks() -> impl Iterator<Item = Piece> {
51        BLACK.iter().cloned()
52    }
53
54    /// Returns the king which belongs to the given side.
55    pub fn king(side: Side) -> Piece {
56        match side {
57            Side::White => Piece::WK,
58            Side::Black => Piece::BK,
59        }
60    }
61
62    /// Returns the queen which belongs to the given side.
63    pub fn queen(side: Side) -> Piece {
64        match side {
65            Side::White => Piece::WQ,
66            Side::Black => Piece::BQ,
67        }
68    }
69
70    /// Returns the rook belonging to the given side.
71    pub fn rook(side: Side) -> Piece {
72        match side {
73            Side::White => Piece::WR,
74            Side::Black => Piece::BR,
75        }
76    }
77
78    /// Returns the pawn which belongs to the given side.
79    pub fn pawn(side: Side) -> Piece {
80        match side {
81            Side::White => Piece::WP,
82            Side::Black => Piece::BP,
83        }
84    }
85
86    /// Returns a slice containing all pieces belonging to the given side.
87    pub fn of(side: Side) -> impl Iterator<Item = Piece> {
88        match side {
89            Side::White => (&WHITE).iter().cloned(),
90            Side::Black => (&BLACK).iter().cloned(),
91        }
92    }
93
94    /// Returns the side that this piece belongs to.
95    pub fn side(self) -> Side {
96        if (self as u8) < 6 {
97            Side::White
98        } else {
99            Side::Black
100        }
101    }
102
103    /// Checks whether this piece is either a white or black pawn.
104    pub fn is_pawn(self) -> bool {
105        (self as u8) % 6 == 0
106    }
107
108    /// Checks whether this piece is either a white or black knight.
109    pub fn is_knight(self) -> bool {
110        (self as u8) % 6 == 1
111    }
112
113    /// Computes the control set for this piece given it's location and the
114    /// locations of all the white and black pieces on the board.
115    pub fn control(self, loc: Square, whites: BitBoard, blacks: BitBoard) -> BitBoard {
116        Piece::CONTROL_FN[self as usize](loc, whites, blacks)
117    }
118
119    /// Computes the control set for this piece given it's location on an
120    /// empty board.
121    pub fn empty_control(self, loc: Square) -> BitBoard {
122        self.control(loc, BitBoard::EMPTY, BitBoard::EMPTY)
123    }
124
125    /// Computes the set of legal moves for this piece given it's location
126    /// and the locations of all the white and black pieces on the board.
127    /// Note that this method does not take into account special restrictions
128    /// for or due to the king, e.g. can't move in such a way to put the king
129    /// into check.
130    pub fn moves(self, loc: Square, whites: BitBoard, blacks: BitBoard) -> BitBoard {
131        Piece::MOVE_FN[self as usize](loc, whites, blacks)
132    }
133
134    const CONTROL_FN: [fn(Square, BitBoard, BitBoard) -> BitBoard; 12] = [
135        pawns::white_control,
136        knights::control,
137        sliding::bishops::control,
138        sliding::rooks::control,
139        sliding::queens::control,
140        kings::control,
141        pawns::black_control,
142        knights::control,
143        sliding::bishops::control,
144        sliding::rooks::control,
145        sliding::queens::control,
146        kings::control,
147    ];
148
149    const MOVE_FN: [fn(Square, BitBoard, BitBoard) -> BitBoard; 12] = [
150        pawns::white_moves,
151        knights::white_moves,
152        sliding::bishops::white_moves,
153        sliding::rooks::white_moves,
154        sliding::queens::white_moves,
155        kings::white_moves,
156        pawns::black_moves,
157        knights::black_moves,
158        sliding::bishops::black_moves,
159        sliding::rooks::black_moves,
160        sliding::queens::black_moves,
161        kings::black_moves,
162    ];
163}
164
165/// Constant piece groupings.
166const ALL: [Piece; 12] = [
167    Piece::WP,
168    Piece::WN,
169    Piece::WB,
170    Piece::WR,
171    Piece::WQ,
172    Piece::WK,
173    Piece::BP,
174    Piece::BN,
175    Piece::BB,
176    Piece::BR,
177    Piece::BQ,
178    Piece::BK,
179];
180
181const WHITE: [Piece; 6] = [
182    Piece::WP,
183    Piece::WN,
184    Piece::WB,
185    Piece::WR,
186    Piece::WQ,
187    Piece::WK,
188];
189
190const BLACK: [Piece; 6] = [
191    Piece::BP,
192    Piece::BN,
193    Piece::BB,
194    Piece::BR,
195    Piece::BQ,
196    Piece::BK,
197];
198
199#[cfg(test)]
200mod test {
201    use crate::Piece;
202
203    #[test]
204    fn display() {
205        assert_eq!("wp", Piece::WP.to_string().as_str());
206        assert_eq!("br", Piece::BR.to_string().as_str());
207    }
208
209    #[test]
210    fn from_str() {
211        assert_eq!(Piece::WP, "wp".parse::<Piece>().unwrap());
212        assert_eq!(Piece::WP, "WP".parse::<Piece>().unwrap());
213        assert_eq!(Piece::BQ, "bq".parse::<Piece>().unwrap());
214        assert!("ba".parse::<Piece>().is_err());
215        assert!("bqs".parse::<Piece>().is_err());
216        assert!("wxk".parse::<Piece>().is_err());
217    }
218}