myopic_core/
castlezone.rs

1use crate::bitboard::BitBoard;
2use crate::pieces::Piece;
3use crate::square::Square;
4use crate::Side;
5use anyhow::anyhow;
6use std::fmt::{Display, Formatter};
7use std::str::FromStr;
8use enumset::*;
9
10/// Represents one of the four different areas on a chessboard where
11/// the special castling move can take place (two for each side).
12#[derive(Debug, EnumSetType, PartialOrd, Ord, Hash)]
13#[rustfmt::skip]
14pub enum CastleZone { WK, WQ, BK, BQ }
15
16impl Display for CastleZone {
17    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
18        write!(f, "{}", format!("{:?}", self).to_lowercase())
19    }
20}
21
22impl FromStr for CastleZone {
23    type Err = anyhow::Error;
24
25    fn from_str(s: &str) -> Result<Self, Self::Err> {
26        match s {
27            "wk" | "WK" => Ok(CastleZone::WK),
28            "wq" | "WQ" => Ok(CastleZone::WQ),
29            "bk" | "BK" => Ok(CastleZone::BK),
30            "bq" | "BQ" => Ok(CastleZone::BQ),
31            _ => Err(anyhow!("Cannot parse {} as CastleZone", s)),
32        }
33    }
34}
35
36impl CastleZone {
37    /// Return the side which this zone belongs to.
38    pub fn side(&self) -> Side {
39        match self {
40            CastleZone::WK | CastleZone::WQ => Side::White,
41            CastleZone::BK | CastleZone::BQ => Side::Black,
42        }
43    }
44
45    /// Return the kingside zone for the given side.
46    pub fn kingside(side: Side) -> CastleZone {
47        match side {
48            Side::White => CastleZone::WK,
49            Side::Black => CastleZone::BK,
50        }
51    }
52
53    /// Return the queenside zone for the given side.
54    pub fn queenside(side: Side) -> CastleZone {
55        match side {
56            Side::White => CastleZone::WQ,
57            Side::Black => CastleZone::BQ,
58        }
59    }
60
61    /// Create an iterator traversing all zones in order.
62    pub fn iter() -> impl Iterator<Item = CastleZone> {
63        [
64            CastleZone::WK,
65            CastleZone::WQ,
66            CastleZone::BK,
67            CastleZone::BQ,
68        ]
69        .iter()
70        .cloned()
71    }
72
73    /// Returns a set of exactly two squares which denote the required
74    /// locations of the king and rook in order for the corresponding
75    /// castle move to take place.
76    pub fn source_squares(self) -> BitBoard {
77        match self {
78            CastleZone::WK => Square::E1 | Square::H1,
79            CastleZone::WQ => Square::E1 | Square::A1,
80            CastleZone::BK => Square::E8 | Square::H8,
81            CastleZone::BQ => Square::E8 | Square::A8,
82        }
83    }
84
85    /// Returns a triple containing the rook which moves in the corresponding
86    /// castle move along with it's required start square followed by the
87    /// square it will finish on.
88    pub fn rook_data(self) -> (Piece, Square, Square) {
89        match self {
90            CastleZone::WK => (Piece::WR, Square::H1, Square::F1),
91            CastleZone::BK => (Piece::BR, Square::H8, Square::F8),
92            CastleZone::WQ => (Piece::WR, Square::A1, Square::D1),
93            CastleZone::BQ => (Piece::BR, Square::A8, Square::D8),
94        }
95    }
96
97    /// Returns a triple containing the king which moves in the corresponding
98    /// castle move along with it's required start square followed by the
99    /// square it will finish on.
100    pub fn king_data(self) -> (Piece, Square, Square) {
101        match self {
102            CastleZone::WK => (Piece::WK, Square::E1, Square::G1),
103            CastleZone::BK => (Piece::BK, Square::E8, Square::G8),
104            CastleZone::WQ => (Piece::WK, Square::E1, Square::C1),
105            CastleZone::BQ => (Piece::BK, Square::E8, Square::C8),
106        }
107    }
108
109    /// Returns a set containing the squares which are required to be
110    /// free of any other pieces in order for the corresponding castle
111    /// move to be legal.
112    pub fn unoccupied_requirement(self) -> BitBoard {
113        match self {
114            CastleZone::WK => Square::F1 | Square::G1,
115            CastleZone::WQ => Square::B1 | Square::C1 | Square::D1,
116            CastleZone::BK => Square::F8 | Square::G8,
117            CastleZone::BQ => Square::B8 | Square::C8 | Square::D8,
118        }
119    }
120
121    /// Returns a set containing the squares which are required to be
122    /// free of enemy control in order for the corresponding castle move
123    /// to be legal.
124    pub fn uncontrolled_requirement(self) -> BitBoard {
125        match self {
126            CastleZone::WK => Square::E1 | Square::F1 | Square::G1,
127            CastleZone::WQ => Square::E1 | Square::C1 | Square::D1,
128            CastleZone::BK => Square::E8 | Square::F8 | Square::G8,
129            CastleZone::BQ => Square::E8 | Square::C8 | Square::D8,
130        }
131    }
132
133    /// Lifts this zone to a set of one element.
134    pub fn lift(self) -> EnumSet<CastleZone> {
135        enum_set!(self)
136    }
137}
138
139#[cfg(test)]
140mod test {
141    use crate::CastleZone;
142
143    #[test]
144    fn display() {
145        assert_eq!("wk", CastleZone::WK.to_string().as_str());
146        assert_eq!("wq", CastleZone::WQ.to_string().as_str());
147        assert_eq!("bk", CastleZone::BK.to_string().as_str());
148        assert_eq!("bq", CastleZone::BQ.to_string().as_str());
149    }
150
151    #[test]
152    fn from_str() {
153        assert_eq!(CastleZone::WK, "wk".parse::<CastleZone>().unwrap());
154        assert_eq!(CastleZone::WK, "WK".parse::<CastleZone>().unwrap());
155        assert_eq!(CastleZone::WQ, "wq".parse::<CastleZone>().unwrap());
156        assert_eq!(CastleZone::WQ, "WQ".parse::<CastleZone>().unwrap());
157        assert_eq!(CastleZone::BK, "bk".parse::<CastleZone>().unwrap());
158        assert_eq!(CastleZone::BK, "BK".parse::<CastleZone>().unwrap());
159        assert_eq!(CastleZone::BQ, "bq".parse::<CastleZone>().unwrap());
160        assert_eq!(CastleZone::BQ, "BQ".parse::<CastleZone>().unwrap());
161    }
162}