1use 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#[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 #[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 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#[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#[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}