chessgen/chessboard/
chessboard.rs

1use std::fmt;
2use std::fmt::Write;
3
4use crate::{BitBoard, Generator, IllegalMoveError, Index};
5
6use super::{Color, InvalidChessBoardStringError, InvalidFENStringError, Move, Piece};
7
8/// ChessBoard representation.
9#[derive(Debug, Copy, Clone, PartialEq, Eq)]
10pub struct ChessBoard {
11    /// BitBoards array of pieces.
12    pub pieces: [[BitBoard; Piece::VALUES.len()]; Color::VALUES.len()],
13    /// Color to move.
14    pub next_move: Color,
15    /// Boolean array of castling options.
16    /// Only [*Piece::King] and [*Piece::Queen] is used.
17    pub castling_options: [[bool; 2]; Color::VALUES.len()],
18    /// En-Passant target or none.
19    pub en_passant_target: Option<Index>,
20    /// Half move clock.
21    pub half_move_clock: usize,
22    /// Full move number.
23    pub full_move_number: usize,
24    /// Piece array [Index] for caching piece on a field.
25    piece_cache: [Option<(Color, Piece)>; Index::ALL_FIELDS.len()],
26}
27
28/// Constructs a new empty ChessBoard.
29impl Default for ChessBoard {
30    fn default() -> Self {
31        ChessBoard::EMPTY
32    }
33}
34
35impl ChessBoard {
36    // FEN definition of standard chessboard layout.
37    ///     
38    /// # Examples
39    ///
40    /// ```
41    /// use chessgen::ChessBoard;
42    ///
43    /// assert_eq!(
44    /// ChessBoard::from_fen(ChessBoard::STANDARD_BOARD_FEN).unwrap(),
45    /// ChessBoard::from_string("
46    ///     r n b q k b n r
47    ///     p p p p p p p p
48    ///     - - - - - - - -
49    ///     - - - - - - - -
50    ///     - - - - - - - -
51    ///     - - - - - - - -
52    ///     P P P P P P P P
53    ///     R N B Q K B N R
54    /// ").unwrap());
55    /// ```
56    pub const STANDARD_BOARD_FEN: &'static str =
57        "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
58
59    /// New empty chessboard.
60    ///     
61    /// # Examples
62    ///
63    /// ```
64    /// use chessgen::ChessBoard;
65    ///
66    /// assert_eq!(
67    /// ChessBoard::EMPTY,
68    /// ChessBoard::from_string("
69    ///     - - - - - - - -
70    ///     - - - - - - - -
71    ///     - - - - - - - -
72    ///     - - - - - - - -
73    ///     - - - - - - - -
74    ///     - - - - - - - -
75    ///     - - - - - - - -
76    ///     - - - - - - - -
77    /// ").unwrap());
78    /// ```
79    pub const EMPTY: ChessBoard = ChessBoard::new();
80
81    /// New chessboard with a standard layout.
82    ///     
83    /// # Examples
84    ///
85    /// ```
86    /// use chessgen::ChessBoard;
87    ///
88    /// assert_eq!(
89    /// ChessBoard::STANDARD,
90    /// ChessBoard::from_string("
91    ///     r n b q k b n r
92    ///     p p p p p p p p
93    ///     - - - - - - - -
94    ///     - - - - - - - -
95    ///     - - - - - - - -
96    ///     - - - - - - - -
97    ///     P P P P P P P P
98    ///     R N B Q K B N R
99    /// ").unwrap());
100    /// ```
101    pub const STANDARD: ChessBoard = ChessBoard::new_standard_board();
102
103    /// Constructs a new empty ChessBoard.
104    ///
105    /// # Examples
106    ///
107    /// ```
108    /// use chessgen::{Color, ChessBoard};
109    ///
110    /// let board = ChessBoard::EMPTY;
111    ///
112    /// assert_eq!(board.pieces(Color::White).popcnt(), 0);
113    /// assert_eq!(board.pieces(Color::Black).popcnt(), 0);
114    /// ```
115    #[must_use]
116    const fn new() -> Self {
117        ChessBoard {
118            pieces: [[BitBoard::EMPTY; Piece::VALUES.len()]; Color::VALUES.len()],
119            next_move: Color::White,
120            castling_options: [[false; 2]; Color::VALUES.len()],
121            en_passant_target: None,
122            half_move_clock: 0,
123            full_move_number: 1,
124            piece_cache: [None; Index::ALL_FIELDS.len()],
125        }
126    }
127
128    /// Creates a new chessboard initialized to the standard layout.
129    ///
130    /// # Examples
131    ///
132    /// ```
133    /// use chessgen::ChessBoard;
134    ///
135    /// let board = ChessBoard::STANDARD;
136    ///
137    /// assert_eq!(
138    /// board,
139    /// ChessBoard::from_string("
140    ///     r n b q k b n r
141    ///     p p p p p p p p
142    ///     - - - - - - - -
143    ///     - - - - - - - -
144    ///     - - - - - - - -
145    ///     - - - - - - - -
146    ///     P P P P P P P P
147    ///     R N B Q K B N R
148    /// ").unwrap());
149    /// ```
150    #[must_use]
151    const fn new_standard_board() -> Self {
152        // note: following code could be written in better way using traits operators,
153        // if the function was not const
154
155        let mut pieces = [[BitBoard::EMPTY; Piece::VALUES.len()]; Color::VALUES.len()];
156
157        pieces[Color::White as usize][Piece::King as usize] = Index::E1.as_bitboard();
158        pieces[Color::White as usize][Piece::Queen as usize] = Index::D1.as_bitboard();
159        pieces[Color::White as usize][Piece::Bishop as usize] =
160            BitBoard::new(Index::C1.as_bitboard().state | Index::F1.as_bitboard().state);
161        pieces[Color::White as usize][Piece::Knight as usize] =
162            BitBoard::new(Index::B1.as_bitboard().state | Index::G1.as_bitboard().state);
163        pieces[Color::White as usize][Piece::Rook as usize] =
164            BitBoard::new(Index::A1.as_bitboard().state | Index::H1.as_bitboard().state);
165        pieces[Color::White as usize][Piece::Pawn as usize] = BitBoard::new(
166            Index::A2.as_bitboard().state
167                | Index::B2.as_bitboard().state
168                | Index::C2.as_bitboard().state
169                | Index::D2.as_bitboard().state
170                | Index::E2.as_bitboard().state
171                | Index::F2.as_bitboard().state
172                | Index::G2.as_bitboard().state
173                | Index::H2.as_bitboard().state,
174        );
175
176        pieces[Color::Black as usize][Piece::King as usize] = Index::E8.as_bitboard();
177        pieces[Color::Black as usize][Piece::Queen as usize] = Index::D8.as_bitboard();
178        pieces[Color::Black as usize][Piece::Bishop as usize] =
179            BitBoard::new(Index::C8.as_bitboard().state | Index::F8.as_bitboard().state);
180        pieces[Color::Black as usize][Piece::Knight as usize] =
181            BitBoard::new(Index::B8.as_bitboard().state | Index::G8.as_bitboard().state);
182        pieces[Color::Black as usize][Piece::Rook as usize] =
183            BitBoard::new(Index::A8.as_bitboard().state | Index::H8.as_bitboard().state);
184        pieces[Color::Black as usize][Piece::Pawn as usize] = BitBoard::new(
185            Index::A7.as_bitboard().state
186                | Index::B7.as_bitboard().state
187                | Index::C7.as_bitboard().state
188                | Index::D7.as_bitboard().state
189                | Index::E7.as_bitboard().state
190                | Index::F7.as_bitboard().state
191                | Index::G7.as_bitboard().state
192                | Index::H7.as_bitboard().state,
193        );
194
195        let mut castling_options = [[false; 2]; Color::VALUES.len()];
196        castling_options[Color::White as usize][Piece::Queen as usize] = true;
197        castling_options[Color::White as usize][Piece::King as usize] = true;
198        castling_options[Color::Black as usize][Piece::Queen as usize] = true;
199        castling_options[Color::Black as usize][Piece::King as usize] = true;
200
201        ChessBoard {
202            pieces,
203            next_move: Color::White,
204            castling_options,
205            en_passant_target: None,
206            half_move_clock: 0,
207            full_move_number: 1,
208            piece_cache: ChessBoard::new_piece_cache(&pieces),
209        }
210    }
211
212    /// Returns bitboard of all pieces of a color.
213    ///
214    /// # Examples
215    ///
216    /// ```
217    /// use chessgen::{BitBoard, ChessBoard, Color};
218    ///
219    /// let board = ChessBoard::STANDARD;
220    ///
221    /// assert_eq!(
222    /// board.pieces(Color::White),
223    /// BitBoard::from_string("
224    ///     - - - - - - - -
225    ///     - - - - - - - -
226    ///     - - - - - - - -
227    ///     - - - - - - - -
228    ///     - - - - - - - -
229    ///     - - - - - - - -
230    ///     x x x x x x x x
231    ///     x x x x x x x x
232    /// ").unwrap());
233    /// ```
234    #[inline(always)]
235    #[must_use]
236    pub fn pieces(&self, color: Color) -> BitBoard {
237        self.pieces[*color]
238            .iter()
239            .fold(BitBoard::EMPTY, |res, val| res | *val)
240    }
241
242    /// Returns bitboard of all pieces.
243    ///     
244    /// # Examples
245    ///
246    /// ```
247    /// use chessgen::{BitBoard, ChessBoard};
248    ///
249    /// let board = ChessBoard::STANDARD;
250    ///
251    /// assert_eq!(
252    /// board.all_pieces(),
253    /// BitBoard::from_string("
254    ///     x x x x x x x x
255    ///     x x x x x x x x
256    ///     - - - - - - - -
257    ///     - - - - - - - -
258    ///     - - - - - - - -
259    ///     - - - - - - - -
260    ///     x x x x x x x x
261    ///     x x x x x x x x
262    /// ").unwrap());
263    /// ```
264    #[inline(always)]
265    #[must_use]
266    pub fn all_pieces(&self) -> BitBoard {
267        self.pieces(Color::White) | self.pieces(Color::Black)
268    }
269
270    /// Returns bitboard of pieces which are to move.
271    ///     
272    /// # Examples
273    ///
274    /// ```
275    /// use chessgen::{BitBoard, ChessBoard};
276    ///
277    /// let board = ChessBoard::STANDARD;
278    ///
279    /// assert_eq!(
280    /// board.my_pieces(),
281    /// BitBoard::from_string("
282    ///     - - - - - - - -
283    ///     - - - - - - - -
284    ///     - - - - - - - -
285    ///     - - - - - - - -
286    ///     - - - - - - - -
287    ///     - - - - - - - -
288    ///     x x x x x x x x
289    ///     x x x x x x x x
290    /// ").unwrap());
291    /// ```
292    #[inline(always)]
293    #[must_use]
294    pub fn my_pieces(&self) -> BitBoard {
295        self.pieces(self.next_move)
296    }
297
298    /// Returns bitboard of opponent pieces.
299    ///     
300    /// # Examples
301    ///
302    /// ```
303    /// use chessgen::{BitBoard, ChessBoard};
304    ///
305    /// let board = ChessBoard::STANDARD;
306    ///
307    /// assert_eq!(
308    /// board.opponent_pieces(),
309    /// BitBoard::from_string("
310    ///     x x x x x x x x
311    ///     x x x x x x x x
312    ///     - - - - - - - -
313    ///     - - - - - - - -
314    ///     - - - - - - - -
315    ///     - - - - - - - -
316    ///     - - - - - - - -
317    ///     - - - - - - - -
318    /// ").unwrap());
319    /// ```   
320    #[inline(always)]
321    #[must_use]
322    pub fn opponent_pieces(&self) -> BitBoard {
323        self.pieces(self.next_move.opponent())
324    }
325
326    /// Returns bitboard of filed which we may attack.
327    /// This means all empty and opponent squares.
328    ///
329    /// # Examples
330    ///
331    /// ```
332    /// use chessgen::{BitBoard, ChessBoard};
333    ///
334    /// let board = ChessBoard::STANDARD;
335    ///
336    /// assert_eq!(
337    /// board.board_to_attack(),
338    /// BitBoard::from_string("
339    ///     x x x x x x x x
340    ///     x x x x x x x x
341    ///     x x x x x x x x
342    ///     x x x x x x x x
343    ///     x x x x x x x x
344    ///     x x x x x x x x
345    ///     - - - - - - - -
346    ///     - - - - - - - -
347    /// ").unwrap());
348    /// ```   
349    #[inline(always)]
350    #[must_use]
351    pub fn board_to_attack(&self) -> BitBoard {
352        !self.my_pieces()
353    }
354
355    /// Returns index of my king - king of color which is at move.
356    ///
357    /// # Examples
358    ///
359    /// ```
360    /// use chessgen::{ChessBoard, Index};
361    ///
362    /// let board = ChessBoard::STANDARD;
363    ///
364    /// assert_eq!(
365    /// Index::E1,
366    /// ChessBoard::from_string("
367    ///     r n b q k b n r
368    ///     p p p p p p p p
369    ///     - - - - - - - -
370    ///     - - - - - - - -
371    ///     - - - - - - - -
372    ///     - - - - - - - -
373    ///     P P P P P P P P
374    ///     R N B Q K B N R
375    /// ").unwrap().my_king().unwrap());
376    ///
377    /// assert!(ChessBoard::EMPTY.my_king().is_none());
378    /// ```   
379    #[inline(always)]
380    #[must_use]
381    pub fn my_king(&self) -> Option<Index> {
382        self.pieces[*self.next_move][*Piece::King].bitscan()
383    }
384
385    /// Returns index of opponent's king - king of color which will be on move next turn.
386    ///
387    /// # Examples
388    ///
389    /// ```
390    /// use chessgen::{ChessBoard, Index};
391    ///
392    /// let board = ChessBoard::STANDARD;
393    ///
394    /// assert_eq!(
395    /// Index::E8,
396    /// ChessBoard::from_string("
397    ///     r n b q k b n r
398    ///     p p p p p p p p
399    ///     - - - - - - - -
400    ///     - - - - - - - -
401    ///     - - - - - - - -
402    ///     - - - - - - - -
403    ///     P P P P P P P P
404    ///     R N B Q K B N R
405    /// ").unwrap().opponent_king().unwrap());
406    /// ```   
407    #[inline(always)]
408    #[must_use]
409    pub fn opponent_king(&self) -> Option<Index> {
410        self.pieces[*self.next_move.opponent()][*Piece::King].bitscan()
411    }
412
413    /// Returns piece (without color) on a ChessBoard field.
414    ///
415    /// # Examples
416    ///
417    /// ```
418    /// use chessgen::{ChessBoard, Color, Index, Piece};
419    ///
420    /// assert_eq!(
421    /// (Color::White, Piece::King),
422    /// ChessBoard::from_string("
423    ///     r n b q k b n r
424    ///     p p p p p p p p
425    ///     - - - - - - - -
426    ///     - - - - - - - -
427    ///     - - - - - - - -
428    ///     - - - - - - - -
429    ///     P P P P P P P P
430    ///     R N B Q K B N R
431    /// ").unwrap().piece_at(Index::E1).unwrap());
432    /// ```
433    #[inline(always)]
434    #[must_use]
435    pub fn piece_at(&self, i: Index) -> Option<(Color, Piece)> {
436        self.piece_cache[i.index]
437    }
438
439    /// Construct piece cache which holds information about Piece at ChessBoard Index.
440    #[must_use]
441    pub(super) const fn new_piece_cache(
442        pieces: &[[BitBoard; Piece::VALUES.len()]; Color::VALUES.len()],
443    ) -> [Option<(Color, Piece)>; Index::ALL_FIELDS.len()] {
444        let mut cache = [None; Index::ALL_FIELDS.len()];
445
446        let mut ic = 0;
447        while ic < Color::VALUES.len() {
448            let mut ip = 0;
449            while ip < Piece::VALUES.len() {
450                let mut b = pieces[ic][ip];
451
452                while let (Some(i), next) = b.bitpop() {
453                    b = next;
454                    cache[i.index] = Some((Color::VALUES[ic], Piece::VALUES[ip]));
455                }
456                ip += 1;
457            }
458            ic += 1;
459        }
460
461        cache
462    }
463
464    /// Generate all legal moves for a board
465    ///
466    /// # Examples
467    ///
468    /// ```
469    /// use chessgen::{ChessBoard, Move};
470    ///
471    /// let mut board = ChessBoard::from_string("
472    ///       a b c d e f g h
473    ///     8 r n b q k b n r 8
474    ///     7 p p p p p p p p 7
475    ///     6 - - - - - - - - 6
476    ///     5 - - - - - - - - 5
477    ///     4 - - - - - - - - 4
478    ///     3 - - - - - - - - 3
479    ///     2 P P P P P P P P 2
480    ///     1 R N B Q K B N R 1
481    ///       a b c d e f g h
482    /// ").unwrap();
483    ///
484    /// let moves = board.legal_moves();
485    /// assert_eq!(moves.len(), 20);
486    /// ```
487    pub fn legal_moves(&self) -> Vec<Move> {
488        Generator::G.legal_moves(self)
489    }
490
491    /// Generates all pseudo-legal moves.
492    ///
493    /// See: [ChessProgramming Pseudo Legal Move](https://www.chessprogramming.org/Pseudo-Legal_Move)
494    ///
495    /// # Examples
496    ///
497    /// ```
498    /// use chessgen::{BitBoard, ChessBoard, Color, Index};
499    ///
500    /// let mut board = ChessBoard::from_string("
501    ///      - - - - q - - -
502    ///      - - - - - - - -
503    ///      - - - - - - - -
504    ///      - - - - - - - -
505    ///      - - - - - - - -
506    ///      - - - - - - - -
507    ///      - - - - B - - -
508    ///      - - - Q K - - -
509    /// ").unwrap();
510    ///
511    /// let mut moves = Vec::new();
512    ///
513    /// board.moves(&mut |m| {
514    ///    moves.push(m);
515    /// });
516    ///
517    /// assert_eq!(moves.len(), 24);
518    /// assert_eq!(board.legal_moves().len(), 16);
519    /// ```
520    pub fn moves(&self, f: &mut impl FnMut(Move)) {
521        Generator::G.moves(self, f)
522    }
523
524    /// Generate attacks (moves and captures) for one side.
525    ///
526    /// # Examples
527    ///
528    /// ```
529    /// use chessgen::{BitBoard, ChessBoard, Color};
530    ///
531    /// let mut board = ChessBoard::from_string("
532    ///      - - - - - - - -
533    ///      - - - - - - - -
534    ///      - - - - - - - -
535    ///      - - - q k - - -
536    ///      - - - - - - - -
537    ///      - - - - - - - -
538    ///      - - - - - - - -
539    ///      - - - Q K - - -
540    /// ").unwrap();
541    ///
542    /// assert_eq!(
543    ///     board.attacks(Color::White),
544    ///     BitBoard::from_string("
545    ///      - - - - - - - -
546    ///      - - - - - - - -
547    ///      - - - - - - - -
548    ///      - - - x - - - x
549    ///      x - - x - - x -
550    ///      - x - x - x - -
551    ///      - - x x x x - -
552    ///      x x x x x x - -
553    ///     ").unwrap()
554    /// );
555    ///
556    /// assert_eq!(
557    ///     board.attacks(Color::Black),
558    ///     BitBoard::from_string("
559    ///      x - - x - - x -
560    ///      - x - x - x - -
561    ///      - - x x x x - -
562    ///      x x x x x x - -
563    ///      - - x x x x - -
564    ///      - x - x - x - -
565    ///      x - - x - - x -
566    ///      - - - x - - - x
567    ///     ").unwrap()
568    /// );
569    /// ```
570    pub fn attacks(&self, color: Color) -> BitBoard {
571        Generator::G.attacks(self, color)
572    }
573
574    /// Checks if opponent king is under check.
575    ///
576    /// # Examples
577    ///
578    /// ```
579    /// use chessgen::{BitBoard, ChessBoard, Color, Index};
580    ///
581    /// let mut board = ChessBoard::from_string("
582    ///      - - - - - - - -
583    ///      - - - - - - - -
584    ///      - - - - - - - -
585    ///      - - - k - - - -
586    ///      - - - - - - - -
587    ///      - - - - - - - -
588    ///      - - - - - - - -
589    ///      - - - Q K - - -
590    /// ").unwrap();
591    ///
592    ///
593    /// assert!(board.is_opponent_king_under_check());
594    /// ```
595    pub fn is_opponent_king_under_check(&self) -> bool {
596        Generator::G.is_opponent_king_under_check(self)
597    }
598
599    /// Checks if BitMask is under attack by a side.
600    ///
601    /// # Examples
602    ///
603    /// ```
604    /// use chessgen::{BitBoard, ChessBoard, Color, Index};
605    ///
606    /// let mut board = ChessBoard::from_string("
607    ///      - - - - - - - -
608    ///      - - - - - - - -
609    ///      - - - - - - - -
610    ///      - - - q k - - -
611    ///      - - - - - - - -
612    ///      - - - - - - - -
613    ///      - - - - - - - -
614    ///      - - - Q K - - -
615    /// ").unwrap();
616    ///
617    ///
618    /// assert!(!board.is_bitmask_under_attack(Color::White, Index::A8 | Index::B8));
619    /// assert!(board.is_bitmask_under_attack(Color::White, Index::A4 | Index::A5));
620    /// ```
621    pub fn is_bitmask_under_attack(&self, color: Color, b: BitBoard) -> bool {
622        Generator::G.is_bitmask_under_attack(self, color, b)
623    }
624
625    /// Validate and apply move.
626    ///
627    /// # Examples
628    ///
629    /// ```
630    /// use chessgen::{ChessBoard, Generator, Move};
631    ///
632    /// let mut board = ChessBoard::from_string("
633    ///       a b c d e f g h
634    ///     8 r n b q k b n r 8
635    ///     7 p p p p p p p p 7
636    ///     6 - - - - - - - - 6
637    ///     5 - - - - - - - - 5
638    ///     4 - - - - - - - - 4
639    ///     3 - - - - - - - - 3
640    ///     2 P P P P P P P P 2
641    ///     1 R N B Q K B N R 1
642    ///       a b c d e f g h
643    /// ").unwrap();
644    ///
645    /// board = board.validate_and_apply_move(&Move::from_string("b1c3").unwrap()).unwrap();
646    ///
647    /// assert_eq!(
648    ///     board.pieces,
649    ///     ChessBoard::from_string("
650    ///       a b c d e f g h
651    ///     8 r n b q k b n r 8
652    ///     7 p p p p p p p p 7
653    ///     6 - - - - - - - - 6
654    ///     5 - - - - - - - - 5
655    ///     4 - - - - - - - - 4
656    ///     3 - - N - - - - - 3
657    ///     2 P P P P P P P P 2
658    ///     1 R - B Q K B N R 1
659    ///       a b c d e f g h
660    ///     ").unwrap().pieces
661    /// );
662    ///
663    /// assert!(board.validate_and_apply_move(&Move::from_string("a1a8").unwrap()).is_err());
664    /// ```
665    pub fn validate_and_apply_move(&self, m: &Move) -> Result<ChessBoard, IllegalMoveError> {
666        match self.legal_moves().iter().find(|mm| *mm == m) {
667            Some(_) => Ok(self.apply_move(m)),
668            None => Err(IllegalMoveError::IllegalMove(*m)),
669        }
670    }
671
672    /// Apply move to copy of the ChessBoard and return it.
673    /// Move validation is not performed, if the move is not valid,
674    /// results of this operation may be unpredictable.
675    ///
676    /// # Examples
677    ///
678    /// ```
679    /// use chessgen::{ChessBoard, Move};
680    ///
681    /// let m = Move::from_string("e2e4").unwrap();
682    /// let board = ChessBoard::STANDARD.apply_move(&m).pieces;
683    ///
684    /// assert_eq!(
685    /// board,
686    /// ChessBoard::from_string("
687    ///     r n b q k b n r
688    ///     p p p p p p p p
689    ///     - - - - - - - -
690    ///     - - - - - - - -
691    ///     - - - - P - - -
692    ///     - - - - - - - -
693    ///     P P P P - P P P
694    ///     R N B Q K B N R
695    /// ").unwrap().pieces);
696    /// ```
697    #[must_use]
698    pub fn apply_move(&self, m: &Move) -> Self {
699        let (_, piece) = self.piece_at(m.from).unwrap();
700        let color = self.next_move;
701
702        let mut pieces = self.pieces;
703        let mut next_move = self.next_move;
704        let mut castling_options = self.castling_options;
705        let mut en_passant_target = self.en_passant_target;
706        let mut full_move_number = self.full_move_number;
707        let mut half_move_clock = self.half_move_clock + 1;
708        let mut piece_cache = self.piece_cache;
709
710        let is_capture = (self.opponent_pieces() & m.to) != BitBoard::EMPTY;
711        let is_enpassant = piece == Piece::Pawn
712            && match en_passant_target {
713                Some(i) => m.to == i,
714                None => false,
715            };
716
717        // reset enPassant
718        en_passant_target = None;
719
720        // make the move
721        pieces[*next_move][*piece] ^= m.from | m.to;
722        piece_cache[*m.from] = None;
723        piece_cache[*m.to] = Some((color, piece));
724
725        match piece {
726            Piece::Rook => match next_move {
727                Color::White => match m.from {
728                    Index::A1 => castling_options[*next_move][*Piece::Queen] = false,
729                    Index::H1 => castling_options[*next_move][*Piece::King] = false,
730                    _ => {}
731                },
732                Color::Black => match m.from {
733                    Index::A8 => castling_options[*next_move][*Piece::Queen] = false,
734                    Index::H8 => castling_options[*next_move][*Piece::King] = false,
735                    _ => {}
736                },
737            },
738            Piece::King => {
739                castling_options[*next_move][*Piece::Queen] = false;
740                castling_options[*next_move][*Piece::King] = false;
741                match next_move {
742                    Color::White => {
743                        if m.from == Index::E1 {
744                            match m.to {
745                                Index::C1 => {
746                                    pieces[*next_move][*Piece::Rook] ^= Index::A1 | Index::D1;
747                                    piece_cache[*Index::A1] = None;
748                                    piece_cache[*Index::D1] = Some((color, Piece::Rook));
749                                }
750                                Index::G1 => {
751                                    pieces[*next_move][*Piece::Rook] ^= Index::H1 | Index::F1;
752                                    piece_cache[*Index::H1] = None;
753                                    piece_cache[*Index::F1] = Some((color, Piece::Rook));
754                                }
755                                _ => {}
756                            }
757                        }
758                    }
759                    Color::Black => {
760                        if m.from == Index::E8 {
761                            match m.to {
762                                Index::C8 => {
763                                    pieces[*next_move][*Piece::Rook] ^= Index::A8 | Index::D8;
764                                    piece_cache[*Index::A8] = None;
765                                    piece_cache[*Index::D8] = Some((color, Piece::Rook));
766                                }
767                                Index::G8 => {
768                                    pieces[*next_move][*Piece::Rook] ^= Index::H8 | Index::F8;
769                                    piece_cache[*Index::H8] = None;
770                                    piece_cache[*Index::F8] = Some((color, Piece::Rook));
771                                }
772                                _ => {}
773                            }
774                        }
775                    }
776                }
777            }
778            Piece::Pawn => {
779                half_move_clock = 0;
780                if m.to.distance_to(m.from) > 10 {
781                    let i = if next_move == Color::White {
782                        m.from.shifted_north()
783                    } else {
784                        m.from.shifted_south()
785                    };
786                    en_passant_target = Some(i.unwrap());
787                } else if let Some(promotion) = m.promotion {
788                    pieces[*next_move][*Piece::Pawn] ^= m.to;
789
790                    pieces[*next_move][*promotion] ^= m.to;
791                    piece_cache[*m.to] = Some((color, promotion));
792                }
793            }
794            _ => {}
795        }
796
797        if is_enpassant {
798            half_move_clock = 0;
799
800            match next_move {
801                Color::White => {
802                    let i = m.to.shifted_south().unwrap();
803                    pieces[*Color::Black][*Piece::Pawn] ^= i;
804                    piece_cache[*i] = None;
805                }
806                Color::Black => {
807                    let i = m.to.shifted_north().unwrap();
808                    pieces[*Color::White][*Piece::Pawn] ^= i;
809                    piece_cache[*i] = None;
810                }
811            }
812        }
813
814        if is_capture {
815            half_move_clock = 0;
816            let opponent_color = self.next_move.opponent();
817
818            for p in Piece::VALUES {
819                if pieces[*opponent_color][*p].has_bit(m.to) {
820                    pieces[*opponent_color][*p] ^= m.to;
821                    break;
822                }
823            }
824
825            match next_move {
826                Color::White => match m.to {
827                    Index::A8 => castling_options[*Color::Black][*Piece::Queen] = false,
828                    Index::H8 => castling_options[*Color::Black][*Piece::King] = false,
829                    _ => {}
830                },
831                Color::Black => match m.to {
832                    Index::A1 => castling_options[*Color::White][*Piece::Queen] = false,
833                    Index::H1 => castling_options[*Color::White][*Piece::King] = false,
834                    _ => {}
835                },
836            }
837        }
838
839        next_move = self.next_move.opponent();
840
841        if next_move == Color::Black {
842            full_move_number += 1;
843        }
844
845        ChessBoard {
846            pieces,
847            next_move,
848            castling_options,
849            en_passant_target,
850            half_move_clock,
851            full_move_number,
852            piece_cache,
853        }
854    }
855
856    /// Creates new board form a string.
857    /// String may or may not be decorated with coordinates.
858    ///
859    /// # Examples
860    ///
861    /// ```
862    /// use chessgen::ChessBoard;
863    ///
864    /// assert_eq!(
865    ///  ChessBoard::from_string("
866    ///     r n b q k b n r
867    ///     p p p p p p p p
868    ///     - - - - - - - -
869    ///     - - - - - - - -
870    ///     - - - - - - - -
871    ///     - - - - - - - -
872    ///     P P P P P P P P
873    ///     R N B Q K B N R
874    /// ").unwrap(),
875    /// ChessBoard::from_string("
876    ///       a b c d e f g h
877    ///     8 r n b q k b n r 8
878    ///     7 p p p p p p p p 7
879    ///     6 - - - - - - - - 6
880    ///     5 - - - - - - - - 5
881    ///     4 - - - - - - - - 4
882    ///     3 - - - - - - - - 3
883    ///     2 P P P P P P P P 2
884    ///     1 R N B Q K B N R 1
885    ///       a b c d e f g h
886    /// ").unwrap());
887    /// ```
888    pub fn from_string(str: &str) -> Result<ChessBoard, InvalidChessBoardStringError> {
889        let mut pieces = str.replace("a b c d e f g h", "");
890        pieces.retain(|c| !"0123456789 \n".contains(c));
891        pieces = pieces.replace('-', "1");
892
893        if pieces.len() != Index::ALL_FIELDS.len() {
894            return Err(InvalidChessBoardStringError::InvalidString(str.to_string()));
895        }
896
897        pieces += " w KQkq - 0 1";
898
899        match ChessBoard::from_fen(&pieces) {
900            Ok(board) => Ok(board),
901            Err(_) => Err(InvalidChessBoardStringError::InvalidString(str.to_string())),
902        }
903    }
904
905    /// Returns FEN representation of this board.
906    ///
907    /// # Examples
908    ///
909    /// ```
910    /// use chessgen::ChessBoard;
911    ///
912    /// assert_eq!(
913    ///  ChessBoard::STANDARD.to_fen(),
914    ///  "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
915    /// );
916    /// ```
917    #[must_use]
918    pub fn to_fen(&self) -> String {
919        let mut pieces = Vec::new();
920
921        for c in Color::VALUES {
922            for p in Piece::VALUES {
923                pieces.push((self.pieces[*c][*p].mirrored_vertically(), p.to_char(c)))
924            }
925        }
926
927        let mut fen = String::new();
928        let mut spaces: usize = 0;
929
930        let output_spaces = |fen: &mut String, spaces: &mut usize| {
931            if *spaces > 0 {
932                write!(fen, "{}", spaces).unwrap();
933            }
934            *spaces = 0;
935        };
936
937        for i in 0..Index::ALL_FIELDS.len() {
938            if i > 0 && (i % 8) == 0 {
939                output_spaces(&mut fen, &mut spaces);
940                write!(fen, "/").unwrap();
941            }
942
943            if let Some((_, c)) = pieces.iter().find(|p| p.0.has_bit(Index::new(i))) {
944                output_spaces(&mut fen, &mut spaces);
945                write!(fen, "{}", c).unwrap();
946            } else {
947                spaces += 1;
948            }
949        }
950        output_spaces(&mut fen, &mut spaces);
951
952        // next move color
953        write!(fen, " {} ", self.next_move).unwrap();
954
955        // Castling
956        let mut some_castling = false;
957        for c in Color::VALUES {
958            if self.castling_options[*c][*Piece::King] {
959                write!(fen, "{}", Piece::King.to_char(c)).unwrap();
960                some_castling = true;
961            }
962            if self.castling_options[*c][*Piece::Queen] {
963                write!(fen, "{}", Piece::Queen.to_char(c)).unwrap();
964                some_castling = true;
965            }
966        }
967        if !some_castling {
968            write!(fen, "-").unwrap();
969        }
970        write!(fen, " ").unwrap();
971
972        // enPassant
973        if let Some(target) = self.en_passant_target {
974            write!(fen, "{} ", target).unwrap();
975        } else {
976            write!(fen, "- ").unwrap();
977        }
978
979        // clock + move number
980        write!(fen, "{} {}", self.half_move_clock, self.full_move_number).unwrap();
981
982        fen
983    }
984
985    /// Returns ChessBoard from FEN definition.
986    ///
987    /// # Examples
988    ///
989    /// ```
990    /// use chessgen::ChessBoard;
991    ///
992    /// assert_eq!(
993    ///  ChessBoard::STANDARD,
994    ///  ChessBoard::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap()
995    /// );
996    ///
997    /// assert_eq!(
998    ///  ChessBoard::STANDARD,
999    ///  ChessBoard::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR").unwrap()
1000    /// );
1001    /// ```
1002    pub fn from_fen(fen: &str) -> Result<Self, InvalidFENStringError> {
1003        //"8/8/8/8/8/8/8/8"
1004        if fen.len() < 15 {
1005            return Err(InvalidFENStringError::InvalidString(fen.to_string()));
1006        }
1007
1008        let mut pieces = [[BitBoard::EMPTY; Piece::VALUES.len()]; Color::VALUES.len()];
1009        let mut next_move = Color::White;
1010        let mut castling_options = [[true; 2]; Color::VALUES.len()];
1011        let mut en_passant_target = None;
1012        let mut full_move_number = 1;
1013        let mut half_move_clock = 0;
1014
1015        let mut i: usize = 0;
1016
1017        let chars = fen.as_bytes();
1018
1019        fn shift_pieces(
1020            pieces: [[BitBoard; Piece::VALUES.len()]; Color::VALUES.len()],
1021            size: usize,
1022        ) -> [[BitBoard; Piece::VALUES.len()]; Color::VALUES.len()] {
1023            let mut shifted = pieces;
1024
1025            for c in Color::VALUES {
1026                for p in Piece::VALUES {
1027                    shifted[*c][*p] = BitBoard::new(*shifted[*c][*p] << size);
1028                }
1029            }
1030
1031            shifted
1032        }
1033
1034        // pieces
1035        while i < chars.len() {
1036            let c = chars[i];
1037            i += 1;
1038
1039            if c == b' ' {
1040                break;
1041            }
1042
1043            if c == b'/' {
1044                // nothing
1045                continue;
1046            }
1047
1048            if c.is_ascii_digit() {
1049                // shift by number of empty fields
1050                pieces = shift_pieces(pieces, (c - b'0') as usize);
1051            } else {
1052                // shift all pieces by 1
1053                pieces = shift_pieces(pieces, 1);
1054
1055                match Piece::from_char(c as char) {
1056                    Ok((color, piece)) => pieces[*color][*piece] |= Index::A1,
1057                    Err(_) => return Err(InvalidFENStringError::InvalidString(fen.to_string())),
1058                }
1059            }
1060        }
1061
1062        // need to mirror the boards
1063        for c in Color::VALUES {
1064            for p in Piece::VALUES {
1065                pieces[*c][*p] = pieces[*c][*p].mirrored_horizontally();
1066            }
1067        }
1068
1069        // next move
1070        if i < chars.len() {
1071            match Color::from_char(chars[i] as char) {
1072                Ok(color) => next_move = color,
1073                Err(_) => return Err(InvalidFENStringError::InvalidString(fen.to_string())),
1074            }
1075            i += 1;
1076        }
1077
1078        // castling
1079        i += 1;
1080        while i < chars.len() {
1081            let c = chars[i];
1082            i += 1;
1083
1084            match c {
1085                b' ' => break,
1086                b'-' => {}
1087                _ => match Piece::from_char(c as char) {
1088                    Ok((color, piece)) => match piece {
1089                        Piece::King => castling_options[*color][*Piece::King] = true,
1090                        Piece::Queen => castling_options[*color][*Piece::Queen] = true,
1091                        _ => return Err(InvalidFENStringError::InvalidString(fen.to_string())),
1092                    },
1093                    Err(_) => return Err(InvalidFENStringError::InvalidString(fen.to_string())),
1094                },
1095            }
1096        }
1097
1098        // enPassant
1099        let mut notation = String::new();
1100        while i < chars.len() {
1101            let c = chars[i];
1102            i += 1;
1103
1104            match c {
1105                b' ' => break,
1106                b'-' => {}
1107                _ => notation.push(c as char),
1108            }
1109        }
1110        if !notation.is_empty() {
1111            match Index::from_string(&notation) {
1112                Ok(i) => en_passant_target = Some(i),
1113                Err(_) => return Err(InvalidFENStringError::InvalidString(fen.to_string())),
1114            }
1115        }
1116
1117        // half move clock
1118        let mut n: usize = 0;
1119        while i < chars.len() {
1120            let c = chars[i];
1121            i += 1;
1122
1123            match c {
1124                b' ' => break,
1125                b'-' => {}
1126                _ if c.is_ascii_digit() => n = n * 10 + (c - b'0') as usize,
1127                _ => return Err(InvalidFENStringError::InvalidString(fen.to_string())),
1128            }
1129        }
1130        if n > 0 {
1131            half_move_clock = n;
1132        }
1133
1134        // full move number
1135        n = 0;
1136        while i < chars.len() {
1137            let c = chars[i];
1138            i += 1;
1139
1140            match c {
1141                b' ' => break,
1142                b'-' => {}
1143                _ if c.is_ascii_digit() => n = n * 10 + (c - b'0') as usize,
1144                _ => return Err(InvalidFENStringError::InvalidString(fen.to_string())),
1145            }
1146        }
1147        // full move number starts at 1
1148        if n > 0 {
1149            full_move_number = n;
1150        }
1151
1152        // fix castling - rooks
1153        if !pieces[*Color::White][*Piece::Rook].has_bit(Index::A1) {
1154            castling_options[*Color::White][*Piece::Queen] = false;
1155        }
1156        if !pieces[*Color::White][*Piece::Rook].has_bit(Index::H1) {
1157            castling_options[*Color::White][*Piece::King] = false;
1158        }
1159        if !pieces[*Color::Black][*Piece::Rook].has_bit(Index::A8) {
1160            castling_options[*Color::Black][*Piece::Queen] = false;
1161        }
1162        if !pieces[*Color::Black][*Piece::Rook].has_bit(Index::H8) {
1163            castling_options[*Color::Black][*Piece::King] = false;
1164        }
1165
1166        // fix castling - kings
1167        if !pieces[*Color::White][*Piece::King].has_bit(Index::E1) {
1168            castling_options[*Color::White][*Piece::King] = false;
1169            castling_options[*Color::White][*Piece::Queen] = false;
1170        }
1171        if !pieces[*Color::Black][*Piece::King].has_bit(Index::E8) {
1172            castling_options[*Color::Black][*Piece::King] = false;
1173            castling_options[*Color::Black][*Piece::Queen] = false;
1174        }
1175
1176        Ok(ChessBoard {
1177            pieces,
1178            next_move,
1179            castling_options,
1180            en_passant_target,
1181            half_move_clock,
1182            full_move_number,
1183            piece_cache: ChessBoard::new_piece_cache(&pieces),
1184        })
1185    }
1186}
1187
1188/// Display and to_string() for a ChessBoard.
1189///
1190/// # Examples
1191///
1192/// ```
1193/// use chessgen::ChessBoard;
1194///
1195/// assert_eq!(
1196///     ChessBoard::STANDARD,
1197///     ChessBoard::from_string(ChessBoard::STANDARD.to_string().as_str()).unwrap()
1198/// );
1199/// ```
1200impl fmt::Display for ChessBoard {
1201    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1202        const HEADER: &str = "  a b c d e f g h\n";
1203        write!(f, "{}", HEADER)?;
1204
1205        let mut pieces = Vec::new();
1206
1207        for c in Color::VALUES {
1208            for p in Piece::VALUES {
1209                pieces.push((self.pieces[*c][*p].mirrored_vertically(), p.to_char(c)))
1210            }
1211        }
1212
1213        for i in 0..Index::ALL_FIELDS.len() {
1214            if (i % 8) == 0 {
1215                if i > 0 {
1216                    writeln!(f, "{}", 9 - (i / 8))?;
1217                }
1218
1219                write!(f, "{} ", 8 - (i / 8))?;
1220            }
1221
1222            if let Some((_, c)) = pieces.iter().find(|p| p.0.has_bit(Index::new(i))) {
1223                write!(f, "{} ", c)?;
1224            } else {
1225                write!(f, "- ")?;
1226            }
1227        }
1228
1229        write!(f, "1\n{}", HEADER)?;
1230
1231        Ok(())
1232    }
1233}