feat: color flip board
This commit is contained in:
parent
4a2f3b5830
commit
26a88adefc
82
src/lib.rs
82
src/lib.rs
@ -53,7 +53,7 @@ const N_PIECES: usize = 6;
|
|||||||
struct PieceErr;
|
struct PieceErr;
|
||||||
|
|
||||||
/// Color and piece.
|
/// Color and piece.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
struct ColPiece {
|
struct ColPiece {
|
||||||
pc: Piece,
|
pc: Piece,
|
||||||
col: Color,
|
col: Color,
|
||||||
@ -192,6 +192,14 @@ impl Square {
|
|||||||
let (r, c) = self.to_row_col();
|
let (r, c) = self.to_row_col();
|
||||||
(r.try_into().unwrap(), c.try_into().unwrap())
|
(r.try_into().unwrap(), c.try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Vertically mirror a square.
|
||||||
|
fn mirror_vert(&self) -> Self {
|
||||||
|
let (r, c) = self.to_row_col();
|
||||||
|
let (nr, nc) = (BOARD_HEIGHT - 1 - r, c);
|
||||||
|
Square::from_row_col(nr, nc)
|
||||||
|
.unwrap_or_else(|e| panic!("mirrored square should be valid: nr {nr} nc {nc}: {e:?}"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Square {
|
impl Display for Square {
|
||||||
@ -259,7 +267,7 @@ impl From<Piece> for char {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Copy)]
|
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
struct Bitboard(u64);
|
struct Bitboard(u64);
|
||||||
|
|
||||||
impl Bitboard {
|
impl Bitboard {
|
||||||
@ -310,7 +318,7 @@ impl Iterator for BitboardIterator {
|
|||||||
/// Array form board.
|
/// Array form board.
|
||||||
///
|
///
|
||||||
/// Complements bitboards, notably for "what piece is at this square?" queries.
|
/// Complements bitboards, notably for "what piece is at this square?" queries.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
struct Mailbox([Option<ColPiece>; N_SQUARES]);
|
struct Mailbox([Option<ColPiece>; N_SQUARES]);
|
||||||
|
|
||||||
impl Default for Mailbox {
|
impl Default for Mailbox {
|
||||||
@ -334,7 +342,7 @@ impl Mailbox {
|
|||||||
/// Piece bitboards and state for one player.
|
/// Piece bitboards and state for one player.
|
||||||
///
|
///
|
||||||
/// Default is all empty.
|
/// Default is all empty.
|
||||||
#[derive(Default, Debug, Clone, Copy)]
|
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
struct Player {
|
struct Player {
|
||||||
/// Bitboards for individual pieces. Piece -> locations.
|
/// Bitboards for individual pieces. Piece -> locations.
|
||||||
bit: [Bitboard; N_PIECES],
|
bit: [Bitboard; N_PIECES],
|
||||||
@ -389,7 +397,7 @@ impl ToString for CastleRights {
|
|||||||
/// Immutable game state, unique to a position.
|
/// Immutable game state, unique to a position.
|
||||||
///
|
///
|
||||||
/// Default is empty.
|
/// Default is empty.
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct BoardState {
|
pub struct BoardState {
|
||||||
/// Player bitboards
|
/// Player bitboards
|
||||||
players: [Player; N_COLORS],
|
players: [Player; N_COLORS],
|
||||||
@ -440,6 +448,11 @@ impl BoardState {
|
|||||||
&mut self.castle.0[col as usize]
|
&mut self.castle.0[col as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get iterator over all squares.
|
||||||
|
fn squares() -> impl Iterator<Item = Square> {
|
||||||
|
(0..N_SQUARES).map(Square::try_from).map(|x| x.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new piece in a location.
|
/// Create a new piece in a location.
|
||||||
fn set_piece(&mut self, idx: Square, pc: ColPiece) {
|
fn set_piece(&mut self, idx: Square, pc: ColPiece) {
|
||||||
let pl = self.pl_mut(pc.col);
|
let pl = self.pl_mut(pc.col);
|
||||||
@ -447,6 +460,16 @@ impl BoardState {
|
|||||||
*self.mail.sq_mut(idx) = Some(pc);
|
*self.mail.sq_mut(idx) = Some(pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the piece (or no piece) in a square.
|
||||||
|
fn set_square(&mut self, idx: Square, pc: Option<ColPiece>) {
|
||||||
|
match pc {
|
||||||
|
Some(pc) => self.set_piece(idx, pc),
|
||||||
|
None => {
|
||||||
|
let _ = self.del_piece(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Delete the piece in a location, and return ("pop") that piece.
|
/// Delete the piece in a location, and return ("pop") that piece.
|
||||||
///
|
///
|
||||||
/// Returns an error if there is no piece in the location.
|
/// Returns an error if there is no piece in the location.
|
||||||
@ -471,6 +494,32 @@ impl BoardState {
|
|||||||
*self.mail.sq(idx)
|
*self.mail.sq(idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mirrors the position so that black and white are switched.
|
||||||
|
///
|
||||||
|
/// Mainly to avoid duplication in tests.
|
||||||
|
fn flip_colors(&self) -> Self {
|
||||||
|
let mut new_board = Self {
|
||||||
|
turn: self.turn.flip(),
|
||||||
|
half_moves: self.half_moves,
|
||||||
|
full_moves: self.full_moves,
|
||||||
|
players: Default::default(),
|
||||||
|
mail: Default::default(),
|
||||||
|
ep_square: self.ep_square.map(|sq| sq.mirror_vert()),
|
||||||
|
castle: CastleRights (self.castle.0),
|
||||||
|
};
|
||||||
|
|
||||||
|
new_board.castle.0.reverse();
|
||||||
|
|
||||||
|
for sq in BoardState::squares() {
|
||||||
|
let opt_pc = self.get_piece(sq.mirror_vert()).map(|pc| ColPiece {
|
||||||
|
col: pc.col.flip(),
|
||||||
|
pc: pc.pc,
|
||||||
|
});
|
||||||
|
new_board.set_square(sq, opt_pc)
|
||||||
|
}
|
||||||
|
new_board
|
||||||
|
}
|
||||||
|
|
||||||
/// Maximum amount of moves in the counter to parse before giving up
|
/// Maximum amount of moves in the counter to parse before giving up
|
||||||
const MAX_MOVES: usize = 9_999;
|
const MAX_MOVES: usize = 9_999;
|
||||||
}
|
}
|
||||||
@ -570,4 +619,27 @@ mod tests {
|
|||||||
.collect::<Vec<Square>>();
|
.collect::<Vec<Square>>();
|
||||||
assert_eq!(white_queens, vec![Square::from_str("d6").unwrap()])
|
assert_eq!(white_queens, vec![Square::from_str("d6").unwrap()])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_square_mirror() {
|
||||||
|
for (sq, expect) in [("a1", "a8"), ("h1", "h8"), ("d4", "d5")] {
|
||||||
|
let sq = sq.parse::<Square>().unwrap();
|
||||||
|
let expect = expect.parse::<Square>().unwrap();
|
||||||
|
assert_eq!(sq.mirror_vert(), expect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_flip_colors() {
|
||||||
|
let test_cases = [
|
||||||
|
("2kqrbnp/8/8/8/8/8/8/2KQRBNP w - - 0 1", "2kqrbnp/8/8/8/8/8/8/2KQRBNP b - - 0 1"),
|
||||||
|
("2kqrbnp/8/8/8/8/8/6N1/2KQRB1P w - a1 0 1", "2kqrb1p/6n1/8/8/8/8/8/2KQRBNP b - a8 0 1"),
|
||||||
|
("r3k2r/8/8/8/8/8/8/R3K2R w Kq - 0 1", "r3k2r/8/8/8/8/8/8/R3K2R b Qk - 0 1"),
|
||||||
|
];
|
||||||
|
for (tc, expect) in test_cases {
|
||||||
|
let tc = BoardState::from_fen(tc).unwrap();
|
||||||
|
let expect = BoardState::from_fen(expect).unwrap();
|
||||||
|
assert_eq!(tc.flip_colors(), expect);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user