Add make_move fn and tests

This commit is contained in:
2024-06-29 16:37:06 +03:00
parent a514431e7e
commit 941c14c199

View File

@@ -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(())
}
}