Skip to main content

chess/
board.rs

1use crate::bitboard::{BitBoard, EMPTY};
2use crate::board_builder::BoardBuilder;
3use crate::castle_rights::CastleRights;
4use crate::chess_move::ChessMove;
5use crate::color::{ALL_COLORS, Color, NUM_COLORS};
6use crate::error::Error;
7use crate::file::File;
8use crate::magic::{
9	between, get_adjacent_files, get_bishop_rays, get_castle_moves, get_file, get_king_moves, get_knight_moves, get_pawn_attacks, get_pawn_dest_double_moves,
10	get_pawn_source_double_moves, get_rank, get_rook_rays,
11};
12use crate::movegen::*;
13use crate::piece::{ALL_PIECES, NUM_PIECES, Piece};
14use crate::square::{ALL_SQUARES, Square};
15use crate::zobrist::Zobrist;
16use std::convert::{TryFrom, TryInto};
17use std::fmt;
18use std::hash::{Hash, Hasher};
19use std::str::FromStr;
20
21/// A representation of a chess board.  That's why you're here, right?
22#[derive(Copy, Clone, PartialEq, Eq, Debug)]
23pub struct Board {
24	pieces: [BitBoard; NUM_PIECES],
25	color_combined: [BitBoard; NUM_COLORS],
26	combined: BitBoard,
27	side_to_move: Color,
28	castle_rights: [CastleRights; NUM_COLORS],
29	pinned: BitBoard,
30	checkers: BitBoard,
31	hash: u64,
32	en_passant: Option<Square>,
33}
34
35/// What is the status of this game?
36#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
37pub enum BoardStatus {
38	Ongoing,
39	Stalemate,
40	Checkmate,
41}
42
43/// Construct the initial position.
44impl Default for Board {
45	#[inline]
46	fn default() -> Board {
47		Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").expect("Valid Position")
48	}
49}
50
51impl Hash for Board {
52	fn hash<H: Hasher>(&self, state: &mut H) {
53		self.hash.hash(state);
54	}
55}
56
57impl Board {
58	/// Construct a new `Board` that is completely empty.
59	/// Note: This does NOT give you the initial position.  Just a blank slate.
60	fn new() -> Board {
61		Board {
62			pieces: [EMPTY; NUM_PIECES],
63			color_combined: [EMPTY; NUM_COLORS],
64			combined: EMPTY,
65			side_to_move: Color::White,
66			castle_rights: [CastleRights::NoRights; NUM_COLORS],
67			pinned: EMPTY,
68			checkers: EMPTY,
69			hash: 0,
70			en_passant: None,
71		}
72	}
73
74	/// Construct a board from a FEN string.
75	///
76	/// ```
77	/// use chess::Board;
78	/// use std::str::FromStr;
79	/// # use chess::Error;
80	///
81	/// # fn main() -> Result<(), Error> {
82	///
83	/// // This is no longer supported
84	/// let init_position = Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1".to_owned()).expect("Valid FEN");
85	/// assert_eq!(init_position, Board::default());
86	///
87	/// // This is the new way
88	/// let init_position_2 = Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")?;
89	/// assert_eq!(init_position_2, Board::default());
90	/// # Ok(())
91	/// # }
92	/// ```
93	#[deprecated(since = "3.1.0", note = "please use `Board::from_str(fen)?` instead")]
94	#[inline]
95	pub fn from_fen(fen: String) -> Option<Board> {
96		Board::from_str(&fen).ok()
97	}
98
99	#[deprecated(since = "3.0.0", note = "please use the MoveGen structure instead.  It is faster and more idiomatic.")]
100	#[inline]
101	pub fn enumerate_moves(&self, moves: &mut [ChessMove; 256]) -> usize {
102		let movegen = MoveGen::new_legal(self);
103		let mut size = 0;
104		for m in movegen {
105			moves[size] = m;
106			size += 1;
107		}
108		size
109	}
110
111	/// Is this game Ongoing, is it Stalemate, or is it Checkmate?
112	///
113	/// ```
114	/// use chess::{Board, BoardStatus, Square, ChessMove};
115	///
116	/// let mut board = Board::default();
117	///
118	/// assert_eq!(board.status(), BoardStatus::Ongoing);
119	///
120	/// board = board.make_move_new(ChessMove::new(Square::E2,
121	///                                            Square::E4,
122	///                                            None));
123	///
124	/// assert_eq!(board.status(), BoardStatus::Ongoing);
125	///
126	/// board = board.make_move_new(ChessMove::new(Square::F7,
127	///                                            Square::F6,
128	///                                            None));
129	///
130	/// assert_eq!(board.status(), BoardStatus::Ongoing);
131	///
132	/// board = board.make_move_new(ChessMove::new(Square::D2,
133	///                                            Square::D4,
134	///                                            None));
135	///
136	/// assert_eq!(board.status(), BoardStatus::Ongoing);
137	///
138	/// board = board.make_move_new(ChessMove::new(Square::G7,
139	///                                            Square::G5,
140	///                                            None));
141	///
142	/// assert_eq!(board.status(), BoardStatus::Ongoing);
143	///
144	/// board = board.make_move_new(ChessMove::new(Square::D1,
145	///                                            Square::H5,
146	///                                            None));
147	///
148	/// assert_eq!(board.status(), BoardStatus::Checkmate);
149	/// ```
150	#[inline]
151	pub fn status(&self) -> BoardStatus {
152		let moves = MoveGen::new_legal(self).len();
153		match moves {
154			0 => {
155				if self.checkers == EMPTY {
156					BoardStatus::Stalemate
157				} else {
158					BoardStatus::Checkmate
159				}
160			}
161			_ => BoardStatus::Ongoing,
162		}
163	}
164
165	/// Grab the "combined" `BitBoard`.  This is a `BitBoard` with every piece.
166	///
167	/// ```
168	/// use chess::{Board, BitBoard, Rank, get_rank};
169	///
170	/// let board = Board::default();
171	///
172	/// let combined_should_be = get_rank(Rank::First) |
173	///                          get_rank(Rank::Second) |
174	///                          get_rank(Rank::Seventh) |
175	///                          get_rank(Rank::Eighth);
176	///
177	/// assert_eq!(*board.combined(), combined_should_be);
178	/// ```
179	#[inline]
180	pub fn combined(&self) -> &BitBoard {
181		&self.combined
182	}
183
184	/// Grab the "color combined" `BitBoard`.  This is a `BitBoard` with every piece of a particular
185	/// color.
186	///
187	/// ```
188	/// use chess::{Board, BitBoard, Rank, get_rank, Color};
189	///
190	/// let board = Board::default();
191	///
192	/// let white_pieces = get_rank(Rank::First) |
193	///                    get_rank(Rank::Second);
194	///
195	/// let black_pieces = get_rank(Rank::Seventh) |
196	///                    get_rank(Rank::Eighth);
197	///
198	/// assert_eq!(*board.color_combined(Color::White), white_pieces);
199	/// assert_eq!(*board.color_combined(Color::Black), black_pieces);
200	/// ```
201	#[inline]
202	pub fn color_combined(&self, color: Color) -> &BitBoard {
203		unsafe { self.color_combined.get_unchecked(color.to_index()) }
204	}
205
206	/// Give me the `Square` the `color` king is on.
207	///
208	/// ```
209	/// use chess::{Board, Square, Color};
210	///
211	/// let board = Board::default();
212	///
213	/// assert_eq!(board.king_square(Color::White), Square::E1);
214	/// assert_eq!(board.king_square(Color::Black), Square::E8);
215	/// ```
216	#[inline]
217	pub fn king_square(&self, color: Color) -> Square {
218		(self.pieces(Piece::King) & self.color_combined(color)).to_square()
219	}
220
221	/// Grab the "pieces" `BitBoard`.  This is a `BitBoard` with every piece of a particular type.
222	///
223	/// ```
224	/// use chess::{Board, BitBoard, Piece, Square};
225	///
226	/// // The rooks should be in each corner of the board
227	/// let rooks = BitBoard::from_square(Square::A1) |
228	///             BitBoard::from_square(Square::H1) |
229	///             BitBoard::from_square(Square::A8) |
230	///             BitBoard::from_square(Square::H8);
231	///
232	/// let board = Board::default();
233	///
234	/// assert_eq!(*board.pieces(Piece::Rook), rooks);
235	/// ```
236	#[inline]
237	pub fn pieces(&self, piece: Piece) -> &BitBoard {
238		unsafe { self.pieces.get_unchecked(piece.to_index()) }
239	}
240
241	/// Grab the `CastleRights` for a particular side.
242	///
243	/// ```
244	/// use chess::{Board, Square, CastleRights, Color, ChessMove};
245	///
246	/// let move1 = ChessMove::new(Square::A2,
247	///                            Square::A4,
248	///                            None);
249	///
250	/// let move2 = ChessMove::new(Square::E7,
251	///                            Square::E5,
252	///                            None);
253	///
254	/// let move3 = ChessMove::new(Square::A1,
255	///                            Square::A2,
256	///                            None);
257	///
258	/// let move4 = ChessMove::new(Square::E8,
259	///                            Square::E7,
260	///                            None);
261	///
262	/// let mut board = Board::default();
263	///
264	/// assert_eq!(board.castle_rights(Color::White), CastleRights::Both);
265	/// assert_eq!(board.castle_rights(Color::Black), CastleRights::Both);
266	///
267	/// board = board.make_move_new(move1)
268	///              .make_move_new(move2)
269	///              .make_move_new(move3)
270	///              .make_move_new(move4);
271	///
272	/// assert_eq!(board.castle_rights(Color::White), CastleRights::KingSide);
273	/// assert_eq!(board.castle_rights(Color::Black), CastleRights::NoRights);
274	/// ```
275	#[inline]
276	pub fn castle_rights(&self, color: Color) -> CastleRights {
277		unsafe { *self.castle_rights.get_unchecked(color.to_index()) }
278	}
279
280	/// Add castle rights for a particular side.  Note: this can create an invalid position.
281	#[deprecated(
282		since = "3.1.0",
283		note = "When doing board setup, use the BoardBuilder structure.  It ensures you don't end up with an invalid position."
284	)]
285	#[inline]
286	pub fn add_castle_rights(&mut self, color: Color, add: CastleRights) {
287		unsafe {
288			*self.castle_rights.get_unchecked_mut(color.to_index()) = self.castle_rights(color).add(add);
289		}
290	}
291
292	/// Remove castle rights for a particular side.
293	///
294	/// ```
295	/// use chess::{Board, CastleRights, Color};
296	///
297	/// let mut board = Board::default();
298	/// assert_eq!(board.castle_rights(Color::White), CastleRights::Both);
299	///
300	/// board.remove_castle_rights(Color::White, CastleRights::KingSide);
301	/// assert_eq!(board.castle_rights(Color::White), CastleRights::QueenSide);
302	/// ```
303	#[deprecated(
304		since = "3.1.0",
305		note = "When doing board setup, use the BoardBuilder structure.  It ensures you don't end up with an invalid position."
306	)]
307	#[inline]
308	pub fn remove_castle_rights(&mut self, color: Color, remove: CastleRights) {
309		unsafe {
310			*self.castle_rights.get_unchecked_mut(color.to_index()) = self.castle_rights(color).remove(remove);
311		}
312	}
313
314	/// Who's turn is it?
315	///
316	/// ```
317	/// use chess::{Board, Color};
318	///
319	/// let mut board = Board::default();
320	/// assert_eq!(board.side_to_move(), Color::White);
321	/// ```
322	#[inline]
323	pub fn side_to_move(&self) -> Color {
324		self.side_to_move
325	}
326
327	/// Grab my `CastleRights`.
328	///
329	/// ```
330	/// use chess::{Board, Color, CastleRights};
331	///
332	/// let mut board = Board::default();
333	/// board.remove_castle_rights(Color::White, CastleRights::KingSide);
334	/// board.remove_castle_rights(Color::Black, CastleRights::QueenSide);
335	///
336	/// assert_eq!(board.my_castle_rights(), board.castle_rights(Color::White));
337	/// ```
338	#[inline]
339	pub fn my_castle_rights(&self) -> CastleRights {
340		self.castle_rights(self.side_to_move())
341	}
342
343	/// Add to my `CastleRights`.  Note: This can make the position invalid.
344	#[deprecated(
345		since = "3.1.0",
346		note = "When doing board setup, use the BoardBuilder structure.  It ensures you don't end up with an invalid position."
347	)]
348	#[inline]
349	pub fn add_my_castle_rights(&mut self, add: CastleRights) {
350		let color = self.side_to_move();
351		#[allow(deprecated)]
352		self.add_castle_rights(color, add);
353	}
354
355	/// Remove some of my `CastleRights`.
356	///
357	/// ```
358	/// use chess::{Board, CastleRights};
359	///
360	/// let mut board = Board::default();
361	/// assert_eq!(board.my_castle_rights(), CastleRights::Both);
362	///
363	/// board.remove_my_castle_rights(CastleRights::KingSide);
364	/// assert_eq!(board.my_castle_rights(), CastleRights::QueenSide);
365	/// ```
366	#[deprecated(
367		since = "3.1.0",
368		note = "When doing board setup, use the BoardBuilder structure.  It ensures you don't end up with an invalid position."
369	)]
370	#[inline]
371	pub fn remove_my_castle_rights(&mut self, remove: CastleRights) {
372		let color = self.side_to_move();
373		#[allow(deprecated)]
374		self.remove_castle_rights(color, remove);
375	}
376
377	/// My opponents `CastleRights`.
378	///
379	/// ```
380	/// use chess::{Board, Color, CastleRights};
381	///
382	/// let mut board = Board::default();
383	/// board.remove_castle_rights(Color::White, CastleRights::KingSide);
384	/// board.remove_castle_rights(Color::Black, CastleRights::QueenSide);
385	///
386	/// assert_eq!(board.their_castle_rights(), board.castle_rights(Color::Black));
387	/// ```
388	#[inline]
389	pub fn their_castle_rights(&self) -> CastleRights {
390		self.castle_rights(!self.side_to_move())
391	}
392
393	/// Add to my opponents `CastleRights`. Note: This can make the position invalid.
394	#[deprecated(
395		since = "3.1.0",
396		note = "When doing board setup, use the BoardBuilder structure.  It ensures you don't end up with an invalid position."
397	)]
398	#[inline]
399	pub fn add_their_castle_rights(&mut self, add: CastleRights) {
400		let color = !self.side_to_move();
401		#[allow(deprecated)]
402		self.add_castle_rights(color, add)
403	}
404
405	/// Remove some of my opponents `CastleRights`.
406	///
407	/// ```
408	/// use chess::{Board, CastleRights};
409	///
410	/// let mut board = Board::default();
411	/// assert_eq!(board.their_castle_rights(), CastleRights::Both);
412	///
413	/// board.remove_their_castle_rights(CastleRights::KingSide);
414	/// assert_eq!(board.their_castle_rights(), CastleRights::QueenSide);
415	/// ```
416	#[deprecated(
417		since = "3.1.0",
418		note = "When doing board setup, use the BoardBuilder structure.  It ensures you don't end up with an invalid position."
419	)]
420	#[inline]
421	pub fn remove_their_castle_rights(&mut self, remove: CastleRights) {
422		let color = !self.side_to_move();
423		#[allow(deprecated)]
424		self.remove_castle_rights(color, remove);
425	}
426
427	/// Add or remove a piece from the bitboards in this struct.
428	fn xor(&mut self, piece: Piece, bb: BitBoard, color: Color) {
429		unsafe {
430			*self.pieces.get_unchecked_mut(piece.to_index()) ^= bb;
431			*self.color_combined.get_unchecked_mut(color.to_index()) ^= bb;
432			self.combined ^= bb;
433			self.hash ^= Zobrist::piece(piece, bb.to_square(), color);
434		}
435	}
436
437	/// For a chess UI: set a piece on a particular square.
438	///
439	/// ```
440	/// use chess::{Board, Piece, Color, Square};
441	///
442	/// let board = Board::default();
443	///
444	/// let new_board = board.set_piece(Piece::Queen,
445	///                                 Color::White,
446	///                                 Square::E4)
447	///                      .expect("Valid Position");
448	///
449	/// assert_eq!(new_board.pieces(Piece::Queen).count(), 3);
450	/// ```
451	#[deprecated(
452		since = "3.1.0",
453		note = "When doing board setup, use the BoardBuilder structure.  It ensures you don't end up with an invalid position."
454	)]
455	#[inline]
456	pub fn set_piece(&self, piece: Piece, color: Color, square: Square) -> Option<Board> {
457		let mut result = *self;
458		let square_bb = BitBoard::from_square(square);
459		match self.piece_on(square) {
460			None => result.xor(piece, square_bb, color),
461			Some(x) => {
462				// remove x from the bitboard
463				if self.color_combined(Color::White) & square_bb == square_bb {
464					result.xor(x, square_bb, Color::White);
465				} else {
466					result.xor(x, square_bb, Color::Black);
467				}
468				// add piece to the bitboard
469				result.xor(piece, square_bb, color);
470			}
471		}
472
473		// If setting this piece down leaves my opponent in check, and it's my move, then the
474		// position is not a valid chess board
475		result.side_to_move = !result.side_to_move;
476		result.update_pin_info();
477		if result.checkers != EMPTY {
478			return None;
479		}
480
481		// undo our damage
482		result.side_to_move = !result.side_to_move;
483		result.update_pin_info();
484
485		Some(result)
486	}
487
488	/// For a chess UI: clear a particular square.
489	///
490	/// ```
491	/// use chess::{Board, Square, Piece};
492	///
493	/// let board = Board::default();
494	///
495	/// let new_board = board.clear_square(Square::A1)
496	///                      .expect("Valid Position");
497	///
498	/// assert_eq!(new_board.pieces(Piece::Rook).count(), 3);
499	/// ```
500	#[deprecated(
501		since = "3.1.0",
502		note = "When doing board setup, use the BoardBuilder structure.  It ensures you don't end up with an invalid position."
503	)]
504	#[inline]
505	pub fn clear_square(&self, square: Square) -> Option<Board> {
506		let mut result = *self;
507		let square_bb = BitBoard::from_square(square);
508		match self.piece_on(square) {
509			None => {}
510			Some(x) => {
511				// remove x from the bitboard
512				if self.color_combined(Color::White) & square_bb == square_bb {
513					result.xor(x, square_bb, Color::White);
514				} else {
515					result.xor(x, square_bb, Color::Black);
516				}
517			}
518		}
519
520		// If setting this piece down leaves my opponent in check, and it's my move, then the
521		// position is not a valid chess board
522		result.side_to_move = !result.side_to_move;
523		result.update_pin_info();
524		if result.checkers != EMPTY {
525			return None;
526		}
527
528		// undo our damage
529		result.side_to_move = !result.side_to_move;
530		result.update_pin_info();
531
532		Some(result)
533	}
534
535	/// Switch the color of the player without actually making a move.  Returns None if the current
536	/// player is in check.
537	///
538	/// Note that this erases the en-passant information, so applying this function twice does not
539	/// always give the same result back.
540	///
541	/// ```
542	/// use chess::{Board, Color};
543	///
544	/// let board = Board::default();
545	///
546	/// assert_eq!(board.side_to_move(), Color::White);
547	///
548	/// let new_board = board.null_move().expect("Valid Position");
549	///
550	/// assert_eq!(new_board.side_to_move(), Color::Black);
551	/// ```
552	#[inline]
553	pub fn null_move(&self) -> Option<Board> {
554		if self.checkers != EMPTY {
555			None
556		} else {
557			let mut result = *self;
558			result.side_to_move = !result.side_to_move;
559			result.remove_ep();
560			result.update_pin_info();
561			Some(result)
562		}
563	}
564
565	/// Does this board "make sense"?
566	/// Do all the pieces make sense, do the bitboards combine correctly, etc?
567	/// This is for sanity checking.
568	///
569	/// ```
570	/// use chess::{Board, Color, Piece, Square};
571	///
572	/// let board = Board::default();
573	///
574	/// assert_eq!(board.is_sane(), true);
575	///
576	/// // Remove the king
577	/// let bad_board = board.clear_square(Square::E1).expect("Valid Position");
578	/// assert_eq!(bad_board.is_sane(), false);
579	/// ```
580	pub fn is_sane(&self) -> bool {
581		// make sure there is no square with multiple pieces on it
582		for x in ALL_PIECES.iter() {
583			for y in ALL_PIECES.iter() {
584				if *x != *y && self.pieces(*x) & self.pieces(*y) != EMPTY {
585					return false;
586				}
587			}
588		}
589
590		// make sure the colors don't overlap, either
591		if self.color_combined(Color::White) & self.color_combined(Color::Black) != EMPTY {
592			return false;
593		}
594
595		// grab all the pieces by OR'ing together each piece() BitBoard
596		let combined = ALL_PIECES.iter().fold(EMPTY, |cur, next| cur | self.pieces(*next));
597
598		// make sure that's equal to the combined bitboard
599		if combined != *self.combined() {
600			return false;
601		}
602
603		// make sure there is exactly one white king
604		if (self.pieces(Piece::King) & self.color_combined(Color::White)).popcnt() != 1 {
605			return false;
606		}
607
608		// make sure there is exactly one black king
609		if (self.pieces(Piece::King) & self.color_combined(Color::Black)).popcnt() != 1 {
610			return false;
611		}
612
613		// make sure the en_passant square has a pawn on it of the right color
614		if let Some(x) = self.en_passant
615			&& self.pieces(Piece::Pawn) & self.color_combined(!self.side_to_move) & BitBoard::from_square(x) == EMPTY
616		{
617			return false;
618		}
619
620		// make sure my opponent is not currently in check (because that would be illegal)
621		let mut board_copy = *self;
622		board_copy.side_to_move = !board_copy.side_to_move;
623		board_copy.update_pin_info();
624		if board_copy.checkers != EMPTY {
625			return false;
626		}
627
628		// for each color, verify that, if they have castle rights, that they haven't moved their
629		// rooks or king
630		for color in ALL_COLORS.iter() {
631			// get the castle rights
632			let castle_rights = self.castle_rights(*color);
633
634			// the castle rights object will tell us which rooks shouldn't have moved yet.
635			// verify there are rooks on all those squares
636			if castle_rights.unmoved_rooks(*color) & self.pieces(Piece::Rook) & self.color_combined(*color) != castle_rights.unmoved_rooks(*color) {
637				return false;
638			}
639			// if we have castle rights, make sure we have a king on the (E, {1,8}) square,
640			// depending on the color
641			if castle_rights != CastleRights::NoRights
642				&& self.pieces(Piece::King) & self.color_combined(*color) != get_file(File::E) & get_rank(color.to_my_backrank())
643			{
644				return false;
645			}
646		}
647
648		// we must make sure the kings aren't touching
649		if get_king_moves(self.king_square(Color::White)) & self.pieces(Piece::King) != EMPTY {
650			return false;
651		}
652
653		// it checks out
654		true
655	}
656
657	/// Get a hash of the board.
658	#[inline]
659	pub fn get_hash(&self) -> u64 {
660		self.hash
661			^ if let Some(ep) = self.en_passant { Zobrist::en_passant(ep.get_file(), !self.side_to_move) } else { 0 }
662			^ Zobrist::castles(self.castle_rights[self.side_to_move.to_index()], self.side_to_move)
663			^ Zobrist::castles(self.castle_rights[(!self.side_to_move).to_index()], !self.side_to_move)
664			^ if self.side_to_move == Color::Black { Zobrist::color() } else { 0 }
665	}
666
667	/// Get a pawn hash of the board (a hash that only changes on color change and pawn moves).
668	///
669	/// Currently not implemented...
670	#[inline]
671	pub fn get_pawn_hash(&self) -> u64 {
672		0
673	}
674
675	/// What piece is on a particular `Square`?  Is there even one?
676	///
677	/// ```
678	/// use chess::{Board, Piece, Square};
679	///
680	/// let board = Board::default();
681	///
682	/// assert_eq!(board.piece_on(Square::A1), Some(Piece::Rook));
683	/// assert_eq!(board.piece_on(Square::D4), None);
684	/// ```
685	#[inline]
686	pub fn piece_on(&self, square: Square) -> Option<Piece> {
687		let opp = BitBoard::from_square(square);
688		if self.combined() & opp == EMPTY {
689			None
690		} else {
691			//naiive algorithm
692			/*
693			for p in ALL_PIECES {
694				if self.pieces(*p) & opp {
695					return p;
696				}
697			} */
698			if (self.pieces(Piece::Pawn) ^ self.pieces(Piece::Knight) ^ self.pieces(Piece::Bishop)) & opp != EMPTY {
699				if self.pieces(Piece::Pawn) & opp != EMPTY {
700					Some(Piece::Pawn)
701				} else if self.pieces(Piece::Knight) & opp != EMPTY {
702					Some(Piece::Knight)
703				} else {
704					Some(Piece::Bishop)
705				}
706			} else {
707				if self.pieces(Piece::Rook) & opp != EMPTY {
708					Some(Piece::Rook)
709				} else if self.pieces(Piece::Queen) & opp != EMPTY {
710					Some(Piece::Queen)
711				} else {
712					Some(Piece::King)
713				}
714			}
715		}
716	}
717
718	/// What color piece is on a particular square?
719	#[inline]
720	pub fn color_on(&self, square: Square) -> Option<Color> {
721		if (self.color_combined(Color::White) & BitBoard::from_square(square)) != EMPTY {
722			Some(Color::White)
723		} else if (self.color_combined(Color::Black) & BitBoard::from_square(square)) != EMPTY {
724			Some(Color::Black)
725		} else {
726			None
727		}
728	}
729
730	/// Unset the en_passant square.
731	fn remove_ep(&mut self) {
732		self.en_passant = None;
733	}
734
735	/// Give me the en_passant square, if it exists.
736	///
737	/// ```
738	/// use chess::{Board, ChessMove, Square};
739	///
740	/// let move1 = ChessMove::new(Square::D2,
741	///                            Square::D4,
742	///                            None);
743	///
744	/// let move2 = ChessMove::new(Square::H7,
745	///                            Square::H5,
746	///                            None);
747	///
748	/// let move3 = ChessMove::new(Square::D4,
749	///                            Square::D5,
750	///                            None);
751	///
752	/// let move4 = ChessMove::new(Square::E7,
753	///                            Square::E5,
754	///                            None);
755	///
756	/// let board = Board::default().make_move_new(move1)
757	///                             .make_move_new(move2)
758	///                             .make_move_new(move3)
759	///                             .make_move_new(move4);
760	///
761	/// assert_eq!(board.en_passant(), Some(Square::E5));
762	/// ```
763	#[inline]
764	pub fn en_passant(self) -> Option<Square> {
765		self.en_passant
766	}
767
768	/// Set the en_passant square.  Note: This must only be called when self.en_passant is already
769	/// None.
770	fn set_ep(&mut self, sq: Square) {
771		// Only set self.en_passant if the pawn can actually be captured next move.
772		if get_adjacent_files(sq.get_file()) & get_rank(sq.get_rank()) & self.pieces(Piece::Pawn) & self.color_combined(!self.side_to_move) != EMPTY {
773			self.en_passant = Some(sq);
774		}
775	}
776
777	/// Is a particular move legal?  This function is very slow, but will work on unsanitized
778	/// input.
779	///
780	/// ```
781	/// use chess::{Board, ChessMove, Square, MoveGen};
782	///
783	/// let move1 = ChessMove::new(Square::E2,
784	///                            Square::E4,
785	///                            None);
786	///
787	/// let move2 = ChessMove::new(Square::E2,
788	///                            Square::E5,
789	///                            None);
790	///
791	/// let board = Board::default();
792	///
793	/// assert_eq!(board.legal(move1), true);
794	/// assert_eq!(board.legal(move2), false);
795	/// ```
796	#[inline]
797	pub fn legal(&self, m: ChessMove) -> bool {
798		MoveGen::new_legal(self).find(|x| *x == m).is_some()
799	}
800
801	/// Make a chess move onto a new board.
802	///
803	/// panic!() if king is captured.
804	///
805	/// ```
806	/// use chess::{Board, ChessMove, Square, Color};
807	///
808	/// let m = ChessMove::new(Square::D2,
809	///                        Square::D4,
810	///                        None);
811	///
812	/// let board = Board::default();
813	/// assert_eq!(board.make_move_new(m).side_to_move(), Color::Black);
814	/// ```
815	#[inline]
816	pub fn make_move_new(&self, m: ChessMove) -> Board {
817		let mut result = *self;
818		result.remove_ep();
819		result.checkers = EMPTY;
820		result.pinned = EMPTY;
821		let source = m.get_source();
822		let dest = m.get_dest();
823
824		let source_bb = BitBoard::from_square(source);
825		let dest_bb = BitBoard::from_square(dest);
826		let move_bb = source_bb ^ dest_bb;
827		let moved = self.piece_on(source).unwrap();
828
829		result.xor(moved, source_bb, self.side_to_move);
830		result.xor(moved, dest_bb, self.side_to_move);
831		if let Some(captured) = self.piece_on(dest) {
832			result.xor(captured, dest_bb, !self.side_to_move);
833		}
834
835		#[allow(deprecated)]
836		result.remove_their_castle_rights(CastleRights::square_to_castle_rights(!self.side_to_move, dest));
837
838		#[allow(deprecated)]
839		result.remove_my_castle_rights(CastleRights::square_to_castle_rights(self.side_to_move, source));
840
841		let opp_king = result.pieces(Piece::King) & result.color_combined(!result.side_to_move);
842
843		let castles = moved == Piece::King && (move_bb & get_castle_moves()) == move_bb;
844
845		let ksq = opp_king.to_square();
846
847		const CASTLE_ROOK_START: [File; 8] = [File::A, File::A, File::A, File::A, File::H, File::H, File::H, File::H];
848		const CASTLE_ROOK_END: [File; 8] = [File::D, File::D, File::D, File::D, File::F, File::F, File::F, File::F];
849
850		if moved == Piece::Knight {
851			result.checkers ^= get_knight_moves(ksq) & dest_bb;
852		} else if moved == Piece::Pawn {
853			if let Some(Piece::Knight) = m.get_promotion() {
854				result.xor(Piece::Pawn, dest_bb, self.side_to_move);
855				result.xor(Piece::Knight, dest_bb, self.side_to_move);
856				result.checkers ^= get_knight_moves(ksq) & dest_bb;
857			} else if let Some(promotion) = m.get_promotion() {
858				result.xor(Piece::Pawn, dest_bb, self.side_to_move);
859				result.xor(promotion, dest_bb, self.side_to_move);
860			} else if (source_bb & get_pawn_source_double_moves()) != EMPTY && (dest_bb & get_pawn_dest_double_moves()) != EMPTY {
861				result.set_ep(dest);
862				result.checkers ^= get_pawn_attacks(ksq, !result.side_to_move, dest_bb);
863			} else if Some(dest.ubackward(self.side_to_move)) == self.en_passant {
864				result.xor(Piece::Pawn, BitBoard::from_square(dest.ubackward(self.side_to_move)), !self.side_to_move);
865				result.checkers ^= get_pawn_attacks(ksq, !result.side_to_move, dest_bb);
866			} else {
867				result.checkers ^= get_pawn_attacks(ksq, !result.side_to_move, dest_bb);
868			}
869		} else if castles {
870			let my_backrank = self.side_to_move.to_my_backrank();
871			let index = dest.get_file().to_index();
872			let start = BitBoard::set(my_backrank, unsafe { *CASTLE_ROOK_START.get_unchecked(index) });
873			let end = BitBoard::set(my_backrank, unsafe { *CASTLE_ROOK_END.get_unchecked(index) });
874			result.xor(Piece::Rook, start, self.side_to_move);
875			result.xor(Piece::Rook, end, self.side_to_move);
876		}
877		// now, lets see if we're in check or pinned
878		let attackers = result.color_combined(result.side_to_move)
879			& ((get_bishop_rays(ksq) & (result.pieces(Piece::Bishop) | result.pieces(Piece::Queen)))
880				| (get_rook_rays(ksq) & (result.pieces(Piece::Rook) | result.pieces(Piece::Queen))));
881
882		for sq in attackers {
883			let between = between(sq, ksq) & result.combined();
884			if between == EMPTY {
885				result.checkers ^= BitBoard::from_square(sq);
886			} else if between.popcnt() == 1 {
887				result.pinned ^= between;
888			}
889		}
890
891		result.side_to_move = !result.side_to_move;
892		result
893	}
894
895	/// Make a chess move onto an already allocated `Board`.
896	///
897	/// panic!() if king is captured.
898	///
899	/// ```
900	/// use chess::{Board, ChessMove, Square, Color};
901	///
902	/// let m = ChessMove::new(Square::D2,
903	///                        Square::D4,
904	///                        None);
905	///
906	/// let board = Board::default();
907	/// let mut result = Board::default();
908	/// board.make_move(m, &mut result);
909	/// assert_eq!(result.side_to_move(), Color::Black);
910	/// ```
911	#[inline]
912	pub fn make_move(&self, m: ChessMove, result: &mut Board) {
913		*result = *self;
914		result.remove_ep();
915		result.checkers = EMPTY;
916		result.pinned = EMPTY;
917		let source = m.get_source();
918		let dest = m.get_dest();
919
920		let source_bb = BitBoard::from_square(source);
921		let dest_bb = BitBoard::from_square(dest);
922		let move_bb = source_bb ^ dest_bb;
923		let moved = self.piece_on(source).unwrap();
924
925		result.xor(moved, source_bb, self.side_to_move);
926		result.xor(moved, dest_bb, self.side_to_move);
927		if let Some(captured) = self.piece_on(dest) {
928			result.xor(captured, dest_bb, !self.side_to_move);
929		}
930
931		#[allow(deprecated)]
932		result.remove_their_castle_rights(CastleRights::square_to_castle_rights(!self.side_to_move, dest));
933
934		#[allow(deprecated)]
935		result.remove_my_castle_rights(CastleRights::square_to_castle_rights(self.side_to_move, source));
936
937		let opp_king = result.pieces(Piece::King) & result.color_combined(!result.side_to_move);
938
939		let castles = moved == Piece::King && (move_bb & get_castle_moves()) == move_bb;
940
941		let ksq = opp_king.to_square();
942
943		const CASTLE_ROOK_START: [File; 8] = [File::A, File::A, File::A, File::A, File::H, File::H, File::H, File::H];
944		const CASTLE_ROOK_END: [File; 8] = [File::D, File::D, File::D, File::D, File::F, File::F, File::F, File::F];
945
946		if moved == Piece::Knight {
947			result.checkers ^= get_knight_moves(ksq) & dest_bb;
948		} else if moved == Piece::Pawn {
949			if let Some(Piece::Knight) = m.get_promotion() {
950				result.xor(Piece::Pawn, dest_bb, self.side_to_move);
951				result.xor(Piece::Knight, dest_bb, self.side_to_move);
952				result.checkers ^= get_knight_moves(ksq) & dest_bb;
953			} else if let Some(promotion) = m.get_promotion() {
954				result.xor(Piece::Pawn, dest_bb, self.side_to_move);
955				result.xor(promotion, dest_bb, self.side_to_move);
956			} else if (source_bb & get_pawn_source_double_moves()) != EMPTY && (dest_bb & get_pawn_dest_double_moves()) != EMPTY {
957				result.set_ep(dest);
958				result.checkers ^= get_pawn_attacks(ksq, !result.side_to_move, dest_bb);
959			} else if Some(dest.ubackward(self.side_to_move)) == self.en_passant {
960				result.xor(Piece::Pawn, BitBoard::from_square(dest.ubackward(self.side_to_move)), !self.side_to_move);
961				result.checkers ^= get_pawn_attacks(ksq, !result.side_to_move, dest_bb);
962			} else {
963				result.checkers ^= get_pawn_attacks(ksq, !result.side_to_move, dest_bb);
964			}
965		} else if castles {
966			let my_backrank = self.side_to_move.to_my_backrank();
967			let index = dest.get_file().to_index();
968			let start = BitBoard::set(my_backrank, unsafe { *CASTLE_ROOK_START.get_unchecked(index) });
969			let end = BitBoard::set(my_backrank, unsafe { *CASTLE_ROOK_END.get_unchecked(index) });
970			result.xor(Piece::Rook, start, self.side_to_move);
971			result.xor(Piece::Rook, end, self.side_to_move);
972		}
973		// now, lets see if we're in check or pinned
974		let attackers = result.color_combined(result.side_to_move)
975			& ((get_bishop_rays(ksq) & (result.pieces(Piece::Bishop) | result.pieces(Piece::Queen)))
976				| (get_rook_rays(ksq) & (result.pieces(Piece::Rook) | result.pieces(Piece::Queen))));
977
978		for sq in attackers {
979			let between = between(sq, ksq) & result.combined();
980			if between == EMPTY {
981				result.checkers ^= BitBoard::from_square(sq);
982			} else if between.popcnt() == 1 {
983				result.pinned ^= between;
984			}
985		}
986
987		result.side_to_move = !result.side_to_move;
988	}
989
990	/// Update the pin information.
991	fn update_pin_info(&mut self) {
992		self.pinned = EMPTY;
993		self.checkers = EMPTY;
994
995		let ksq = (self.pieces(Piece::King) & self.color_combined(self.side_to_move)).to_square();
996
997		let pinners = self.color_combined(!self.side_to_move)
998			& ((get_bishop_rays(ksq) & (self.pieces(Piece::Bishop) | self.pieces(Piece::Queen)))
999				| (get_rook_rays(ksq) & (self.pieces(Piece::Rook) | self.pieces(Piece::Queen))));
1000
1001		for sq in pinners {
1002			let between = between(sq, ksq) & self.combined();
1003			if between == EMPTY {
1004				self.checkers ^= BitBoard::from_square(sq);
1005			} else if between.popcnt() == 1 {
1006				self.pinned ^= between;
1007			}
1008		}
1009
1010		self.checkers ^= get_knight_moves(ksq) & self.color_combined(!self.side_to_move) & self.pieces(Piece::Knight);
1011
1012		self.checkers ^= get_pawn_attacks(ksq, self.side_to_move, self.color_combined(!self.side_to_move) & self.pieces(Piece::Pawn));
1013	}
1014
1015	/// Give me the `BitBoard` of my pinned pieces.
1016	#[inline]
1017	pub fn pinned(&self) -> &BitBoard {
1018		&self.pinned
1019	}
1020
1021	/// Give me the `Bitboard` of the pieces putting me in check.
1022	#[inline]
1023	pub fn checkers(&self) -> &BitBoard {
1024		&self.checkers
1025	}
1026}
1027
1028impl fmt::Display for Board {
1029	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1030		let fen: BoardBuilder = self.into();
1031		write!(f, "{}", fen)
1032	}
1033}
1034
1035impl TryFrom<&BoardBuilder> for Board {
1036	type Error = Error;
1037
1038	fn try_from(fen: &BoardBuilder) -> Result<Self, Self::Error> {
1039		let mut board = Board::new();
1040
1041		for sq in ALL_SQUARES.iter() {
1042			if let Some((piece, color)) = fen[*sq] {
1043				board.xor(piece, BitBoard::from_square(*sq), color);
1044			}
1045		}
1046
1047		board.side_to_move = fen.get_side_to_move();
1048
1049		if let Some(ep) = fen.get_en_passant() {
1050			board.side_to_move = !board.side_to_move;
1051			board.set_ep(ep);
1052			board.side_to_move = !board.side_to_move;
1053		}
1054
1055		#[allow(deprecated)]
1056		board.add_castle_rights(Color::White, fen.get_castle_rights(Color::White));
1057		#[allow(deprecated)]
1058		board.add_castle_rights(Color::Black, fen.get_castle_rights(Color::Black));
1059
1060		board.update_pin_info();
1061
1062		if board.is_sane() { Ok(board) } else { Err(Error::InvalidBoard) }
1063	}
1064}
1065
1066impl TryFrom<&mut BoardBuilder> for Board {
1067	type Error = Error;
1068
1069	fn try_from(fen: &mut BoardBuilder) -> Result<Self, Self::Error> {
1070		(&*fen).try_into()
1071	}
1072}
1073
1074impl TryFrom<BoardBuilder> for Board {
1075	type Error = Error;
1076
1077	fn try_from(fen: BoardBuilder) -> Result<Self, Self::Error> {
1078		(&fen).try_into()
1079	}
1080}
1081
1082impl FromStr for Board {
1083	type Err = Error;
1084
1085	fn from_str(value: &str) -> Result<Self, Self::Err> {
1086		BoardBuilder::from_str(value)?.try_into()
1087	}
1088}
1089
1090#[test]
1091fn test_null_move_en_passant() {
1092	let start = Board::from_str("rnbqkbnr/pppp2pp/8/4pP2/8/8/PPPP1PPP/RNBQKBNR w KQkq e6 0 0").unwrap();
1093	let expected = Board::from_str("rnbqkbnr/pppp2pp/8/4pP2/8/8/PPPP1PPP/RNBQKBNR b KQkq - 0 0").unwrap();
1094	assert_eq!(start.null_move().unwrap(), expected);
1095}