From 6ee1ff886a90ba15ca23e21772d091c0f56b08c3 Mon Sep 17 00:00:00 2001 From: KrinjMaster <5opkasuka@gmail.com> Date: Fri, 22 Mar 2024 00:16:20 +0300 Subject: [PATCH] feat: changed move encoding method --- src/board.rs | 446 ++++++++++++++++++++++++++++----------------------- src/test.rs | 31 +++- src/utils.rs | 6 +- 3 files changed, 276 insertions(+), 207 deletions(-) diff --git a/src/board.rs b/src/board.rs index 766a551..79a4fc3 100644 --- a/src/board.rs +++ b/src/board.rs @@ -1,11 +1,10 @@ use crate::{ - constants::BOARD_SQUARES, + constants::{BOARD_SQUARES, FIFTH_RANK, FOURTH_RANK}, move_generation::{ generate_bishop_moves, generate_king_moves, generate_knight_moves, generate_pawn_moves, generate_queen_moves, generate_rook_moves, }, piece_parsing::parse_bitboards, - utils::encode_move, }; pub type Move = u16; @@ -42,8 +41,54 @@ pub struct BoardState { } impl BoardState { - pub fn is_in_check(self) -> bool { - let opposite_color = match self.to_move { + pub fn encode_move(&self, from_bb: u8, to_bb: u8, capture: Piece) -> u16 { + from_bb as u16 | ((to_bb as u16) << 6) | ((capture as u16) << 12) + } + pub fn decode_move(&self, piece_move: Move) -> (Bitboard, Bitboard, Piece, Color, Piece, bool) { + let start_bb: Bitboard = + BOARD_SQUARES[((piece_move & !(1 << 7 | 1 << 8 | 1 << 6)) as u8) as usize]; + let end_bb: Bitboard = + BOARD_SQUARES[((piece_move >> 6 & !(1 << 7 | 1 << 8)) as u8) as usize]; + let captured_piece_index: u8 = ((piece_move) >> 12 & (1 | 1 << 1 | 1 << 2)) as u8; + let captured_piece = match captured_piece_index { + 0 => Piece::Pawn, + 1 => Piece::Knight, + 2 => Piece::Bishop, + 3 => Piece::Rook, + 4 => Piece::Queen, + 5 => Piece::King, + _ => Piece::None, + }; + let is_promotion: bool = (piece_move >> 15 & 1) as u8 == 1; + let mut piece: Piece = Piece::None; + let mut color: Color = Color::White; + + for color_index in 0..1 { + for piece_index in 0..5 { + if self.bb_pieces[color_index as usize][piece_index as usize] & start_bb != 0 { + piece = match piece_index { + 0 => Piece::Pawn, + 1 => Piece::Knight, + 2 => Piece::Bishop, + 3 => Piece::Rook, + 4 => Piece::Queen, + 5 => Piece::King, + _ => Piece::None, + }; + + if color_index == 0 { + color = Color::White; + } else { + color = Color::Black; + } + } + } + } + + (start_bb, end_bb, piece, color, captured_piece, is_promotion) + } + pub fn is_in_check(self, color: Color) -> bool { + let opposite_color = match color { Color::White => Color::Black, Color::Black => Color::White, }; @@ -55,198 +100,199 @@ impl BoardState { acc | BOARD_SQUARES[((cur >> 6 & !(1 << 8 | 1 << 7)) as u8) as usize] }); - all_attacks & self.get_piece_bb(self.to_move, Piece::King) != 0 + all_attacks & self.get_piece_bb(color, Piece::King) != 0 + } + + pub fn make_move(&mut self, encoded_move: Move) { + let (start_pos, end_pos, piece, color, _, _) = self.decode_move(encoded_move); + + // delete piece on the move square if there is one + // for index in 0..6 { + // // opposite color + // match color { + // Color::White => { + // if self.bb_pieces[Color::Black as usize][index] & end_pos != 0 { + // self.bb_pieces[Color::Black as usize][index] &= !end_pos; + // match index { + // 0 => captured_piece = Piece::Pawn, + // 1 => captured_piece = Piece::Knight, + // 2 => captured_piece = Piece::Bishop, + // 3 => captured_piece = Piece::Rook, + // 4 => captured_piece = Piece::Queen, + // 5 => captured_piece = Piece::King, + // _ => (), + // }; + // } + // } + // Color::Black => { + // if self.bb_pieces[Color::White as usize][index] & end_pos != 0 { + // self.bb_pieces[Color::White as usize][index] &= !end_pos; + // match index { + // 0 => captured_piece = Piece::Pawn, + // 1 => captured_piece = Piece::Knight, + // 2 => captured_piece = Piece::Bishop, + // 3 => captured_piece = Piece::Rook, + // 4 => captured_piece = Piece::Queen, + // 5 => captured_piece = Piece::King, + // _ => (), + // }; + // } + // } + // } + // } + + // delete piece from color bitboards + match color { + Color::White => { + self.bb_colors[Color::White as usize] &= !start_pos; + self.bb_colors[Color::White as usize] |= end_pos; + + self.bb_colors[Color::Black as usize] &= !start_pos; + self.bb_colors[Color::Black as usize] &= !end_pos; + } + Color::Black => { + self.bb_colors[Color::Black as usize] &= !start_pos; + self.bb_colors[Color::Black as usize] |= end_pos; + + self.bb_colors[Color::White as usize] &= !start_pos; + self.bb_colors[Color::White as usize] &= !end_pos; + } + } + + // new fullboard + self.bb_fullboard = + self.bb_colors[Color::White as usize] | self.bb_colors[Color::Black as usize]; + + // check for castling and en passant + match piece { + Piece::King => { + self.bb_castling_rigths[color as usize][1] = 0; + self.bb_castling_rigths[color as usize][0] = 0; + } + Piece::Rook => { + self.bb_castling_rigths[color as usize][1] &= !start_pos; + self.bb_castling_rigths[color as usize][0] &= !start_pos; + } + Piece::Pawn => { + if start_pos >> 16 == end_pos && matches!(color, Color::White) { + self.bb_en_passant |= start_pos >> 8; + } + if start_pos << 16 == end_pos && matches!(color, Color::Black) { + self.bb_en_passant |= start_pos << 8; + } + } + _ => (), + } + + // make a move + self.bb_pieces[color as usize][piece as usize] |= end_pos; + self.bb_colors[color as usize] |= end_pos; + self.bb_fullboard |= end_pos; + + self.move_history.push(encoded_move); + + // if black to move + if self.halfmove == 1 { + self.to_move = Color::White; + self.halfmove = 0; + self.fullmove += 1; + } else { + self.to_move = Color::Black; + self.halfmove = 1; + } } - // pub fn make_move(&mut self, color: &Color, piece: &Piece, piece_move: (Bitboard, Bitboard)) { - // self.bb_pieces[*color as usize][*piece as usize] ^= piece_move.0; - // let mut captured_piece: Piece = Piece::None; - // - // // delete piece on the move square if there is one - // for index in 0..6 { - // // opposite color - // match color { - // Color::White => { - // if self.bb_pieces[Color::Black as usize][index] & piece_move.1 != 0 { - // self.bb_pieces[Color::Black as usize][index] &= !(piece_move.1); - // match index { - // 0 => captured_piece = Piece::Pawn, - // 1 => captured_piece = Piece::Knight, - // 2 => captured_piece = Piece::Bishop, - // 3 => captured_piece = Piece::Rook, - // 4 => captured_piece = Piece::Queen, - // 5 => captured_piece = Piece::King, - // _ => (), - // }; - // } - // } - // Color::Black => { - // if self.bb_pieces[Color::White as usize][index] & piece_move.1 != 0 { - // self.bb_pieces[Color::White as usize][index] &= !(piece_move.1); - // match index { - // 0 => captured_piece = Piece::Pawn, - // 1 => captured_piece = Piece::Knight, - // 2 => captured_piece = Piece::Bishop, - // 3 => captured_piece = Piece::Rook, - // 4 => captured_piece = Piece::Queen, - // 5 => captured_piece = Piece::King, - // _ => (), - // }; - // } - // } - // } - // } - // - // // delete piece from color bitboards - // match color { - // Color::White => { - // self.bb_colors[Color::White as usize] &= !(piece_move.0); - // self.bb_colors[Color::White as usize] |= piece_move.1; - // - // self.bb_colors[Color::Black as usize] &= !(piece_move.0); - // self.bb_colors[Color::Black as usize] &= !(piece_move.1); - // } - // Color::Black => { - // self.bb_colors[Color::Black as usize] &= !(piece_move.0); - // self.bb_colors[Color::Black as usize] |= piece_move.1; - // - // self.bb_colors[Color::White as usize] &= !(piece_move.0); - // self.bb_colors[Color::White as usize] &= !(piece_move.1); - // } - // } - // - // // new fullboard - // self.bb_fullboard = - // self.bb_colors[Color::White as usize] | self.bb_colors[Color::Black as usize]; - // - // // check for castling and en passant - // match piece { - // Piece::King => { - // self.bb_castling_rigths[*color as usize][1] = 0; - // self.bb_castling_rigths[*color as usize][0] = 0; - // } - // Piece::Rook => { - // self.bb_castling_rigths[*color as usize][1] &= !(piece_move.0); - // self.bb_castling_rigths[*color as usize][0] &= !(piece_move.0); - // } - // Piece::Pawn => { - // if piece_move.0 >> 16 == piece_move.1 && matches!(color, Color::White) { - // self.bb_en_passant |= piece_move.0 >> 8; - // } - // if piece_move.0 << 16 == piece_move.1 && matches!(color, Color::Black) { - // self.bb_en_passant |= piece_move.0 << 8; - // } - // } - // _ => (), - // } - // - // // make a move - // self.bb_pieces[*color as usize][*piece as usize] |= piece_move.1; - // self.bb_colors[*color as usize] |= piece_move.1; - // self.bb_fullboard |= piece_move.1; - // - // // self.move_history - // // .push((piece_move.0, piece_move.1, *color, *piece, captured_piece)); - // - // // if black to move - // if self.halfmove == 1 { - // self.to_move = Color::White; - // self.halfmove = 0; - // self.fullmove += 1; - // } else { - // self.to_move = Color::Black; - // self.halfmove = 1; - // } - // } - - // pub fn undo_move(&mut self) -> Result<(), &str> { - // let (prev_pos, cur_pos, color, piece, captured_piece) = - // self.move_history.pop().expect("No items found!"); - // - // let opposite_color = match color { - // Color::White => Color::Black, - // Color::Black => Color::White, - // }; - // - // // check for castling and castling avaliability - // match piece { - // Piece::Pawn => { - // match color { - // Color::White => { - // // if piece was previosly of fourth rank - // if prev_pos & FOURTH_RANK != 0 { - // self.bb_en_passant |= prev_pos << 8; - // } - // } - // Color::Black => { - // // if piece was previosly of fifth rank - // if prev_pos & FIFTH_RANK != 0 { - // self.bb_en_passant |= prev_pos >> 8; - // } - // } - // } - // } - // Piece::Rook => { - // for index in 0..2 { - // if (self.bb_castling_rigths[color as usize][index] | prev_pos).count_ones() == 1 - // { - // self.bb_castling_rigths[color as usize][index] = prev_pos; - // } - // } - // } - // Piece::King => { - // if matches!(color, Color::White) { - // // white kingside - // self.bb_castling_rigths[color as usize][0] = - // self.bb_pieces[color as usize][Piece::Rook as usize] & BOARD_SQUARES[63]; - // // white queenside - // self.bb_castling_rigths[color as usize][1] = - // self.bb_pieces[color as usize][Piece::Rook as usize] & BOARD_SQUARES[56]; - // } else { - // // black kingside - // self.bb_castling_rigths[color as usize][0] = - // self.bb_pieces[color as usize][Piece::Rook as usize] & BOARD_SQUARES[0]; - // // black queenside - // self.bb_castling_rigths[color as usize][1] = - // self.bb_pieces[color as usize][Piece::Rook as usize] & BOARD_SQUARES[7]; - // } - // } - // _ => (), - // } - // - // // undo move - // self.bb_pieces[color as usize][piece as usize] |= prev_pos; - // self.bb_pieces[color as usize][piece as usize] ^= cur_pos; - // - // // undo move in colors bb - // self.bb_colors[color as usize] |= prev_pos; - // self.bb_colors[color as usize] ^= cur_pos; - // - // // if move captured piece - // if !matches!(captured_piece, Piece::None) { - // // if captured piece is not empty - // self.bb_pieces[opposite_color as usize][captured_piece as usize] |= cur_pos; - // self.bb_colors[opposite_color as usize] |= cur_pos; - // self.bb_fullboard |= cur_pos; - // } - // - // // undo move in fullboard - // self.bb_fullboard = - // self.bb_colors[color as usize] | self.bb_colors[opposite_color as usize]; - // - // // halfmove undo - // if self.halfmove == 0 { - // // undo fullmove count only if white currently to move - // self.halfmove = 1; - // self.fullmove -= 1; - // } else { - // self.halfmove = 0; - // } - // - // self.to_move = opposite_color; - // - // Ok(()) - // } + pub fn undo_move(&mut self) -> Result<(), &str> { + let last_move = self.move_history.pop().expect("No more moves found!"); + + let (start_pos, end_pos, piece, color, captured_piece, is_promotion) = + self.decode_move(last_move); + + let opposite_color = match color { + Color::White => Color::Black, + Color::Black => Color::White, + }; + + // check for castling and castling avaliability + match piece { + Piece::Pawn => { + match color { + Color::White => { + // if piece was previosly of fourth rank + if start_pos & FOURTH_RANK != 0 { + self.bb_en_passant |= start_pos << 8; + } + } + Color::Black => { + // if piece was previosly of fifth rank + if start_pos & FIFTH_RANK != 0 { + self.bb_en_passant |= start_pos >> 8; + } + } + } + } + Piece::Rook => { + for index in 0..2 { + if (self.bb_castling_rigths[color as usize][index] | start_pos).count_ones() + == 1 + { + self.bb_castling_rigths[color as usize][index] = start_pos; + } + } + } + Piece::King => { + if matches!(color, Color::White) { + // white kingside + self.bb_castling_rigths[color as usize][0] = + self.bb_pieces[color as usize][Piece::Rook as usize] & BOARD_SQUARES[63]; + // white queenside + self.bb_castling_rigths[color as usize][1] = + self.bb_pieces[color as usize][Piece::Rook as usize] & BOARD_SQUARES[56]; + } else { + // black kingside + self.bb_castling_rigths[color as usize][0] = + self.bb_pieces[color as usize][Piece::Rook as usize] & BOARD_SQUARES[0]; + // black queenside + self.bb_castling_rigths[color as usize][1] = + self.bb_pieces[color as usize][Piece::Rook as usize] & BOARD_SQUARES[7]; + } + } + _ => (), + } + + // undo move + self.bb_pieces[color as usize][piece as usize] |= start_pos; + self.bb_pieces[color as usize][piece as usize] ^= end_pos; + + // undo move in colors bb + self.bb_colors[color as usize] |= start_pos; + self.bb_colors[color as usize] ^= end_pos; + + // if move captured piece + if !matches!(captured_piece, Piece::None) { + // if captured piece is not empty + self.bb_pieces[opposite_color as usize][captured_piece as usize] |= end_pos; + self.bb_colors[opposite_color as usize] |= end_pos; + self.bb_fullboard |= end_pos; + } + + // undo move in fullboard + self.bb_fullboard = + self.bb_colors[color as usize] | self.bb_colors[opposite_color as usize]; + + // halfmove undo + if self.halfmove == 0 { + // undo fullmove count only if white currently to move + self.halfmove = 1; + self.fullmove -= 1; + } else { + self.halfmove = 0; + } + + self.to_move = opposite_color; + + Ok(()) + } pub fn generate_moves_by_color(&self, color: &Color) -> Vec { let opposite_color: &Color = match *color { @@ -270,7 +316,7 @@ impl BoardState { while move_bb != 0 { let least_sign_bit = move_bb.trailing_zeros(); - moves_vec.push(encode_move( + moves_vec.push(self.encode_move( piece_move.0.trailing_zeros() as u8, least_sign_bit as u8, Piece::None, @@ -291,7 +337,7 @@ impl BoardState { while move_bb != 0 { let least_sign_bit = move_bb.trailing_zeros(); - moves_vec.push(encode_move( + moves_vec.push(self.encode_move( king.0.trailing_zeros() as u8, least_sign_bit as u8, Piece::None, @@ -312,7 +358,7 @@ impl BoardState { while move_bb != 0 { let least_sign_bit = move_bb.trailing_zeros(); - moves_vec.push(encode_move( + moves_vec.push(self.encode_move( piece_move.0.trailing_zeros() as u8, least_sign_bit as u8, Piece::None, @@ -334,7 +380,7 @@ impl BoardState { while move_bb != 0 { let least_sign_bit = move_bb.trailing_zeros(); - moves_vec.push(encode_move( + moves_vec.push(self.encode_move( piece_move.0.trailing_zeros() as u8, least_sign_bit as u8, Piece::None, @@ -356,7 +402,7 @@ impl BoardState { while move_bb != 0 { let least_sign_bit = move_bb.trailing_zeros(); - moves_vec.push(encode_move( + moves_vec.push(self.encode_move( piece_move.0.trailing_zeros() as u8, least_sign_bit as u8, Piece::None, @@ -378,7 +424,7 @@ impl BoardState { while move_bb != 0 { let least_sign_bit = move_bb.trailing_zeros(); - moves_vec.push(encode_move( + moves_vec.push(self.encode_move( piece_move.0.trailing_zeros() as u8, least_sign_bit as u8, Piece::None, diff --git a/src/test.rs b/src/test.rs index 36ee1d6..2fc0902 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,4 +1,7 @@ -use crate::{board::BoardState, constants::DEFAULT_FEN_STRING}; +use crate::{ + board::{BoardState, Color, Piece}, + constants::{BOARD_SQUARES, DEFAULT_FEN_STRING}, +}; #[test] fn from_fen_ok() { @@ -19,5 +22,29 @@ fn in_check() { let board = BoardState::from_fen("rnbqkbnr/ppppp1pp/8/5p1Q/4P3/8/PPPP1PPP/RNB1KBNR b KQkq - 0 1") .expect("Fail during board setup"); - assert!(board.is_in_check()) + assert!(board.is_in_check(Color::Black)) +} + +#[test] +fn encoding_test() { + let board = BoardState::from_fen(DEFAULT_FEN_STRING).expect("Fail during board setup"); + + let moves = board.encode_move(62, 52, Piece::None); + // choose a white right knight move + + assert_eq!(moves, 27966); +} + +#[test] +fn decoding_test() { + let board = BoardState::from_fen(DEFAULT_FEN_STRING).expect("Fail during board setup"); + + let moves = board.decode_move(board.generate_moves_by_color(&Color::White)[18]); + // choose a white right knight move + + assert_eq!(moves.0, BOARD_SQUARES[62]); + assert_eq!(moves.1, BOARD_SQUARES[45]); + assert!(matches!(moves.2, Piece::Knight)); + assert!(matches!(moves.3, Color::White)); + assert!(matches!(moves.4, Piece::None)); } diff --git a/src/utils.rs b/src/utils.rs index e32e3d5..d8b4bf9 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,4 @@ -use crate::board::{Bitboard, Piece}; +use crate::board::Bitboard; pub fn print_bitboard(bb: Bitboard) { let formatted_bb: String = format!("{:064b}", bb); @@ -29,7 +29,3 @@ pub fn print_bitboard(bb: Bitboard) { println!("\n a b c d e f g h\n"); println!("biboard is: {}", bb); } - -pub fn encode_move(from_bb: u8, to_bb: u8, capture: Piece) -> u16 { - from_bb as u16 | ((to_bb as u16) << 6) | ((capture as u16) << 12) -}