shakmaty/
color.rs

1//! White or black.
2
3use core::{array, convert::identity, error, fmt, mem, num, ops, str::FromStr};
4
5use crate::{util::out_of_range_error, ByRole, Piece, Rank, Role};
6
7/// `White` or `Black`.
8#[allow(missing_docs)]
9#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
10#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
11pub enum Color {
12    Black = 0,
13    White = 1,
14}
15
16impl Color {
17    pub const fn from_char(ch: char) -> Option<Color> {
18        Some(match ch {
19            'w' => Color::White,
20            'b' => Color::Black,
21            _ => return None,
22        })
23    }
24
25    pub fn char(self) -> char {
26        self.fold_wb('w', 'b')
27    }
28
29    fn from_name(name: &str) -> Option<Color> {
30        Some(match name {
31            "white" => Color::White,
32            "black" => Color::Black,
33            _ => return None,
34        })
35    }
36
37    const fn name(self) -> &'static str {
38        match self {
39            Color::Black => "black",
40            Color::White => "white",
41        }
42    }
43
44    #[inline]
45    pub const fn from_white(white: bool) -> Color {
46        if white {
47            Color::White
48        } else {
49            Color::Black
50        }
51    }
52
53    #[inline]
54    pub const fn from_black(black: bool) -> Color {
55        if black {
56            Color::Black
57        } else {
58            Color::White
59        }
60    }
61
62    #[inline]
63    pub fn fold_wb<T>(self, white: T, black: T) -> T {
64        match self {
65            Color::White => white,
66            Color::Black => black,
67        }
68    }
69
70    #[inline]
71    pub const fn is_white(self) -> bool {
72        matches!(self, Color::White)
73    }
74    #[inline]
75    pub const fn is_black(self) -> bool {
76        matches!(self, Color::Black)
77    }
78
79    /// Same as `!self`, but usable in `const` contexts.
80    #[must_use]
81    pub const fn other(self) -> Color {
82        match self {
83            Color::White => Color::Black,
84            Color::Black => Color::White,
85        }
86    }
87
88    #[inline]
89    pub const fn backrank(self) -> Rank {
90        match self {
91            Color::White => Rank::First,
92            Color::Black => Rank::Eighth,
93        }
94    }
95
96    #[inline]
97    pub const fn relative_rank(self, rank: Rank) -> Rank {
98        match self {
99            Color::White => rank,
100            Color::Black => rank.flip_vertical(),
101        }
102    }
103
104    #[inline]
105    pub const fn pawn(self) -> Piece {
106        Role::Pawn.of(self)
107    }
108
109    #[inline]
110    pub const fn knight(self) -> Piece {
111        Role::Knight.of(self)
112    }
113
114    #[inline]
115    pub const fn bishop(self) -> Piece {
116        Role::Bishop.of(self)
117    }
118
119    #[inline]
120    pub const fn rook(self) -> Piece {
121        Role::Rook.of(self)
122    }
123
124    #[inline]
125    pub const fn queen(self) -> Piece {
126        Role::Queen.of(self)
127    }
128
129    #[inline]
130    pub const fn king(self) -> Piece {
131        Role::King.of(self)
132    }
133
134    /// `White` and `Black`, in this order.
135    pub const ALL: [Color; 2] = [Color::White, Color::Black];
136}
137
138impl ops::Not for Color {
139    type Output = Color;
140
141    #[inline]
142    fn not(self) -> Color {
143        self.fold_wb(Color::Black, Color::White)
144    }
145}
146
147impl ops::BitXor<bool> for Color {
148    type Output = Color;
149
150    #[inline]
151    fn bitxor(self, toggle: bool) -> Color {
152        Color::from_white(self.is_white() ^ toggle)
153    }
154}
155
156impl ops::BitXorAssign<bool> for Color {
157    fn bitxor_assign(&mut self, toggle: bool) {
158        *self = *self ^ toggle;
159    }
160}
161
162impl fmt::Display for Color {
163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164        f.write_str(self.name())
165    }
166}
167
168/// Error when parsing an invalid color name.
169#[derive(Clone, Debug)]
170pub struct ParseColorError;
171
172impl fmt::Display for ParseColorError {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        f.write_str("invalid color")
175    }
176}
177
178impl error::Error for ParseColorError {}
179
180impl FromStr for Color {
181    type Err = ParseColorError;
182
183    fn from_str(s: &str) -> Result<Color, ParseColorError> {
184        Color::from_name(s).ok_or(ParseColorError)
185    }
186}
187
188from_enum_as_int_impl! { Color, u8 i8 u16 i16 u32 i32 u64 i64 u128 i128 usize isize }
189
190macro_rules! try_color_from_int_impl {
191    ($($t:ty)+) => {
192        $(impl core::convert::TryFrom<$t> for Color {
193            type Error = num::TryFromIntError;
194
195            #[inline]
196            fn try_from(value: $t) -> Result<Color, Self::Error> {
197                Ok(match value {
198                    0 => Color::Black,
199                    1 => Color::White,
200                    _ => return Err(out_of_range_error())
201                })
202            }
203        })+
204    }
205}
206
207try_color_from_int_impl! { u8 i8 u16 i16 u32 i32 u64 i64 u128 i128 usize isize }
208
209/// Container with values for each [`Color`].
210#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
211#[derive(Copy, Clone, Default, Eq, PartialEq, Debug, Hash)]
212pub struct ByColor<T> {
213    pub black: T,
214    pub white: T,
215}
216
217impl<T> ByColor<T> {
218    #[inline]
219    pub fn new_with<F>(mut init: F) -> ByColor<T>
220    where
221        F: FnMut(Color) -> T,
222    {
223        ByColor {
224            white: init(Color::White),
225            black: init(Color::Black),
226        }
227    }
228
229    #[inline]
230    pub const fn get(&self, color: Color) -> &T {
231        match color {
232            Color::Black => &self.black,
233            Color::White => &self.white,
234        }
235    }
236
237    #[inline]
238    pub const fn get_mut(&mut self, color: Color) -> &mut T {
239        match color {
240            Color::Black => &mut self.black,
241            Color::White => &mut self.white,
242        }
243    }
244
245    pub const fn swap(&mut self) {
246        mem::swap(&mut self.white, &mut self.black);
247    }
248
249    #[must_use]
250    pub fn into_swapped(self) -> ByColor<T> {
251        ByColor {
252            white: self.black,
253            black: self.white,
254        }
255    }
256
257    #[inline]
258    pub fn for_each<F>(self, mut f: F)
259    where
260        F: FnMut(T),
261    {
262        f(self.white);
263        f(self.black);
264    }
265
266    #[inline]
267    pub fn map<U, F>(self, mut f: F) -> ByColor<U>
268    where
269        F: FnMut(T) -> U,
270    {
271        ByColor {
272            white: f(self.white),
273            black: f(self.black),
274        }
275    }
276
277    #[inline]
278    pub fn find<F>(&self, mut predicate: F) -> Option<Color>
279    where
280        F: FnMut(&T) -> bool,
281    {
282        if predicate(&self.white) {
283            Some(Color::White)
284        } else if predicate(&self.black) {
285            Some(Color::Black)
286        } else {
287            None
288        }
289    }
290
291    #[inline]
292    pub const fn as_ref(&self) -> ByColor<&T> {
293        ByColor {
294            black: &self.black,
295            white: &self.white,
296        }
297    }
298
299    #[inline]
300    pub const fn as_mut(&mut self) -> ByColor<&mut T> {
301        ByColor {
302            black: &mut self.black,
303            white: &mut self.white,
304        }
305    }
306
307    pub fn zip<U>(self, other: ByColor<U>) -> ByColor<(T, U)> {
308        ByColor {
309            black: (self.black, other.black),
310            white: (self.white, other.white),
311        }
312    }
313
314    pub fn zip_color(self) -> ByColor<(Color, T)> {
315        ByColor::new_with(identity).zip(self)
316    }
317
318    pub fn iter(&self) -> array::IntoIter<&T, 2> {
319        self.into_iter()
320    }
321
322    pub fn iter_mut(&mut self) -> array::IntoIter<&mut T, 2> {
323        self.into_iter()
324    }
325}
326
327impl<T: PartialOrd> ByColor<T> {
328    pub fn normalize(&mut self) {
329        if self.white < self.black {
330            self.swap();
331        }
332    }
333
334    #[must_use]
335    pub fn into_normalized(mut self) -> ByColor<T> {
336        self.normalize();
337        self
338    }
339}
340
341impl<T: PartialEq> ByColor<T> {
342    pub fn is_symmetric(&self) -> bool {
343        self.white == self.black
344    }
345}
346
347impl<T: Copy> ByColor<&T> {
348    pub fn copied(self) -> ByColor<T> {
349        self.map(|item| *item)
350    }
351}
352
353impl<T: Clone> ByColor<&T> {
354    pub fn cloned(self) -> ByColor<T> {
355        self.map(Clone::clone)
356    }
357}
358
359impl<T> IntoIterator for ByColor<T> {
360    type Item = T;
361    type IntoIter = array::IntoIter<T, 2>;
362
363    fn into_iter(self) -> Self::IntoIter {
364        [self.white, self.black].into_iter()
365    }
366}
367
368impl<'a, T> IntoIterator for &'a ByColor<T> {
369    type Item = &'a T;
370    type IntoIter = array::IntoIter<&'a T, 2>;
371
372    fn into_iter(self) -> Self::IntoIter {
373        self.as_ref().into_iter()
374    }
375}
376
377impl<'a, T> IntoIterator for &'a mut ByColor<T> {
378    type Item = &'a mut T;
379    type IntoIter = array::IntoIter<&'a mut T, 2>;
380
381    fn into_iter(self) -> Self::IntoIter {
382        self.as_mut().into_iter()
383    }
384}
385
386impl<T> ops::Index<Color> for ByColor<T> {
387    type Output = T;
388
389    #[inline]
390    fn index(&self, index: Color) -> &T {
391        self.get(index)
392    }
393}
394
395impl<T> ops::IndexMut<Color> for ByColor<T> {
396    #[inline]
397    fn index_mut(&mut self, index: Color) -> &mut T {
398        self.get_mut(index)
399    }
400}
401
402impl<T> ByColor<ByRole<T>> {
403    #[inline]
404    pub const fn piece(&self, piece: Piece) -> &T {
405        self.get(piece.color).get(piece.role)
406    }
407
408    #[inline]
409    pub const fn piece_mut(&mut self, piece: Piece) -> &mut T {
410        self.get_mut(piece.color).get_mut(piece.role)
411    }
412
413    pub fn transpose_piece(self) -> ByRole<ByColor<T>> {
414        ByRole {
415            pawn: ByColor {
416                white: self.white.pawn,
417                black: self.black.pawn,
418            },
419            knight: ByColor {
420                white: self.white.knight,
421                black: self.black.knight,
422            },
423            bishop: ByColor {
424                white: self.white.bishop,
425                black: self.black.bishop,
426            },
427            rook: ByColor {
428                white: self.white.rook,
429                black: self.black.rook,
430            },
431            queen: ByColor {
432                white: self.white.queen,
433                black: self.black.queen,
434            },
435            king: ByColor {
436                white: self.white.king,
437                black: self.black.king,
438            },
439        }
440    }
441}
442
443#[cfg(feature = "variant")]
444impl ByColor<ByRole<u8>> {
445    pub(crate) fn count(self) -> usize {
446        self.iter().map(|&role| role.count()).sum()
447    }
448}
449
450impl<T> ops::Index<Piece> for ByColor<ByRole<T>> {
451    type Output = T;
452
453    #[inline]
454    fn index(&self, index: Piece) -> &T {
455        self.piece(index)
456    }
457}
458
459impl<T> ops::IndexMut<Piece> for ByColor<ByRole<T>> {
460    #[inline]
461    fn index_mut(&mut self, index: Piece) -> &mut T {
462        self.piece_mut(index)
463    }
464}