From 5e19c1e4943e29df0843425260bacd9b354b0f57 Mon Sep 17 00:00:00 2001 From: Moritz Date: Thu, 13 Nov 2025 14:47:43 +0100 Subject: [PATCH] finished make_move --- src/board.rs | 427 +++++++++++++++++++++++++++++-------- src/main.rs | 7 +- src/move.rs | 48 +++-- src/movegen/non_sliders.rs | 9 +- src/movegen/pawns.rs | 22 +- src/square.rs | 16 ++ 6 files changed, 399 insertions(+), 130 deletions(-) diff --git a/src/board.rs b/src/board.rs index 9df2d05..6018337 100644 --- a/src/board.rs +++ b/src/board.rs @@ -1,20 +1,27 @@ -use crate::square::Square; -use std::mem; use crate::r#move::*; +use crate::square::{Square, SQUARES}; +use std::mem; use std::ops::Not; -pub const CASTLING_WK: u8 = 1; +pub const CASTLING_WK_FLAG: u8 = 1; pub const CASTLING_WK_MASK: u64 = 96; // F1 G1 +pub const CASTLING_WK_K_POS_MASK: u64 = 16; // E1 +pub const CASTLING_WK_R_POS_MASK: u64 = 128; // H1 -pub const CASTLING_WQ: u8 = 2; +pub const CASTLING_WQ_FLAG: u8 = 2; pub const CASTLING_WQ_MASK: u64 = 14; // B1 C1 D1 +pub const CASTLING_WQ_K_POS_MASK: u64 = 16; // E1 +pub const CASTLING_WQ_R_POS_MASK: u64 = 1; // A1 -pub const CASTLING_BK: u8 = 4; +pub const CASTLING_BK_FLAG: u8 = 4; pub const CASTLING_BK_MASK: u64 = 6917529027641081856; // F8 G8 +pub const CASTLING_BK_K_POS_MASK: u64 = 1152921504606846976; // E8 +pub const CASTLING_BK_R_POS_MASK: u64 = 9223372036854775808; // H8 -pub const CASTLING_BQ: u8 = 8; +pub const CASTLING_BQ_FLAG: u8 = 8; pub const CASTLING_BQ_MASK: u64 = 1008806316530991104; // B8 C8 D8 - +pub const CASTLING_BQ_K_POS_MASK: u64 = 1152921504606846976; // E8 +pub const CASTLING_BQ_R_POS_MASK: u64 = 72057594037927936; // A8 #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] @@ -51,13 +58,15 @@ pub const PIECE_TYPES: [PieceType; 6] = [ PieceType::Bishop, PieceType::Rook, PieceType::Queen, - PieceType::King]; + PieceType::King, +]; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Board { pub side_to_move: Color, pub pieces: [[u64; 2]; 6], + pub pieces_on_squares: [Option; 64], // <-- ADDED pub occupied: [u64; 2], pub all_occupied: u64, @@ -76,9 +85,10 @@ impl Board { pub fn from_fen(fen: &str) -> Self { let mut parts = fen.split_whitespace(); - // Initialisiere das 2D-Array + // Initialisiere die Arrays let mut pieces = [[0u64; 2]; 6]; let mut occupied = [0u64; 2]; + let mut pieces_on_squares = [None; 64]; // <-- ADDED // Part 1: Piece placement let placement = parts.next().unwrap_or(""); @@ -99,24 +109,60 @@ impl Board { let color_idx = Color::White as usize; occupied[color_idx] |= mask; match c { - 'P' => pieces[PieceType::Pawn as usize][color_idx] |= mask, - 'N' => pieces[PieceType::Knight as usize][color_idx] |= mask, - 'B' => pieces[PieceType::Bishop as usize][color_idx] |= mask, - 'R' => pieces[PieceType::Rook as usize][color_idx] |= mask, - 'Q' => pieces[PieceType::Queen as usize][color_idx] |= mask, - 'K' => pieces[PieceType::King as usize][color_idx] |= mask, + 'P' => { + pieces[PieceType::Pawn as usize][color_idx] |= mask; + pieces_on_squares[sq as usize] = Some(PieceType::Pawn); // <-- ADDED + } + 'N' => { + pieces[PieceType::Knight as usize][color_idx] |= mask; + pieces_on_squares[sq as usize] = Some(PieceType::Knight); // <-- ADDED + } + 'B' => { + pieces[PieceType::Bishop as usize][color_idx] |= mask; + pieces_on_squares[sq as usize] = Some(PieceType::Bishop); // <-- ADDED + } + 'R' => { + pieces[PieceType::Rook as usize][color_idx] |= mask; + pieces_on_squares[sq as usize] = Some(PieceType::Rook); // <-- ADDED + } + 'Q' => { + pieces[PieceType::Queen as usize][color_idx] |= mask; + pieces_on_squares[sq as usize] = Some(PieceType::Queen); // <-- ADDED + } + 'K' => { + pieces[PieceType::King as usize][color_idx] |= mask; + pieces_on_squares[sq as usize] = Some(PieceType::King); // <-- ADDED + } _ => {} } } else { let color_idx = Color::Black as usize; occupied[color_idx] |= mask; match c { - 'p' => pieces[PieceType::Pawn as usize][color_idx] |= mask, - 'n' => pieces[PieceType::Knight as usize][color_idx] |= mask, - 'b' => pieces[PieceType::Bishop as usize][color_idx] |= mask, - 'r' => pieces[PieceType::Rook as usize][color_idx] |= mask, - 'q' => pieces[PieceType::Queen as usize][color_idx] |= mask, - 'k' => pieces[PieceType::King as usize][color_idx] |= mask, + 'p' => { + pieces[PieceType::Pawn as usize][color_idx] |= mask; + pieces_on_squares[sq as usize] = Some(PieceType::Pawn); // <-- ADDED + } + 'n' => { + pieces[PieceType::Knight as usize][color_idx] |= mask; + pieces_on_squares[sq as usize] = Some(PieceType::Knight); // <-- ADDED + } + 'b' => { + pieces[PieceType::Bishop as usize][color_idx] |= mask; + pieces_on_squares[sq as usize] = Some(PieceType::Bishop); // <-- ADDED + } + 'r' => { + pieces[PieceType::Rook as usize][color_idx] |= mask; + pieces_on_squares[sq as usize] = Some(PieceType::Rook); // <-- ADDED + } + 'q' => { + pieces[PieceType::Queen as usize][color_idx] |= mask; + pieces_on_squares[sq as usize] = Some(PieceType::Queen); // <-- ADDED + } + 'k' => { + pieces[PieceType::King as usize][color_idx] |= mask; + pieces_on_squares[sq as usize] = Some(PieceType::King); // <-- ADDED + } _ => {} } } @@ -133,10 +179,18 @@ impl Board { // Part 3: Castling rights let mut castling_rights = 0u8; if let Some(castle_str) = parts.next() { - if castle_str.contains('K') { castling_rights |= CASTLING_WK; } - if castle_str.contains('Q') { castling_rights |= CASTLING_WQ; } - if castle_str.contains('k') { castling_rights |= CASTLING_BK; } - if castle_str.contains('q') { castling_rights |= CASTLING_BQ; } + if castle_str.contains('K') { + castling_rights |= CASTLING_WK_FLAG; + } + if castle_str.contains('Q') { + castling_rights |= CASTLING_WQ_FLAG; + } + if castle_str.contains('k') { + castling_rights |= CASTLING_BK_FLAG; + } + if castle_str.contains('q') { + castling_rights |= CASTLING_BQ_FLAG; + } } // Part 4: En passant target @@ -163,7 +217,8 @@ impl Board { Board { side_to_move, - pieces, // GeƤndertes Feld + pieces, + pieces_on_squares, // <-- ADDED occupied, all_occupied, empty_squares, @@ -206,15 +261,27 @@ impl Board { // Part 2: Active color fen.push(' '); - fen.push(if self.side_to_move == Color::White { 'w' } else { 'b' }); + fen.push(if self.side_to_move == Color::White { + 'w' + } else { + 'b' + }); // Part 3: Castling rights fen.push(' '); let mut castle_str = String::new(); - if (self.castling_rights & CASTLING_WK) != 0 { castle_str.push('K'); } - if (self.castling_rights & CASTLING_WQ) != 0 { castle_str.push('Q'); } - if (self.castling_rights & CASTLING_BK) != 0 { castle_str.push('k'); } - if (self.castling_rights & CASTLING_BQ) != 0 { castle_str.push('q'); } + if (self.castling_rights & CASTLING_WK_FLAG) != 0 { + castle_str.push('K'); + } + if (self.castling_rights & CASTLING_WQ_FLAG) != 0 { + castle_str.push('Q'); + } + if (self.castling_rights & CASTLING_BK_FLAG) != 0 { + castle_str.push('k'); + } + if (self.castling_rights & CASTLING_BQ_FLAG) != 0 { + castle_str.push('q'); + } if castle_str.is_empty() { fen.push('-'); @@ -250,18 +317,42 @@ impl Board { let white = Color::White as usize; let black = Color::Black as usize; - if (self.pieces[PieceType::Pawn as usize][white] & sq_mask) != 0 { return Some('P'); } - if (self.pieces[PieceType::Pawn as usize][black] & sq_mask) != 0 { return Some('p'); } - if (self.pieces[PieceType::Knight as usize][white] & sq_mask) != 0 { return Some('N'); } - if (self.pieces[PieceType::Knight as usize][black] & sq_mask) != 0 { return Some('n'); } - if (self.pieces[PieceType::Bishop as usize][white] & sq_mask) != 0 { return Some('B'); } - if (self.pieces[PieceType::Bishop as usize][black] & sq_mask) != 0 { return Some('b'); } - if (self.pieces[PieceType::Rook as usize][white] & sq_mask) != 0 { return Some('R'); } - if (self.pieces[PieceType::Rook as usize][black] & sq_mask) != 0 { return Some('r'); } - if (self.pieces[PieceType::Queen as usize][white] & sq_mask) != 0 { return Some('Q'); } - if (self.pieces[PieceType::Queen as usize][black] & sq_mask) != 0 { return Some('q'); } - if (self.pieces[PieceType::King as usize][white] & sq_mask) != 0 { return Some('K'); } - if (self.pieces[PieceType::King as usize][black] & sq_mask) != 0 { return Some('k'); } + if (self.pieces[PieceType::Pawn as usize][white] & sq_mask) != 0 { + return Some('P'); + } + if (self.pieces[PieceType::Pawn as usize][black] & sq_mask) != 0 { + return Some('p'); + } + if (self.pieces[PieceType::Knight as usize][white] & sq_mask) != 0 { + return Some('N'); + } + if (self.pieces[PieceType::Knight as usize][black] & sq_mask) != 0 { + return Some('n'); + } + if (self.pieces[PieceType::Bishop as usize][white] & sq_mask) != 0 { + return Some('B'); + } + if (self.pieces[PieceType::Bishop as usize][black] & sq_mask) != 0 { + return Some('b'); + } + if (self.pieces[PieceType::Rook as usize][white] & sq_mask) != 0 { + return Some('R'); + } + if (self.pieces[PieceType::Rook as usize][black] & sq_mask) != 0 { + return Some('r'); + } + if (self.pieces[PieceType::Queen as usize][white] & sq_mask) != 0 { + return Some('Q'); + } + if (self.pieces[PieceType::Queen as usize][black] & sq_mask) != 0 { + return Some('q'); + } + if (self.pieces[PieceType::King as usize][white] & sq_mask) != 0 { + return Some('K'); + } + if (self.pieces[PieceType::King as usize][black] & sq_mask) != 0 { + return Some('k'); + } None } @@ -286,7 +377,6 @@ impl Board { println!(" a b c d e f g h\n"); } - /// Prints a single bitboard (u64) as an 8x8 grid for debugging. fn print_bitboard(&self, name: &str, bitboard: u64) { println!("--- {} ---", name); @@ -319,17 +409,35 @@ impl Board { self.print_bitboard("White Pawns", self.pieces[PieceType::Pawn as usize][white]); self.print_bitboard("Black Pawns", self.pieces[PieceType::Pawn as usize][black]); - self.print_bitboard("White Knights", self.pieces[PieceType::Knight as usize][white]); - self.print_bitboard("Black Knights", self.pieces[PieceType::Knight as usize][black]); + self.print_bitboard( + "White Knights", + self.pieces[PieceType::Knight as usize][white], + ); + self.print_bitboard( + "Black Knights", + self.pieces[PieceType::Knight as usize][black], + ); - self.print_bitboard("White Bishops", self.pieces[PieceType::Bishop as usize][white]); - self.print_bitboard("Black Bishops", self.pieces[PieceType::Bishop as usize][black]); + self.print_bitboard( + "White Bishops", + self.pieces[PieceType::Bishop as usize][white], + ); + self.print_bitboard( + "Black Bishops", + self.pieces[PieceType::Bishop as usize][black], + ); self.print_bitboard("White Rooks", self.pieces[PieceType::Rook as usize][white]); self.print_bitboard("Black Rooks", self.pieces[PieceType::Rook as usize][black]); - self.print_bitboard("White Queens", self.pieces[PieceType::Queen as usize][white]); - self.print_bitboard("Black Queens", self.pieces[PieceType::Queen as usize][black]); + self.print_bitboard( + "White Queens", + self.pieces[PieceType::Queen as usize][white], + ); + self.print_bitboard( + "Black Queens", + self.pieces[PieceType::Queen as usize][black], + ); self.print_bitboard("White King", self.pieces[PieceType::King as usize][white]); self.print_bitboard("Black King", self.pieces[PieceType::King as usize][black]); @@ -343,63 +451,196 @@ impl Board { println!("============================================\n"); } - fn capture_black_piece(&mut self, target_square_bitboard: u64) -> PieceType { + fn clear_square(&mut self, target_square: Square, color: Color) -> PieceType { + let target_square_bitboard = target_square.to_bitboard(); + + // update occupancy helper bitboards + self.occupied[color as usize] ^= target_square_bitboard; + self.all_occupied ^= target_square_bitboard; + self.empty_squares |= target_square_bitboard; + + self.pieces_on_squares[target_square as usize] = None; // <-- ADDED + for piece_type in PIECE_TYPES { - if self.pieces[piece_type as usize][Color::Black as usize] & target_square_bitboard > 0 { - self.pieces[piece_type as usize][Color::Black as usize] ^= target_square_bitboard; + if self.pieces[piece_type as usize][color as usize] & target_square_bitboard > 0 { + self.pieces[piece_type as usize][color as usize] ^= target_square_bitboard; return piece_type; } } - panic!("fn 'capture_black_piece' failed: no piece found"); + panic!("fn 'clear_square' failed: no piece found"); } - fn capture_white_piece(&mut self, target_square_bitboard: u64) -> PieceType { - for piece_type in PIECE_TYPES { - if self.pieces[piece_type as usize][Color::White as usize] & target_square_bitboard > 0 { - self.pieces[piece_type as usize][Color::White as usize] ^= target_square_bitboard; - return piece_type; - } - } - panic!("fn 'capture_white_piece' failed: no piece found"); + fn remove_specific_piece( + &mut self, + target_square: Square, + color: Color, + piece_type: PieceType, + ) { + let target_square_bitboard = target_square.to_bitboard(); + self.pieces[piece_type as usize][color as usize] ^= target_square_bitboard; + + self.pieces_on_squares[target_square as usize] = None; // <-- ADDED + + // update occupancy helper bitboards + self.occupied[color as usize] ^= target_square_bitboard; + self.all_occupied ^= target_square_bitboard; + self.empty_squares |= target_square_bitboard; } - fn put_white_piece(&mut self, target_square_bitboard: u64, piece_type: PieceType) { - self.pieces[piece_type as usize][Color::White as usize] |= target_square_bitboard; - } + fn put_piece(&mut self, target_square: Square, color: Color, piece_type: PieceType) { + let target_square_bitboard = target_square.to_bitboard(); + self.pieces[piece_type as usize][color as usize] |= target_square_bitboard; - fn put_black_piece(&mut self, target_square_bitboard: u64, piece_type: PieceType) { - self.pieces[piece_type as usize][Color::Black as usize] |= target_square_bitboard; + self.pieces_on_squares[target_square as usize] = Some(piece_type); // <-- ADDED + + // update occupancy helper bitboards + self.occupied[color as usize] |= target_square_bitboard; + self.all_occupied |= target_square_bitboard; + self.empty_squares ^= target_square_bitboard; } pub fn make_move(&mut self, mv: Move) -> UndoMove { - // TODO implement .get_from, .get_to, .get_flag - let from = mv.value()& MOVE_FROM_MASK; - let to = (mv.value() & MOVE_TO_MASK) >> 6; - let flag = mv.value() & MOVE_FLAG_MASK; + let from = mv.get_from(); + let to = mv.get_to(); + let flags = mv.get_flags(); - // TODO castle, en passant, promotion - if self.side_to_move == Color::White { - return if flag & MOVE_FLAG_CAPTURE > 0 { // Capture - let piece_type_from = self.capture_white_piece(1_u64 << from); - let piece_type_capture = self.capture_black_piece(1_u64 << to); - self.put_white_piece(1_u64 << to, piece_type_from); - UndoMove::new(mv, - Some(piece_type_capture), - self.en_passant_target, - self.castling_rights, - self.halfmove_clock) - } else { // No capture - let piece_type_from = self.capture_white_piece(1_u64 << from); - self.put_white_piece(1_u64 << to, piece_type_from); - UndoMove::new(mv, - None, - self.en_passant_target, - self.castling_rights, - self.halfmove_clock) + let old_en_passant_target: Option = self.en_passant_target; + let old_castling_rights: u8 = self.castling_rights; + let old_halfmove_clock: u8 = self.halfmove_clock; + + // needed for half move tracking + let old_friendly_pawns = self.pieces[PieceType::Pawn as usize][self.side_to_move as usize]; + let old_total_pieces = self.all_occupied.count_ones(); + + let mut opt_captured_piece: Option = None; + let mut opt_en_passant_target: Option = None; + + match flags { + MOVE_FLAG_QUIET => { + let piece_type_from = self.clear_square(from, self.side_to_move); + self.put_piece(to, self.side_to_move, piece_type_from); + } + MOVE_FLAG_CAPTURE => { + let piece_type_from = self.clear_square(from, self.side_to_move); + opt_captured_piece = Some(self.clear_square(to, !self.side_to_move)); + self.put_piece(to, self.side_to_move, piece_type_from); + } + MOVE_FLAG_DOUBLE_PAWN => { + let piece_type_from = self.clear_square(from, self.side_to_move); + self.put_piece(to, self.side_to_move, piece_type_from); + opt_en_passant_target = Some(to + (self.side_to_move as i8 * 16 - 8)); + } + MOVE_FLAG_PROMO_Q => { + self.remove_specific_piece(from, self.side_to_move, PieceType::Pawn); + self.put_piece(to, self.side_to_move, PieceType::Queen); + } + MOVE_FLAG_PROMO_N => { + self.remove_specific_piece(from, self.side_to_move, PieceType::Pawn); + self.put_piece(to, self.side_to_move, PieceType::Knight); + } + MOVE_FLAG_PROMO_B => { + self.remove_specific_piece(from, self.side_to_move, PieceType::Pawn); + self.put_piece(to, self.side_to_move, PieceType::Bishop); + } + MOVE_FLAG_PROMO_R => { + self.remove_specific_piece(from, self.side_to_move, PieceType::Pawn); + self.put_piece(to, self.side_to_move, PieceType::Rook); + } + MOVE_FLAG_PROMO_Q_CAP => { + self.remove_specific_piece(from, self.side_to_move, PieceType::Pawn); + opt_captured_piece = Some(self.clear_square(to, !self.side_to_move)); + self.put_piece(to, self.side_to_move, PieceType::Queen); + } + MOVE_FLAG_PROMO_N_CAP => { + self.remove_specific_piece(from, self.side_to_move, PieceType::Pawn); + opt_captured_piece = Some(self.clear_square(to, !self.side_to_move)); + self.put_piece(to, self.side_to_move, PieceType::Knight); + } + MOVE_FLAG_PROMO_B_CAP => { + self.remove_specific_piece(from, self.side_to_move, PieceType::Pawn); + opt_captured_piece = Some(self.clear_square(to, !self.side_to_move)); + self.put_piece(to, self.side_to_move, PieceType::Bishop); + } + MOVE_FLAG_PROMO_R_CAP => { + self.remove_specific_piece(from, self.side_to_move, PieceType::Pawn); + opt_captured_piece = Some(self.clear_square(to, !self.side_to_move)); + self.put_piece(to, self.side_to_move, PieceType::Rook); + } + MOVE_FLAG_WK_CASTLE => { + self.remove_specific_piece(Square::E1, Color::White, PieceType::King); + self.remove_specific_piece(Square::H1, Color::White, PieceType::Rook); + self.put_piece(Square::G1, Color::White, PieceType::King); + self.put_piece(Square::F1, Color::White, PieceType::Rook); + } + MOVE_FLAG_BK_CASTLE => { + self.remove_specific_piece(Square::E8, Color::Black, PieceType::King); + self.remove_specific_piece(Square::H8, Color::Black, PieceType::Rook); + self.put_piece(Square::G8, Color::Black, PieceType::King); + self.put_piece(Square::F8, Color::Black, PieceType::Rook); + } + MOVE_FLAG_WQ_CASTLE => { + self.remove_specific_piece(Square::E1, Color::White, PieceType::King); + self.remove_specific_piece(Square::A1, Color::White, PieceType::Rook); + self.put_piece(Square::C1, Color::White, PieceType::King); + self.put_piece(Square::D1, Color::White, PieceType::Rook); + } + MOVE_FLAG_BQ_CASTLE => { + self.remove_specific_piece(Square::E8, Color::Black, PieceType::King); + self.remove_specific_piece(Square::A8, Color::Black, PieceType::Rook); + self.put_piece(Square::C8, Color::Black, PieceType::King); + self.put_piece(Square::D8, Color::Black, PieceType::Rook); + } + MOVE_FLAG_EN_PASSANT => { + self.remove_specific_piece(from, self.side_to_move, PieceType::Pawn); + if self.side_to_move == Color::White { + self.remove_specific_piece(to - 8_u8, !self.side_to_move, PieceType::Pawn); + } else { + self.remove_specific_piece(to + 8_u8, !self.side_to_move, PieceType::Pawn); + } + opt_captured_piece = Some(PieceType::Pawn); + self.put_piece(to, self.side_to_move, PieceType::Pawn); + } + _ => { + panic!("unable to make_move: invalid flags: {}", flags); } - } else { // Black - } - panic!("not yet implemented"); + + // set castle rights + let wk = self.pieces[PieceType::King as usize][Color::White as usize]; + let wr = self.pieces[PieceType::Rook as usize][Color::White as usize]; + let bk = self.pieces[PieceType::King as usize][Color::Black as usize]; + let br = self.pieces[PieceType::Rook as usize][Color::Black as usize]; + let castling_right_wk = ((wk & CASTLING_WK_K_POS_MASK) > 0 && (wr & CASTLING_WK_R_POS_MASK) > 0) as u8; + let castling_right_wq = (((wk & CASTLING_WQ_K_POS_MASK) > 0 && (wr & CASTLING_WQ_R_POS_MASK) > 0) as u8) << 1; + let castling_right_bk = (((bk & CASTLING_BK_K_POS_MASK) > 0 && (br & CASTLING_BK_R_POS_MASK) > 0) as u8) << 2; + let castling_right_bq = (((bk & CASTLING_BQ_K_POS_MASK) > 0 && (br & CASTLING_BQ_R_POS_MASK) > 0) as u8) << 3; + let new_castling_rights = + castling_right_wk | castling_right_wq | castling_right_bk | castling_right_bq; + self.castling_rights = self.castling_rights & new_castling_rights; // & operator makes sure castling rights cant be gained back + + // set new en passant target + self.en_passant_target = opt_en_passant_target; + + // increase halfmove clock by 1 if no pawn was pushed and now piece captured + let new_friendly_pawns = self.pieces[PieceType::Pawn as usize][self.side_to_move as usize]; + let new_total_pieces = self.all_occupied.count_ones(); + let pawns_changed = old_friendly_pawns ^ new_friendly_pawns; + let piece_captured = (old_total_pieces - new_total_pieces) as u64; + let increase_halfmove_clock = ((pawns_changed + piece_captured) == 0) as u8; + self.halfmove_clock = increase_halfmove_clock * (self.halfmove_clock + 1); + + // increase full move number by 1 when black made a move + self.fullmove_number += self.side_to_move as u16; + + // flip the side to move + self.side_to_move = !self.side_to_move; + + UndoMove::new( + mv, + opt_captured_piece, + old_en_passant_target, + old_castling_rights, + old_halfmove_clock, + ) } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 3dbca9f..17a06d6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,11 +10,10 @@ fn main() { generate_pseudo_legal_moves(&board, &mut move_list); for mv in move_list.iter() { - board.pretty_print_ascii(); + board = Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); println!("--> making move: {}", mv.to_algebraic()); - let mut board_clone = board.clone(); - board_clone.make_move(*mv); - board_clone.pretty_print_ascii(); + board.make_move(*mv); + board.pretty_print_ascii(); println!("---------------------"); } } diff --git a/src/move.rs b/src/move.rs index d2ba77d..8c78ff6 100644 --- a/src/move.rs +++ b/src/move.rs @@ -2,6 +2,7 @@ use std::slice; use std::fmt; use crate::square::Square; use crate::board::PieceType; +use crate::square::SQUARES; // BIT 0 - 5: FROM SQUARE (0-63) pub const MOVE_FROM_MASK: u16 = 0b0000_0000_0011_1111; @@ -14,6 +15,7 @@ pub const MOVE_FLAG_MASK: u16 = 0b1111_0000_0000_0000; pub const MOVE_FLAG_QUIET: u16 = 0b0000_0000_0000_0000; pub const MOVE_FLAG_CAPTURE: u16 = 0b0001_0000_0000_0000; +pub const MOVE_FLAG_DOUBLE_PAWN: u16 = 0b0010_0000_0000_0000; pub const MOVE_FLAG_EN_PASSANT: u16 = 0b0011_0000_0000_0000; // Castle flags @@ -27,6 +29,7 @@ pub const MOVE_FLAG_BQ_CASTLE: u16 = 0b0111_0000_0000_0000; // Promotion flags (use the 1xxx bits) // We combine capture flag with promotion type +pub const MOVE_MASK_PROMO: u16 = 0b1000_0000_0000_0000; pub const MOVE_FLAG_PROMO: u16 = 0b1000_0000_0000_0000; pub const MOVE_FLAG_PROMO_N: u16 = 0b1000_0000_0000_0000; @@ -47,16 +50,25 @@ impl Move { ((to as u16) << 6 ) | from as u16) } - - pub fn value(&self) -> u16 { - self.0 - } - + + #[inline(always)] pub fn get_flags(&self) -> u16 { self.0 & MOVE_FLAG_MASK } + #[inline(always)] + pub fn get_from(&self) -> Square { + SQUARES[(self.0 & MOVE_FROM_MASK) as usize] + } + + #[inline(always)] + pub fn get_to(&self) -> Square { + // --- KORREKTUR HIER --- + // Die Klammern um (self.0 & MOVE_TO_MASK) sind entscheidend + SQUARES[((self.0 & MOVE_TO_MASK) >> 6) as usize] + } + /// Converts a square index (0-63) to algebraic notation (e.g., 0 -> "a1", 63 -> "h8"). fn square_val_to_alg(val: u16) -> String { @@ -68,7 +80,7 @@ impl Move { /// Converts the move to coordinate notation (e.g., "e2e4", "e7e8q", "e1g1"). pub fn to_algebraic(&self) -> String { let flags = self.get_flags(); - + // Handle castling first. In this new format, the "to" square is // the *king's* destination square (g1/c1 or g8/c8). // Your old implementation reading the file is still fine. @@ -158,17 +170,17 @@ pub struct UndoMove { impl UndoMove { pub fn new(mv: Move, - captured_piece: Option, - old_en_passant_square: Option, - old_castling_rights: u8, - old_halfmove_clock: u8) -> Self { - Self { - mv, - captured_piece, - old_en_passant_square, - old_castling_rights, - old_halfmove_clock - } - + captured_piece: Option, + old_en_passant_square: Option, + old_castling_rights: u8, + old_halfmove_clock: u8) -> Self { + Self { + mv, + captured_piece, + old_en_passant_square, + old_castling_rights, + old_halfmove_clock } + + } } \ No newline at end of file diff --git a/src/movegen/non_sliders.rs b/src/movegen/non_sliders.rs index b3dc0d4..594cfa0 100644 --- a/src/movegen/non_sliders.rs +++ b/src/movegen/non_sliders.rs @@ -30,6 +30,7 @@ pub fn generate_knight_moves(board: &Board, list: &mut MoveList) { } pub fn generate_king_moves(board: &Board, list: &mut MoveList) { + // TODO no castle when square or piece under attack let enemy_occupied = board.occupied[!board.side_to_move as usize]; let friendly_king = board.pieces[PieceType::King as usize][board.side_to_move as usize]; @@ -59,26 +60,26 @@ pub fn generate_king_moves(board: &Board, list: &mut MoveList) { // 2. Generate castling king moves if board.side_to_move == Color::White { // Kingside (OO) - if (board.castling_rights & CASTLING_WK) != 0 { + if (board.castling_rights & CASTLING_WK_FLAG) != 0 { if (board.all_occupied & CASTLING_WK_MASK) == 0 { list.push(Move::new(Square::E1, Square::G1, MOVE_FLAG_WK_CASTLE)); } } // Queenside (OOO) - if (board.castling_rights & CASTLING_WQ) != 0 { + if (board.castling_rights & CASTLING_WQ_FLAG) != 0 { if (board.all_occupied & CASTLING_WQ_MASK) == 0 { list.push(Move::new(Square::E1, Square::C1, MOVE_FLAG_WQ_CASTLE)); } } } else { // Black // Kingside (OO) - if (board.castling_rights & CASTLING_BK) != 0 { + if (board.castling_rights & CASTLING_BK_FLAG) != 0 { if (board.all_occupied & CASTLING_BK_MASK) == 0 { list.push(Move::new(Square::E8, Square::G8, MOVE_FLAG_BK_CASTLE)); } } // Queenside (OOO) - if (board.castling_rights & CASTLING_BQ) != 0 { + if (board.castling_rights & CASTLING_BQ_FLAG) != 0 { if (board.all_occupied & CASTLING_BQ_MASK) == 0 { list.push(Move::new(Square::E8, Square::C8, MOVE_FLAG_BQ_CASTLE)); } diff --git a/src/movegen/pawns.rs b/src/movegen/pawns.rs index 9980f96..f87b8e9 100644 --- a/src/movegen/pawns.rs +++ b/src/movegen/pawns.rs @@ -52,7 +52,7 @@ pub fn generate_pawn_moves(board: &Board, list: &mut MoveList) { while double_push_targets > 0 { let to = SQUARES[double_push_targets.trailing_zeros() as usize]; let from = to - 16; - list.push(Move::new(from, to, MOVE_FLAG_QUIET)); + list.push(Move::new(from, to, MOVE_FLAG_DOUBLE_PAWN)); double_push_targets &= double_push_targets - 1; } @@ -149,7 +149,7 @@ pub fn generate_pawn_moves(board: &Board, list: &mut MoveList) { while single_push_targets > 0 { let to = SQUARES[single_push_targets.trailing_zeros() as usize]; - let from = to + 8; + let from = to + 8_u8; list.push(Move::new(from, to, MOVE_FLAG_QUIET)); single_push_targets &= single_push_targets - 1; } @@ -161,8 +161,8 @@ pub fn generate_pawn_moves(board: &Board, list: &mut MoveList) { while double_push_targets > 0 { let to = SQUARES[double_push_targets.trailing_zeros() as usize]; - let from = to + 16; - list.push(Move::new(from, to, MOVE_FLAG_QUIET)); + let from = to + 16_u8; + list.push(Move::new(from, to, MOVE_FLAG_DOUBLE_PAWN)); double_push_targets &= double_push_targets - 1; } @@ -172,7 +172,7 @@ pub fn generate_pawn_moves(board: &Board, list: &mut MoveList) { while a_side_capture_targets > 0 { let to = SQUARES[a_side_capture_targets.trailing_zeros() as usize]; - let from = to + 9; + let from = to + 9_u8; list.push(Move::new(from, to, MOVE_FLAG_CAPTURE)); a_side_capture_targets &= a_side_capture_targets - 1; } @@ -182,7 +182,7 @@ pub fn generate_pawn_moves(board: &Board, list: &mut MoveList) { while h_side_capture_targets > 0 { let to = SQUARES[h_side_capture_targets.trailing_zeros() as usize]; - let from = to + 7; + let from = to + 7_u8; list.push(Move::new(from, to, MOVE_FLAG_CAPTURE)); h_side_capture_targets &= h_side_capture_targets - 1; } @@ -193,7 +193,7 @@ pub fn generate_pawn_moves(board: &Board, list: &mut MoveList) { while promotion_targets > 0 { let to = SQUARES[promotion_targets.trailing_zeros() as usize]; - let from = to + 8; + let from = to + 8_u8; list.push(Move::new(from, to, MOVE_FLAG_PROMO_Q)); list.push(Move::new(from, to, MOVE_FLAG_PROMO_R)); list.push(Move::new(from, to, MOVE_FLAG_PROMO_B)); @@ -206,7 +206,7 @@ pub fn generate_pawn_moves(board: &Board, list: &mut MoveList) { let mut promotion_targets_a_side_capture = ((friendly_pawns & PAWN_A_SIDE_CAPTURE_PROMOTION_MASK_BLACK) >> 9) & opponent_occupied; while promotion_targets_a_side_capture > 0 { let to = SQUARES[promotion_targets_a_side_capture.trailing_zeros() as usize]; - let from = to + 9; + let from = to + 9_u8; list.push(Move::new(from, to, MOVE_FLAG_PROMO_N_CAP)); list.push(Move::new(from, to, MOVE_FLAG_PROMO_B_CAP)); list.push(Move::new(from, to, MOVE_FLAG_PROMO_R_CAP)); @@ -218,7 +218,7 @@ pub fn generate_pawn_moves(board: &Board, list: &mut MoveList) { let mut promotion_targets_h_side_capture = ((friendly_pawns & PAWN_H_SIDE_CAPTURE_PROMOTION_MASK_BLACK) >> 7) & opponent_occupied; while promotion_targets_h_side_capture > 0 { let to = SQUARES[promotion_targets_h_side_capture.trailing_zeros() as usize]; - let from = to + 7; + let from = to + 7_u8; list.push(Move::new(from, to, MOVE_FLAG_PROMO_N_CAP)); list.push(Move::new(from, to, MOVE_FLAG_PROMO_B_CAP)); list.push(Move::new(from, to, MOVE_FLAG_PROMO_R_CAP)); @@ -235,14 +235,14 @@ pub fn generate_pawn_moves(board: &Board, list: &mut MoveList) { // 1. Check A-Side capture (>> 9, e.g., B4 -> A3) let attacker_mask_a_side = (en_passant_target_bb << 9) & PAWN_A_SIDE_CAPTURE_MASK_BLACK; if (attacker_mask_a_side & friendly_pawns) > 0 { - let from = en_passant_target_square + 9; + let from = en_passant_target_square + 9_u8; list.push(Move::new(from, en_passant_target_square, MOVE_FLAG_EN_PASSANT)); } // 2. Check H-Side capture (>> 7, e.g., G4 -> H3) let attacker_mask_h_side = (en_passant_target_bb << 7) & PAWN_H_SIDE_CAPTURE_MASK_BLACK; if (attacker_mask_h_side & friendly_pawns) > 0 { - let from = en_passant_target_square + 7; + let from = en_passant_target_square + 7_u8; list.push(Move::new(from, en_passant_target_square, MOVE_FLAG_EN_PASSANT)); } } diff --git a/src/square.rs b/src/square.rs index ee066da..675975f 100644 --- a/src/square.rs +++ b/src/square.rs @@ -81,6 +81,12 @@ pub const SQUARES: [Square; 64] = [ Square::A8, Square::B8, Square::C8, Square::D8, Square::E8, Square::F8, Square::G8, Square::H8, ]; +impl Square { + pub fn to_bitboard(&self) -> u64 { + 1_u64 << (*self as u8) + } +} + impl TryFrom for Square { type Error = &'static str; fn try_from(value: u8) -> Result { @@ -101,6 +107,16 @@ impl Add for Square { } } +impl Add for Square { + type Output = Self; + + fn add(self, rhs: i8) -> Self::Output { + let new_val = ((self as i8) + rhs) as u8; + new_val.try_into().expect("Square addition resulted in an invalid square") + } +} + + impl Sub for Square { type Output = Self;