diff --git a/.github/workflows/upload_data.yml b/.github/workflows/upload_data.yml index c0ab435..ca88108 100644 --- a/.github/workflows/upload_data.yml +++ b/.github/workflows/upload_data.yml @@ -20,7 +20,7 @@ jobs: echo "$GOOGLE_SERVICE_ACCOUNT_JSON" > service_account.json python <<'PYCODE' - import gspread, json, subprocess + import gspread, json, subprocess, time creds = json.load(open("service_account.json")) gc = gspread.service_account_from_dict(creds) @@ -38,6 +38,7 @@ jobs: for i, row in enumerate(rows_to_append): worksheet.insert_row(row, start_row + i) + time.sleep(1) with open(f"{v}/test_data.log", "r") as f: diff --git a/engine/src/bitboard.rs b/engine/src/bitboard.rs index 504ae04..1486484 100644 --- a/engine/src/bitboard.rs +++ b/engine/src/bitboard.rs @@ -3,5 +3,7 @@ mod utils; mod legality; mod checkinfo; mod attacks; +mod bitmove; +mod movebuffer; pub mod board; \ No newline at end of file diff --git a/engine/src/bitboard/bitmove.rs b/engine/src/bitboard/bitmove.rs new file mode 100644 index 0000000..86c5b44 --- /dev/null +++ b/engine/src/bitboard/bitmove.rs @@ -0,0 +1,77 @@ +use super::utils::*; + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct BitMove { + + move_type: BitMoveType, + from_square: u8, + to_square: u8, + promotion_piece: Option +} + +impl BitMove { + + #[inline] + pub fn quiet(from: u8, to: u8, promotion_piece: Option) -> Self { + return Self { + move_type: BitMoveType::Quiet, + from_square: from, + to_square: to, + promotion_piece: promotion_piece + }; + } + #[inline] + pub fn capture(from: u8, to: u8, promotion_piece: Option) -> Self { + return Self { + move_type: BitMoveType::Capture, + from_square: from, + to_square: to, + promotion_piece: promotion_piece + }; + } + #[inline] + pub fn castle(from: u8, to: u8) -> Self { + return Self { + move_type: BitMoveType::Castle, + from_square: from, + to_square: to, + promotion_piece: None + }; + } + + #[inline(always)] + pub fn move_type(&self) -> BitMoveType { + return self.move_type; + } + #[inline(always)] + pub fn from_square(&self) -> u8 { + return self.from_square; + } + #[inline(always)] + pub fn to_square(&self) -> u8 { + return self.to_square; + } + #[inline(always)] + pub fn promotion_piece(&self) -> Option { + return self.promotion_piece; + } + + pub fn uci_notation(&self) -> String { + let mut notation = notation_from_square_number(self.from_square()); + notation.push_str(¬ation_from_square_number(self.to_square())); + + if let Some(promotion_piece) = self.promotion_piece { + notation.push(get_character_by_piece_id(promotion_piece).to_ascii_lowercase()); + } + + return notation; + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum BitMoveType { + Quiet, + Capture, + Castle, + EnPassant +} \ No newline at end of file diff --git a/engine/src/bitboard/board.rs b/engine/src/bitboard/board.rs index 11a85ce..483f1c4 100644 --- a/engine/src/bitboard/board.rs +++ b/engine/src/bitboard/board.rs @@ -64,7 +64,7 @@ impl Board { for (i, c) in coming_up.chars().enumerate() { if pieces.contains(&c) { - // board.place_piece(row*8 + col, c); + board.place_piece(row*8 + col, c); col += 1; } else if ('1'..='8').contains(&c) { @@ -179,4 +179,21 @@ impl Board { } } + pub fn place_piece(&mut self, sq: i32, piece: char) { + match piece { + 'p' => {self.bitboards[6] |= 1 << sq} + 'n' => {self.bitboards[7] |= 1 << sq} + 'b' => {self.bitboards[8] |= 1 << sq} + 'r' => {self.bitboards[9] |= 1 << sq} + 'q' => {self.bitboards[10] |= 1 << sq} + 'k' => {self.bitboards[11] |= 1 << sq} + 'P' => {self.bitboards[0] |= 1 << sq} + 'N' => {self.bitboards[1] |= 1 << sq} + 'B' => {self.bitboards[2] |= 1 << sq} + 'R' => {self.bitboards[3] |= 1 << sq} + 'Q' => {self.bitboards[4] |= 1 << sq} + 'K' => {self.bitboards[5] |= 1 << sq} + _ => () + } + } } \ No newline at end of file diff --git a/engine/src/bitboard/legality.rs b/engine/src/bitboard/legality.rs index 7d12e57..f42fdc1 100644 --- a/engine/src/bitboard/legality.rs +++ b/engine/src/bitboard/legality.rs @@ -1,8 +1,56 @@ use super::board::Board; use super::attackmaps::RAY_TABLE; +use super::checkinfo::CheckInfo; +use super::attacks::get_raycast_from_square_in_direction; impl Board { + pub fn check_test(&self) -> CheckInfo { + let mut check_info: CheckInfo = CheckInfo::new(); + let offset: usize = 6 * self.side_to_move as usize; + let king: u64 = self.bitboards[5 + offset]; + let king_sq = king.trailing_zeros() as usize; + let occupancy = self.occupancy[2]; + + // queen-rook checks (+) + let attacker_mask = self.bitboards[10 - offset] | self.bitboards[9 - offset]; + + for dir in [0, 2, 4, 6] { + let threat_mask: u64 = get_raycast_from_square_in_direction(occupancy, king_sq, dir); + if threat_mask & attacker_mask != 0 { + check_info.add_checker(threat_mask); + } + } + + // queen-bishop checks (x) + let attacker_mask = self.bitboards[10 - offset] | self.bitboards[8 - offset]; + + for dir in [1, 3, 5, 7] { + let threat_mask = get_raycast_from_square_in_direction(occupancy, king_sq, dir); + if threat_mask & attacker_mask != 0 { + check_info.add_checker(threat_mask); + } + } + + // knight checks (L) + let attacker_mask = self.bitboards[7 - offset]; + let threat_mask = self.get_pseudo_knight_moves(king_sq as u32); + let checker = threat_mask & attacker_mask; + if checker != 0 { + check_info.add_checker(checker); + } + + // pawn checks (v) + let attacker_mask = self.bitboards[6 - offset]; + let threat_mask = self.get_pseudo_pawn_captures(king_sq as u32); + let checker = threat_mask & attacker_mask; + if checker != 0 { + check_info.add_checker(checker); + } + + return check_info; + } + pub(in super) fn calc_pinned_squares(&mut self) { self.pinned_squares = [4; 64]; self.pin_mask = 0u64; @@ -45,4 +93,35 @@ impl Board { } } +} + +// <----- TESTS -----> + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn check_test_test() { + + let fens = [ + "rnb1k1nr/pppppppp/4q3/8/1b6/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", // no check + "rnb1k1nr/pppppppp/4q3/8/1b1P4/8/PPP1PPPP/RNBQKBNR w KQkq d3 0 1", // single check + "rnb1k1nr/ppp1p2p/3pq1p1/8/1b1P1P2/8/PPP2PPP/RNBQKBNR w KQkq - 0 1" // double check + ]; + let expected_results = [ + CheckInfo { check_count: 0, move_mask: 0xFFFF_FFFF_FFFF_FFFF }, + CheckInfo { check_count: 1, move_mask: 0x0000_0000_0204_0800 }, + CheckInfo { check_count: 2, move_mask: 0x0000_0000_0000_0000 } + ]; + + for test_nr in 0..3 { + let fen = fens[test_nr]; + let board = Board::build(fen); + let check_test_actual = board.check_test(); + assert_eq!(check_test_actual.check_count, expected_results[test_nr].check_count); + assert_eq!(check_test_actual.move_mask, expected_results[test_nr].move_mask); + } + + } } \ No newline at end of file diff --git a/engine/src/bitboard/movebuffer.rs b/engine/src/bitboard/movebuffer.rs new file mode 100644 index 0000000..61a7844 --- /dev/null +++ b/engine/src/bitboard/movebuffer.rs @@ -0,0 +1,44 @@ +use super::bitmove::BitMove; + +pub struct MoveBuffer { + + buffer: [BitMove; 256], + count: usize +} + +impl MoveBuffer { + + pub fn new() -> Self { + return Self { + buffer: [BitMove::quiet(0, 0, None); 256], + count: 0 + }; + } + + #[inline] + pub fn add(&mut self, bitmove: BitMove) { + self.buffer[self.count] = bitmove; + self.count += 1; + } + #[inline] + pub fn append(&mut self, other: &MoveBuffer) { + self.buffer[self.count..self.count + other.count()].copy_from_slice(other.contents()); + self.count += other.count(); + } + #[inline(always)] + pub fn clear(&mut self) { + self.count = 0; + } + #[inline(always)] + pub fn count(&self) -> usize{ + return self.count; + } + #[inline(always)] + pub fn get(&self, idx: usize) -> &BitMove { + return &self.buffer[idx]; + } + #[inline(always)] + pub fn contents(&self) -> &[BitMove] { + return &self.buffer[0..self.count]; + } +} \ No newline at end of file diff --git a/engine/src/bitboard/utils.rs b/engine/src/bitboard/utils.rs index 25d7ba8..f7f83fe 100644 --- a/engine/src/bitboard/utils.rs +++ b/engine/src/bitboard/utils.rs @@ -48,6 +48,11 @@ pub fn try_get_square_number_from_notation(notation: &str) -> Result { } } +const PIECE_CHARACTERS: [char; 12] = ['P', 'N', 'B', 'R', 'Q', 'K', 'p', 'n', 'b', 'r', 'q', 'k']; +pub fn get_character_by_piece_id(id: u8) -> char { + return PIECE_CHARACTERS[id as usize]; +} + // <----- TESTS ----->