Struct cozy_chess::Board
source · pub struct Board { /* private fields */ }
Expand description
A chessboard.
This keeps about as much state as a FEN string, and does not keep track of history.
Implementations§
source§impl Board
impl Board
sourcepub fn generate_moves(&self, listener: impl FnMut(PieceMoves) -> bool) -> bool
pub fn generate_moves(&self, listener: impl FnMut(PieceMoves) -> bool) -> bool
Generate all legal moves given a position in no particular order.
To retrieve the moves, a listener
callback must be passed that receives compact PieceMoves
.
This does not guarantee that each PieceMoves
value has a unique from
square.
However, each PieceMoves
value will have at least one move.
The listener will be called a maximum of 18 times.
The listener can abort the movegen early by returning true
.
In this case, this function also returns true
.
§Examples
let board = Board::default();
let mut total_moves = 0;
board.generate_moves(|moves| {
// Done this way for demonstration.
// Actual counting is best done in bulk with moves.len().
for _mv in moves {
total_moves += 1;
}
false
});
assert_eq!(total_moves, 20);
sourcepub fn generate_moves_for(
&self,
mask: BitBoard,
listener: impl FnMut(PieceMoves) -> bool
) -> bool
pub fn generate_moves_for( &self, mask: BitBoard, listener: impl FnMut(PieceMoves) -> bool ) -> bool
Version of Board::generate_moves
moves that
generates moves for only a subset of pieces.
§Examples
let board = Board::default();
let knights = board.pieces(Piece::Knight);
let mut knight_moves = 0;
board.generate_moves_for(knights, |moves| {
// Done this way for demonstration.
// Actual counting is best done in bulk with moves.len().
for _mv in moves {
knight_moves += 1;
}
false
});
assert_eq!(knight_moves, 4);
source§impl Board
impl Board
sourcepub fn from_fen(fen: &str, shredder: bool) -> Result<Self, FenParseError>
pub fn from_fen(fen: &str, shredder: bool) -> Result<Self, FenParseError>
Parse a FEN string. If shredder
is true, it parses Shredder FEN instead.
You can also parse the board with FromStr
, which parses both FEN types.
§Examples
§FEN
const STARTPOS: &str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
let board = Board::from_fen(STARTPOS, false).unwrap();
assert_eq!(format!("{}", board), STARTPOS);
§Shredder FEN
const STARTPOS: &str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w HAha - 0 1";
let board = Board::from_fen(STARTPOS, true).unwrap();
assert_eq!(format!("{:#}", board), STARTPOS);
source§impl Board
impl Board
sourcepub fn startpos() -> Self
pub fn startpos() -> Self
Get a board with the default start position.
§Examples
let startpos = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1".parse().unwrap();
let board = Board::default();
assert_eq!(board, startpos);
sourcepub fn chess960_startpos(scharnagl_number: u32) -> Self
pub fn chess960_startpos(scharnagl_number: u32) -> Self
Get a board with a chess960 start position. Converts a Scharnagl number to its corresponding position.
§Panics
Panic if the Scharnagl number is invalid (not within the range 0..960).
§Examples
let startpos = Board::default();
// 518 is the Scharnagl number for the default start position.
let board = Board::chess960_startpos(518);
assert_eq!(board, startpos);
sourcepub fn double_chess960_startpos(
white_scharnagl_number: u32,
black_scharnagl_number: u32
) -> Self
pub fn double_chess960_startpos( white_scharnagl_number: u32, black_scharnagl_number: u32 ) -> Self
Get a board with a double chess960 start position. Uses two Scharnagl numbers for the initial setup for white and the initial setup for black.
§Panics
Panic if either Scharnagl number is invalid (not within the range 0..960).
§Examples
let startpos = Board::default();
// 518 is the Scharnagl number for the default start position.
let board = Board::double_chess960_startpos(518, 518);
assert_eq!(board, startpos);
sourcepub fn colors(&self, color: Color) -> BitBoard
pub fn colors(&self, color: Color) -> BitBoard
Get a BitBoard
of all the pieces of a certain color.
§Examples
let board = Board::default();
let white_pieces = board.colors(Color::White);
assert_eq!(white_pieces, bitboard! {
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
X X X X X X X X
X X X X X X X X
});
sourcepub fn colored_pieces(&self, color: Color, piece: Piece) -> BitBoard
pub fn colored_pieces(&self, color: Color, piece: Piece) -> BitBoard
Get a BitBoard
of all the pieces of a certain color and type.
Shorthand for board.colors(color) & board.pieces(piece)
.
§Examples
let board = Board::default();
let white_pawns = board.colored_pieces(Color::White, Piece::Pawn);
assert_eq!(white_pawns, bitboard! {
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
X X X X X X X X
. . . . . . . .
});
sourcepub fn side_to_move(&self) -> Color
pub fn side_to_move(&self) -> Color
Get the current side to move.
§Examples
let mut board = Board::default();
assert_eq!(board.side_to_move(), Color::White);
board.play("e2e4".parse().unwrap());
assert_eq!(board.side_to_move(), Color::Black);
sourcepub fn castle_rights(&self, color: Color) -> &CastleRights
pub fn castle_rights(&self, color: Color) -> &CastleRights
Get the CastleRights
for some side.
§Examples
let mut board = Board::default();
let rights = board.castle_rights(Color::White);
assert_eq!(rights.short, Some(File::H));
assert_eq!(rights.long, Some(File::A));
board.play("e2e4".parse().unwrap());
board.play("e7e5".parse().unwrap());
board.play("e1e2".parse().unwrap());
let rights = board.castle_rights(Color::White);
assert_eq!(rights.short, None);
assert_eq!(rights.long, None);
sourcepub fn en_passant(&self) -> Option<File>
pub fn en_passant(&self) -> Option<File>
Get the en passant file, if it exists.
§Examples
let mut board: Board = "1k2r3/2p5/p4p2/Pb6/1p1b1P1R/1P6/2P3PP/5K2 w - - 1 36"
.parse().unwrap();
assert_eq!(board.en_passant(), None);
board.play("c2c4".parse().unwrap());
assert_eq!(board.en_passant(), Some(File::C));
board.play("b4c3".parse().unwrap());
assert_eq!(board.en_passant(), None);
sourcepub fn hash(&self) -> u64
pub fn hash(&self) -> u64
Get the incrementally updated position hash. Does not include the halfmove clock or fullmove number.
§Examples
let mut board = Board::default();
board.play("e2e4".parse().unwrap());
board.play("e7e5".parse().unwrap());
board.play("e1e2".parse().unwrap());
board.play("e8e7".parse().unwrap());
let expected: Board = "rnbq1bnr/ppppkppp/8/4p3/4P3/8/PPPPKPPP/RNBQ1BNR w - - 2 3"
.parse().unwrap();
assert_eq!(expected.hash(), board.hash());
sourcepub fn hash_without_ep(&self) -> u64
pub fn hash_without_ep(&self) -> u64
Get the incrementally updated position hash without en passant information. Does not include the halfmove clock or fullmove number. This may be used for equivalence checks if en passant is not relevant.
§Examples
let has_ep: Board = "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"
.parse().unwrap();
let no_ep: Board = "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 4 3"
.parse().unwrap();
assert_ne!(has_ep.hash(), no_ep.hash());
assert_eq!(has_ep.hash_without_ep(), no_ep.hash());
sourcepub fn pinned(&self) -> BitBoard
pub fn pinned(&self) -> BitBoard
Get the pinned pieces for the side to move. Note that this counts pieces regardless of color. This counts any piece preventing check on our king.
§Examples
let board: Board = "8/8/1q4k1/5p2/1n6/3B4/1KP3r1/8 w - - 0 1".parse().unwrap();
assert_eq!(board.pinned(), bitboard! {
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. X . . . . . .
. . . . . . . .
. . X . . . . .
. . . . . . . .
});
sourcepub fn checkers(&self) -> BitBoard
pub fn checkers(&self) -> BitBoard
Get the pieces currently giving check.
§Examples
let mut board: Board = "1r4r1/pbpknp1p/1b3P2/8/8/B1PB1q2/P4PPP/3R2K1 w - - 0 22"
.parse().unwrap();
assert_eq!(board.checkers(), BitBoard::EMPTY);
board.play("d3f5".parse().unwrap());
assert_eq!(board.checkers(), bitboard! {
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . X . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . X . . . .
});
sourcepub fn halfmove_clock(&self) -> u8
pub fn halfmove_clock(&self) -> u8
Get the halfmove clock.
§Examples
let mut board = Board::default();
assert_eq!(board.halfmove_clock(), 0);
board.play("e2e4".parse().unwrap());
board.play("e7e5".parse().unwrap());
// Remains at zero for pawn moves
assert_eq!(board.halfmove_clock(), 0);
board.play("e1e2".parse().unwrap());
// Non-pawn move
assert_eq!(board.halfmove_clock(), 1);
sourcepub fn set_halfmove_clock(&mut self, n: u8)
pub fn set_halfmove_clock(&mut self, n: u8)
Set the halfmove clock.
§Panics
This method panics if the argument is larger than 100.
§Examples
let mut board: Board = "rnbqkb1r/pppppppp/5n2/8/8/5N2/PPPPPPPP/RNBQKB1R w KQkq - 2 2"
.parse().unwrap();
assert_eq!(board.halfmove_clock(), 2);
board.set_halfmove_clock(0);
assert_eq!(board.halfmove_clock(), 0);
sourcepub fn fullmove_number(&self) -> u16
pub fn fullmove_number(&self) -> u16
Get the fullmove number.
§Examples
let mut board = Board::default();
// The fullmove number starts at one.
assert_eq!(board.fullmove_number(), 1);
board.play("e2e4".parse().unwrap());
board.play("e7e5".parse().unwrap());
board.play("e1e2".parse().unwrap());
// 3 plies is 1.5 moves, which rounds down
assert_eq!(board.fullmove_number(), 2);
sourcepub fn set_fullmove_number(&mut self, n: u16)
pub fn set_fullmove_number(&mut self, n: u16)
Set the fullmove number.
§Panics
This method panics if the argument is zero.
§Examples
let mut board = Board::default();
// The fullmove number starts at one.
assert_eq!(board.fullmove_number(), 1);
board.set_fullmove_number(2);
assert_eq!(board.fullmove_number(), 2);
sourcepub fn king(&self, color: Color) -> Square
pub fn king(&self, color: Color) -> Square
Get the king square of some side.
§Examples
let board = Board::default();
assert_eq!(board.king(Color::White), Square::E1);
sourcepub fn status(&self) -> GameStatus
pub fn status(&self) -> GameStatus
Get the status of the game. Note that this game may still be drawn from threefold repetition. The game may also be drawn from insufficient material cases such as bare kings; This method does not detect such cases. If the game is won, the loser is the current side to move.
§Examples
§Checkmate
let mut board = Board::default();
const MOVES: &[&str] = &[
"e2e4", "e7e5", "g1f3", "b8c6", "d2d4", "e5d4",
"f3d4", "f8c5", "c2c3", "d8f6", "d4c6", "f6f2"
];
for mv in MOVES {
assert_eq!(board.status(), GameStatus::Ongoing);
board.play(mv.parse().unwrap());
}
assert_eq!(board.status(), GameStatus::Won);
let winner = !board.side_to_move();
assert_eq!(winner, Color::Black);
§Stalemate
let mut board = Board::default();
const MOVES: &[&str] = &[
"c2c4", "h7h5", "h2h4", "a7a5", "d1a4",
"a8a6", "a4a5", "a6h6", "a5c7", "f7f6",
"c7d7", "e8f7", "d7b7", "d8d3", "b7b8",
"d3h7", "b8c8", "f7g6", "c8e6"
];
for mv in MOVES {
assert_eq!(board.status(), GameStatus::Ongoing);
board.play(mv.parse().unwrap());
}
assert_eq!(board.status(), GameStatus::Drawn);
§50 move rule
let mut board = Board::default();
board.play("e2e4".parse().unwrap());
board.play("e7e5".parse().unwrap());
const MOVES: &[&str] = &["e1e2", "e8e7", "e2e1", "e7e8"];
for mv in MOVES.iter().cycle().take(50 * 2) {
assert_eq!(board.status(), GameStatus::Ongoing);
board.play(mv.parse().unwrap());
}
assert_eq!(board.status(), GameStatus::Drawn);
sourcepub fn same_position(&self, other: &Self) -> bool
pub fn same_position(&self, other: &Self) -> bool
Check if two positions are equivalent based on the FIDE definition.
This differs from the Eq
implementation in that:
- It does not check the halfmove clock or fullmove number
- It ignores the state of the en passant square if it does not apply (capture would not be legal) This method can be used as a strict check for threefold repetition.
§Examples
let board_a = "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"
.parse::<Board>().unwrap();
let board_b = "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 4 3"
.parse::<Board>().unwrap();
assert!(board_a != board_b); // Differing EP and halfmove clock
assert!(board_a.same_position(&board_b)); // Identical by FIDE rules
let board_c = "rnbqkb1r/ppp1pppp/5n2/3pP3/8/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 3"
.parse::<Board>().unwrap();
let board_d = "rnbqkb1r/ppp1pppp/5n2/3pP3/8/8/PPPP1PPP/RNBQKBNR w KQkq - 4 5"
.parse::<Board>().unwrap();
assert!(!board_c.same_position(&board_d)); // En passant is legal here
sourcepub fn null_move(&self) -> Option<Board>
pub fn null_move(&self) -> Option<Board>
Attempt to play a null move, returning a new board if successful.
§Examples
let mut board = Board::default();
board.play("f2f4".parse().unwrap());
board.play("e7e5".parse().unwrap());
assert_eq!(board.side_to_move(), Color::White);
board = board.null_move().unwrap();
assert_eq!(board.side_to_move(), Color::Black);
board.play("d8h4".parse().unwrap());
// Can't leave the king in check
assert!(board.null_move().is_none());
sourcepub fn play(&mut self, mv: Move)
pub fn play(&mut self, mv: Move)
Play a move while checking its legality. Note that this only supports Chess960 style castling.
This method does not account for the 50 move rule, and checks only whether the move would be legal.
The halfmove clock is capped at 100 and the fullmove number is capped at u16::MAX
.
§Panics
This is guaranteed to panic if the move is illegal.
See Board::try_play
for a non-panicking variant.
See Board::play_unchecked
for a faster variant
that’s not guaranteed to panic on illegal moves.
§Examples
§Legal moves
let mut board = Board::default();
board.play("e2e4".parse().unwrap());
board.play("e7e5".parse().unwrap());
board.play("e1e2".parse().unwrap());
board.play("e8e7".parse().unwrap());
const EXPECTED: &str = "rnbq1bnr/ppppkppp/8/4p3/4P3/8/PPPPKPPP/RNBQ1BNR w - - 2 3";
assert_eq!(format!("{}", board), EXPECTED);
§Illegal moves
let mut board = Board::default();
board.play("e1e8".parse().unwrap());
sourcepub fn try_play(&mut self, mv: Move) -> Result<(), IllegalMoveError>
pub fn try_play(&mut self, mv: Move) -> Result<(), IllegalMoveError>
Non-panicking version of Board::play
.
Tries to play a move, returning Ok(())
on success.
§Errors
Errors with IllegalMoveError
if the move was illegal.
sourcepub fn play_unchecked(&mut self, mv: Move)
pub fn play_unchecked(&mut self, mv: Move)
Unchecked version of Board::play
.
Use this method with caution; Only legal moves should ever be passed to this method.
Playing illegal moves may corrupt the board state, causing panics.
However, it will not cause undefined behaviour.
§Panics
This may panic if the move is illegal.
Additionally, playing illegal moves may corrupt the board state, which may cause further panics.
See Board::play
for a variant guaranteed to panic on illegal moves.
§Examples
let mut board = Board::default();
board.play_unchecked("e2e4".parse().unwrap());
board.play_unchecked("e7e5".parse().unwrap());
board.play_unchecked("e1e2".parse().unwrap());
board.play_unchecked("e8e7".parse().unwrap());
const EXPECTED: &str = "rnbq1bnr/ppppkppp/8/4p3/4P3/8/PPPPKPPP/RNBQ1BNR w - - 2 3";
assert_eq!(format!("{}", board), EXPECTED);
Trait Implementations§
source§impl Display for Board
impl Display for Board
source§fn fmt(&self, f: &mut Formatter<'_>) -> Result
fn fmt(&self, f: &mut Formatter<'_>) -> Result
Display the board. You can use the alternate format mode for Shredder FEN.
§Examples
§FEN
const STARTPOS: &str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
let board = Board::default();
assert_eq!(format!("{}", board), STARTPOS);
§Shredder FEN
const STARTPOS: &str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w HAha - 0 1";
let board = Board::default();
assert_eq!(format!("{:#}", board), STARTPOS);
source§impl FromStr for Board
impl FromStr for Board
source§fn from_str(fen: &str) -> Result<Self, Self::Err>
fn from_str(fen: &str) -> Result<Self, Self::Err>
Parse the board.
This method will parse both regular FENs and Shredder FENs.
See also: Board::from_fen
.
§Examples
const STARTPOS: &str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
let board: Board = STARTPOS.parse().unwrap();
assert_eq!(format!("{}", board), STARTPOS);