Skip to main content

chess/position/
rights.rs

1use crate::{Player, Square, board::Players, finite::Empty as _, square::File};
2
3use File::*;
4
5#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
6pub struct Castles(pub Players<Sides<Option<File>>>);
7
8crate::finite_set!(
9    /// A square that can hold an en-passant target.
10    EnPassant,
11    EnPassantTable {
12        A3 = 0 as a3,
13        B3 = 1 as b3,
14        C3 = 2 as c3,
15        D3 = 3 as d3,
16        E3 = 4 as e3,
17        F3 = 5 as f3,
18        G3 = 6 as g3,
19        H3 = 7 as h3,
20        A6 = 8 as a6,
21        B6 = 9 as b6,
22        C6 = 10 as c6,
23        D6 = 11 as d6,
24        E6 = 12 as e6,
25        F6 = 13 as f6,
26        G6 = 14 as g6,
27        H6 = 15 as h6,
28    }
29);
30
31// This has a multitude of choices:
32// a) queenside / kingside (based on queen/king starting sides in chess)
33// b) long / short (based on travel of king in chess)
34// c) a-side / h-side (based on left/right-most files in both chess and freestyle) - used in freestyle
35// d) c-file / g-file (where the king lands in both chess and freestyle) - new invention
36//
37// The goal would be to have something that makes intuitive sense for Chess
38// and remains correct for Freestyle.
39//
40// Note that "O-O-O" and "O-O" continue to be used in Freestyle.
41// a) is wrong for Freestyle, c) is rare in Chess, d) is a new invention,
42// even though c) is not quite right in terms of actual king travel in Freestyle,
43// the move notation still reflects it.
44//
45// Also Side is a bit misleading, could also mean what we call Player
46crate::finite_set!(
47    /// A side of the board to castle toward.
48    Side,
49    Sides,
50    SideTable,
51    {
52        King = 0 as king,
53        Queen = 1 as queen,
54    }
55);
56
57impl Castles {
58    #[inline]
59    pub const fn empty() -> Self {
60        Self(Players::EMPTY)
61    }
62
63    #[inline]
64    pub const fn chess() -> Self {
65        use Side::*;
66        let rooks = Sides { queen: Some(Queen.chess_rook()), king: Some(King.chess_rook()) };
67        Self(Players { black: rooks, white: rooks })
68    }
69
70    #[inline]
71    pub const fn chess_compatible(self) -> bool {
72        self.is_subset(Self::chess())
73    }
74
75    #[inline]
76    pub const fn is_subset(self, other: Self) -> bool {
77        // This needs const eq, so can't use the Eq trait and define on Option<T>.
78        const fn file_subset(left: Option<File>, right: Option<File>) -> bool {
79            match (left, right) {
80                (None, _) => true,
81                (Some(left), Some(right)) => left.eq(right),
82                (Some(_), None) => false,
83            }
84        }
85
86        finite_for!(player in Player {
87            finite_for!(side in Side {
88                if !file_subset(self.get(player, side), other.get(player, side)) {
89                    return false;
90                }
91            });
92        });
93
94        true
95    }
96
97    #[inline]
98    pub const fn get(self, player: Player, side: Side) -> Option<File> {
99        self.0.get(player).get(side)
100    }
101
102    #[inline]
103    pub const fn has(self, player: Player, side: Side) -> bool {
104        self.get(player, side).is_some()
105    }
106
107    #[inline]
108    pub const fn set(&mut self, player: Player, side: Side, file: File) {
109        *self.0.get_mut(player).get_mut(side) = Some(file);
110    }
111
112    #[inline]
113    pub fn clear(&mut self, player: Player, side: Side) {
114        self.0[player][side] = None;
115    }
116
117    #[inline]
118    pub fn clear_player(&mut self, player: Player) {
119        self.0[player] = Sides::EMPTY;
120    }
121}
122
123impl EnPassant {
124    #[inline]
125    pub const fn from_square(square: Square) -> Option<Self> {
126        use Square::*;
127        Some(match square {
128            A3 => EnPassant::A3,
129            B3 => EnPassant::B3,
130            C3 => EnPassant::C3,
131            D3 => EnPassant::D3,
132            E3 => EnPassant::E3,
133            F3 => EnPassant::F3,
134            G3 => EnPassant::G3,
135            H3 => EnPassant::H3,
136            A6 => EnPassant::A6,
137            B6 => EnPassant::B6,
138            C6 => EnPassant::C6,
139            D6 => EnPassant::D6,
140            E6 => EnPassant::E6,
141            F6 => EnPassant::F6,
142            G6 => EnPassant::G6,
143            H6 => EnPassant::H6,
144            _ => return None,
145        })
146    }
147
148    #[inline]
149    pub const fn square(self) -> Square {
150        use EnPassant::*;
151        match self {
152            A3 => Square::A3,
153            B3 => Square::B3,
154            C3 => Square::C3,
155            D3 => Square::D3,
156            E3 => Square::E3,
157            F3 => Square::F3,
158            G3 => Square::G3,
159            H3 => Square::H3,
160            A6 => Square::A6,
161            B6 => Square::B6,
162            C6 => Square::C6,
163            D6 => Square::D6,
164            E6 => Square::E6,
165            F6 => Square::F6,
166            G6 => Square::G6,
167            H6 => Square::H6,
168        }
169    }
170}
171
172impl Side {
173    #[inline]
174    pub const fn chess_rook(self) -> File {
175        match self {
176            Side::King => H,
177            Side::Queen => A,
178        }
179    }
180
181    #[inline]
182    pub const fn of_rook(king: Square, rook: File) -> Self {
183        use Side::*;
184        if king.file().index() <= rook.index() { King } else { Queen }
185    }
186
187    /// The file the king moves to when castling on this side
188    #[inline]
189    pub const fn king_to_file(self) -> File {
190        use Side::*;
191        match self {
192            King => G,
193            Queen => C,
194        }
195    }
196
197    /// The file the rook moves to when castling on this side.
198    #[inline]
199    pub const fn rook_to_file(self) -> File {
200        use Side::*;
201        match self {
202            King => F,
203            Queen => D,
204        }
205    }
206}
207
208impl TryFrom<Square> for EnPassant {
209    type Error = ();
210
211    fn try_from(square: Square) -> Result<Self, ()> {
212        Self::from_square(square).ok_or(())
213    }
214}
215
216impl From<EnPassant> for Square {
217    fn from(en_passant: EnPassant) -> Self {
218        en_passant.square()
219    }
220}