Add make_move fn and tests
This commit is contained in:
256
src/board.rs
256
src/board.rs
@@ -11,7 +11,7 @@ use crate::movegen::{
|
||||
};
|
||||
use crate::r#move::{Move, MoveType, Promote};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct Board {
|
||||
pub white_pieces: [Piece; 6],
|
||||
pub black_pieces: [Piece; 6],
|
||||
@@ -146,6 +146,128 @@ impl Board {
|
||||
Kind::King => king_pseudo_moves(pieces, all_occupancies, own_occupancies, self, color),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_move_and_reset(&mut self, mv: Move, color: Color) -> Vec<Board> {
|
||||
let mut board_variants = vec![];
|
||||
let original_board = self.clone();
|
||||
if self.make_move(mv, color) {
|
||||
board_variants.push(self.clone());
|
||||
}
|
||||
*self = original_board;
|
||||
board_variants
|
||||
}
|
||||
|
||||
pub fn make_move(&mut self, mv: Move, color: Color) -> bool {
|
||||
self.update_board_state(&mv, color);
|
||||
self.state.update_castling_state(mv.source as u8, color);
|
||||
self.state.next_turn();
|
||||
|
||||
let pieces = match color {
|
||||
Color::White => &self.white_pieces,
|
||||
Color::Black => &self.black_pieces,
|
||||
};
|
||||
|
||||
let own_king_square = pieces[Kind::King.idx()].bitboard.trailing_zeros() as usize;
|
||||
self.is_move_legit(own_king_square, Color::opponent_color(color))
|
||||
}
|
||||
|
||||
pub fn update_board_state(&mut self, mv: &Move, color: Color) {
|
||||
let (own_pieces, opponent_pieces) = match color {
|
||||
Color::White => (&mut self.white_pieces, &mut self.black_pieces),
|
||||
Color::Black => (&mut self.black_pieces, &mut self.white_pieces),
|
||||
};
|
||||
|
||||
match &mv.move_type {
|
||||
MoveType::Quiet => {
|
||||
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces);
|
||||
}
|
||||
MoveType::Capture => {
|
||||
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces);
|
||||
Board::remove_piece(mv.target as u8, opponent_pieces);
|
||||
}
|
||||
MoveType::EnPassant => {
|
||||
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces);
|
||||
Board::remove_pawn_enpassant(mv.target as u8, opponent_pieces, color);
|
||||
self.state.set_en_passant_target_square(None);
|
||||
}
|
||||
MoveType::DoublePush => {
|
||||
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces);
|
||||
let en_passant = Some(mv.source as u8 + 8);
|
||||
self.state.set_en_passant_target_square(en_passant);
|
||||
}
|
||||
MoveType::Promotion(promote) => {
|
||||
Board::remove_piece(mv.source as u8, own_pieces);
|
||||
Board::promote_piece(mv.target as u8, own_pieces, *promote);
|
||||
}
|
||||
MoveType::PromotionCapture(promote) => {
|
||||
Board::remove_piece(mv.source as u8, own_pieces);
|
||||
Board::remove_piece(mv.target as u8, opponent_pieces);
|
||||
Board::promote_piece(mv.target as u8, own_pieces, *promote);
|
||||
}
|
||||
MoveType::Castle => {
|
||||
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces);
|
||||
Board::move_rook_castle(mv.target as u8, own_pieces);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn move_piece(source: u8, target: u8, pieces: &mut [Piece; 6]) {
|
||||
for p in pieces.iter_mut() {
|
||||
if p.bitboard & (1_u64 << source) != 0 {
|
||||
p.bitboard &= !(1_u64 << source);
|
||||
p.bitboard |= 1_u64 << target;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn move_rook_castle(square: u8, pieces: &mut [Piece; 6]) {
|
||||
let (rook_source, rook_target) = match square {
|
||||
2 | 58 => (square - 2, square + 1),
|
||||
6 | 62 => (square + 1, square - 1),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
for p in pieces.iter_mut() {
|
||||
if p.bitboard & 1_u64 << rook_source != 0 {
|
||||
p.bitboard &= !(1_u64 << rook_source);
|
||||
p.bitboard |= 1_u64 << rook_target;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn promote_piece(square: u8, pieces: &mut [Piece; 6], promote: Promote) {
|
||||
match promote {
|
||||
Promote::Knight => pieces[Kind::Knight.idx()].bitboard |= 1_u64 << square,
|
||||
Promote::Bishop => pieces[Kind::Bishop.idx()].bitboard |= 1_u64 << square,
|
||||
Promote::Rook => pieces[Kind::Rook.idx()].bitboard |= 1_u64 << square,
|
||||
Promote::Queen => pieces[Kind::Queen.idx()].bitboard |= 1_u64 << square,
|
||||
};
|
||||
}
|
||||
|
||||
fn remove_piece(square: u8, pieces: &mut [Piece; 6]) {
|
||||
for p in pieces.iter_mut() {
|
||||
if p.bitboard & (1_u64 << square) != 0 {
|
||||
p.bitboard &= !(1_u64 << square);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_pawn_enpassant(square: u8, pieces: &mut [Piece; 6], color: Color) {
|
||||
let piece_to_remove = match color {
|
||||
Color::White => square - 8,
|
||||
Color::Black => square + 8,
|
||||
};
|
||||
|
||||
for p in pieces.iter_mut() {
|
||||
if p.bitboard & (1_u64 << piece_to_remove) != 0 {
|
||||
p.bitboard &= !(1_u64 << piece_to_remove);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Board {
|
||||
@@ -187,12 +309,21 @@ impl Kind {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Color {
|
||||
White,
|
||||
Black,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub const fn opponent_color(color: Color) -> Color {
|
||||
match color {
|
||||
Color::White => Color::Black,
|
||||
Color::Black => Color::White,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{attack::init_attacks, fen::from_fen};
|
||||
@@ -202,10 +333,11 @@ mod tests {
|
||||
const FEN_EXAMPLE: [&str; 1] = ["8/6P1/4n2b/1p6/1Kp5/6n1/4b3/1k6 w - - 0 1"];
|
||||
|
||||
#[test]
|
||||
fn test_get_all_occupancies() -> Result<(), String> {
|
||||
fn test_get_occupancies() -> Result<(), String> {
|
||||
let new_game = from_fen(FEN_EXAMPLE[0])?;
|
||||
assert_eq!(new_game.board.get_white_occupancies(), 0x40000002000000);
|
||||
assert_eq!(new_game.board.get_black_occupancies(), 0x900204401002);
|
||||
assert_eq!(new_game.board.get_all_occupancies(), 0x40900206401002);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -217,4 +349,120 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fifty_move_draw() -> Result<(), String> {
|
||||
//TODO: make a MoveHistory/BoardHistory/GameHistory struct
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_threefold_repetition() -> Result<(), String> {
|
||||
//TODO: make a MoveHistory/BoardHistory/GameHistory struct
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const FEN_QUIET: [&str; 2] = [
|
||||
"r3k2r/2p1p1qp/2npb3/1p3p2/p3P1pP/P1PB1Q2/1P1PNPP1/R3K2R w KQkq - 0 1",
|
||||
"r3k2r/2p1p1qp/2npb3/1p3p2/pP2P1pP/P1PB1Q2/3PNPP1/R3K2R b KQkq b3 0 1",
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_make_move_quiet() -> Result<(), String> {
|
||||
let mut game = from_fen(FEN_QUIET[0])?;
|
||||
let mv = Move::new_with_type(9, 25, MoveType::DoublePush);
|
||||
game.board.make_move(mv, Color::White);
|
||||
assert_eq!(game, from_fen(FEN_QUIET[1])?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const FEN_CAPTURE: [&str; 2] = [
|
||||
"r3k2r/2p1p1qp/2npb3/1p3p2/p3P1pP/P1PB1Q2/1P1PNPP1/R3K2R w KQkq - 0 1",
|
||||
"r3k2r/2p1p1qp/2npb3/1p3Q2/p3P1pP/P1PB4/1P1PNPP1/R3K2R b KQkq - 0 1",
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_make_move_capture() -> Result<(), String> {
|
||||
let mut game = from_fen(FEN_CAPTURE[0])?;
|
||||
let mv = Move::new_with_type(21, 37, MoveType::Capture);
|
||||
game.board.make_move(mv, Color::White);
|
||||
assert_eq!(game, from_fen(FEN_CAPTURE[1])?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const FEN_EN_PASSANT: [&str; 2] = [
|
||||
"r3k2r/2p1p1qp/2npb3/1p3p2/p3P1pP/P1PB1Q2/1P1PNPP1/R3K2R b KQkq h3 0 1",
|
||||
"r3k2r/2p1p1qp/2npb3/1p3p2/p3P3/P1PB1Q1p/1P1PNPP1/R3K2R w KQkq - 0 1",
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_make_move_en_passant() -> Result<(), String> {
|
||||
let mut game = from_fen(FEN_EN_PASSANT[0])?;
|
||||
let mv = Move::new_with_type(30, 23, MoveType::EnPassant);
|
||||
game.board.make_move(mv, Color::Black);
|
||||
assert_eq!(game, from_fen(FEN_EN_PASSANT[1])?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const FEN_DOUBLE_PUSH: [&str; 2] = [
|
||||
"rnbqkbnr/p1pppppp/8/8/1p5P/8/PPPPPPP1/RNBQKBNR w KQkq - 0 1",
|
||||
"rnbqkbnr/p1pppppp/8/8/1pP4P/8/PP1PPPP1/RNBQKBNR b KQkq c3 0 1",
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_make_move_double_push() -> Result<(), String> {
|
||||
let mut game = from_fen(FEN_DOUBLE_PUSH[0])?;
|
||||
let mv = Move::new_with_type(10, 26, MoveType::DoublePush);
|
||||
game.board.make_move(mv, Color::White);
|
||||
assert_eq!(game, from_fen(FEN_DOUBLE_PUSH[1])?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const FEN_PROMOTION: [&str; 2] = [
|
||||
"8/6P1/4n2b/1p6/1Kp5/6n1/4b3/1k6 w - - 0 1",
|
||||
"6Q1/8/4n2b/1p6/1Kp5/6n1/4b3/1k6 b - - 0 1",
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_make_move_promotion() -> Result<(), String> {
|
||||
let mut game = from_fen(FEN_PROMOTION[0])?;
|
||||
let mv = Move::new_with_type(54, 62, MoveType::Promotion(Promote::Queen));
|
||||
game.board.make_move(mv, Color::White);
|
||||
assert_eq!(game, from_fen(FEN_PROMOTION[1])?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const FEN_PROMOTION_CAPTURE: [&str; 2] = [
|
||||
"5n2/6P1/7b/1p6/1Kp5/6n1/4b3/1k6 w - - 0 1",
|
||||
"5Q2/8/7b/1p6/1Kp5/6n1/4b3/1k6 b - - 0 1",
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_make_move_promotion_capture() -> Result<(), String> {
|
||||
let mut game = from_fen(FEN_PROMOTION_CAPTURE[0])?;
|
||||
let mv = Move::new_with_type(54, 61, MoveType::PromotionCapture(Promote::Queen));
|
||||
game.board.make_move(mv, Color::White);
|
||||
assert_eq!(game, from_fen(FEN_PROMOTION_CAPTURE[1])?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const FEN_CASTLE: [&str; 2] = [
|
||||
"r3k2r/2p1p1qp/2npb3/1p3p2/p3P1pP/P1PB1Q2/1P1PNPP1/R3K2R w KQkq - 0 1",
|
||||
"r3k2r/2p1p1qp/2npb3/1p3p2/p3P1pP/P1PB1Q2/1P1PNPP1/R4RK1 b kq - 0 1",
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_make_move_castle() -> Result<(), String> {
|
||||
let mut game = from_fen(FEN_CASTLE[0])?;
|
||||
let mv = Move::new_with_type(4, 6, MoveType::Castle);
|
||||
game.board.make_move(mv, Color::White);
|
||||
assert_eq!(game, from_fen(FEN_CASTLE[1])?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user