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(¬ation) {
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}