diff options
Diffstat (limited to 'src/grossmeister/move_generation.rs')
-rw-r--r-- | src/grossmeister/move_generation.rs | 262 |
1 files changed, 0 insertions, 262 deletions
diff --git a/src/grossmeister/move_generation.rs b/src/grossmeister/move_generation.rs deleted file mode 100644 index ea40d18..0000000 --- a/src/grossmeister/move_generation.rs +++ /dev/null @@ -1,262 +0,0 @@ -use std::cmp::Ordering; - -use crate::{moves::{Move, MoveKind}, board::{color::Color, piece::Piece, CastlingSide}, bitboard::BitboardFns, square::Square}; - -use super::Grossmeister; - - -impl Grossmeister { - pub fn generate_pseudolegal_moves(&self) -> Vec<Move> { - let mut result = Vec::new(); - result.append(&mut self.generate_moves_core(true)); - result.append(&mut self.generate_moves_core(false)); - result - } - - fn generate_moves_core(&self, tactical_only: bool) -> Vec<Move> { - let color = self.board.color(); - let player_pieces = self.board.pieces_by_color(color); - let mut moves = Vec::with_capacity(256); - let empty = self.board.empty(); - - let targets = if tactical_only { - self.board.color_occupancy(color.flip()) ^ match color { - // Exclude opponent king because we can't capture it - Color::White => self.board.piece_sets[Piece::KingBlack as usize], - Color::Black => self.board.piece_sets[Piece::King as usize], - } - } else { - empty - }; - - let kind = if tactical_only { - MoveKind::Capture - } else { - MoveKind::Quiet - }; - - for (piece_id, piece) in player_pieces.iter().enumerate() { - for source in piece.serialize() { - let piece_type = Piece::from(piece_id); - let move_targets = match piece_type { - Piece::Bishop => self.attacks.bishop(self.board.occupancy, source), - Piece::Rook => self.attacks.rook(self.board.occupancy, source), - Piece::Queen => self.attacks.queen(self.board.occupancy, source), - Piece::Knight => self.attacks.knight[source as usize], - Piece::King => self.attacks.king[source as usize], - Piece::Pawn => if tactical_only { - self.attacks.pawn[color as usize][source as usize] - } else { - self.attacks.pawn_pushes[color as usize][source as usize] - }, - _ => panic!("Incorrect piece type"), - }; - - // Generalized attack/move pattern for all pieces - for target in (move_targets & targets).serialize() { - moves.push(Move { source, target, kind }); - - // Promotions - if piece_type == Piece::Pawn { - if target.rank() == 7 { - for promo_type in [Piece::Bishop, Piece::Knight, Piece::Rook, Piece::Queen] { - moves.push(Move { source, target, kind: MoveKind::Promotion(promo_type)}) - } - } else if target.rank() == 0 { - for promo_type in [Piece::BishopBlack, Piece::KnightBlack, Piece::RookBlack, Piece::QueenBlack] { - moves.push(Move { source, target, kind: MoveKind::Promotion(promo_type)}) - } - } - } - } - - // En Passant - if piece_type == Piece::Pawn && tactical_only { - for target in (move_targets & self.board.ep_bitboard()).serialize() { - moves.push(Move { source, target, kind: MoveKind::EnPassant }); - } - } - } - } - - if !tactical_only { - // Double Pushes - // Make sure no blocking piece is standing in front of the pawns - // that are potential double-push sources - let able_to_double_push_mask = match color { - Color::White => empty >> 8, - Color::Black => empty << 8, - }; - for source in (player_pieces[Piece::Pawn as usize] & able_to_double_push_mask).serialize() { - for target in (self.attacks.pawn_double_pushes[color as usize][source as usize] & empty).serialize() { - moves.push(Move { source, target, kind: MoveKind::DoublePush }); - }; - } - - // Castling - let king_home_position = match color { - Color::White => Square::E1, - Color::Black => Square::E8, - }; - if player_pieces[Piece::King as usize].bitscan() == king_home_position { - for rook_square in player_pieces[Piece::Rook as usize] - .serialize() - .iter() - .filter(|rook_square| rook_square.rank() == king_home_position.rank()) - { - match rook_square.file() { - 0 => { - let all_empty = [ - king_home_position.west_one(), - king_home_position.west_one().west_one(), - king_home_position.west_one().west_one().west_one(), - - ].iter().all(|square| empty & square.to_bitboard() > 0); - - let any_checks = [ - king_home_position, - king_home_position.west_one(), - king_home_position.west_one().west_one(), - ].iter().any(|square| self.board.is_square_attacked(*square, color.flip())); - - if all_empty && !any_checks && self.board.castling_rights[color as usize][CastlingSide::Queen as usize] { - moves.push(Move { - source: king_home_position, - target: king_home_position.west_one().west_one(), - kind: MoveKind::Castle, - }) - } - }, - 7 => { - let all_empty = [ - king_home_position.east_one(), - king_home_position.east_one().east_one(), - - ].iter().all(|square| empty & square.to_bitboard() > 0); - - let any_checks = [ - king_home_position, - king_home_position.east_one(), - king_home_position.east_one().east_one(), - ].iter().any(|square| self.board.is_square_attacked(*square, color.flip())); - - if all_empty && !any_checks && self.board.castling_rights[color as usize][CastlingSide::King as usize] { - moves.push(Move { - source: king_home_position, - target: king_home_position.east_one().east_one(), - kind: MoveKind::Castle, - }) - } - }, - _ => {}, - } - } - } - } - moves - } - - /// Evaluate move for move ordering, prioritizing efficient captures - /// where low-value pieces capture high-value pieces - fn eval_move(&self, m: Move) -> f32 { - if m.is_tactical() { - let [source_eval, target_eval] = [m.source, m.target] - .map(|sq| self.board.piece_by_square(sq)) - .map(|p| { - match p { - Some(p) => p.static_eval(), - None => 0., - } - }); - return 2. * target_eval - source_eval - } - 0.0 - } - - pub fn order_moves(&self, moves: Vec<Move>, killer_moves: Vec<Move>) -> Vec<Move> { - let mut moves_with_eval: Vec<(Move, f32)> = moves - .iter() - .map(|m| (*m, self.eval_move(*m))) - .collect(); - - moves_with_eval.sort_unstable_by(|(a, a_eval), (b, b_eval)| { - if *a_eval == 0.0 && *b_eval == 0.0 { - // Prioritize equal captures over non-captures - if a.is_tactical() && !b.is_tactical() { - return Ordering::Less - } - if b.is_tactical() && !a.is_tactical() { - return Ordering::Greater - } - } - a_eval.total_cmp(b_eval).reverse() - }); - - let mut ordered_moves: Vec<Move> = moves_with_eval.iter().map(|(m, _)| *m).collect(); - - // Insert killer moves after winning captures - let equal_capture_index = moves_with_eval - .iter() - .position(|(m, eval)| m.is_tactical() && *eval == 0.0) - .unwrap_or(0); - - for killer in killer_moves { - if let Some(index) = ordered_moves.iter().position(|m| *m == killer) { - let mov = ordered_moves.remove(index); - ordered_moves.insert(equal_capture_index, mov); - } - } - - if let Some(transposition) = self.transposition() { - ordered_moves.insert(0, transposition.mov); - } - - ordered_moves - } - -} - -#[cfg(test)] -mod tests { - use crate::board::{Board, io::IO}; - - use super::*; - - #[test] - fn generate_pseudolegal_moves_starting_position() { - let board = Board::new(); - let mut gm = Grossmeister::new(board); - let moves = gm.generate_pseudolegal_moves(); - gm.board.ply += 1; - let black_moves = gm.generate_pseudolegal_moves(); - - assert_eq!(moves.len(), 20); - assert_eq!(black_moves.len(), 20); - - for mov in moves { - mov.print(); - } - } - - - #[test] - fn moved_king_castle() { - let fen = String::from("4k2r/ppp1n3/8/4R1Pp/5P2/q1P5/P1P1BP2/1K1R4 b - - 2 22"); - let board = Board::from_FEN(fen); - let mut gm = Grossmeister::new(board); - gm.board.ply += 1; - - // Shuffle kings around, returning to the same position - gm.board.make_move(Move { source: Square::E8, target: Square::F8, kind: MoveKind::Quiet }); - gm.board.make_move(Move { source: Square::B1, target: Square::A1, kind: MoveKind::Quiet }); - gm.board.make_move(Move { source: Square::F8, target: Square::E8, kind: MoveKind::Quiet }); - gm.board.make_move(Move { source: Square::A1, target: Square::B1, kind: MoveKind::Quiet }); - gm.board.print(); - - let moves = gm.generate_pseudolegal_moves(); - - let castle = moves.iter().find(|m| **m == Move { source: Square::E8, target: Square::G8, kind: MoveKind::Castle }); - println!("{:?}", board.castling_rights); - assert!(castle.is_none(), "Castle should not be allowed after king has moved"); - } -} |