[position] Implement generating pawn moves by looking up bitboards in the Library

This enables a bunch of clean up! Remove the MoveGenerationParameters and MoveList
types from move_generator::pawn.

Implement BitBoard::pawn_pushes to look up pawn pushes by square and color.
This commit is contained in:
Eryn Wells 2024-01-28 10:25:01 -08:00
parent 77f419ad3b
commit ea74b214da
3 changed files with 48 additions and 72 deletions

View file

@ -44,6 +44,10 @@ impl BitBoard {
library().pawn_attacks(sq, color) library().pawn_attacks(sq, color)
} }
pub fn pawn_pushes(sq: Square, color: Color) -> BitBoard {
library().pawn_pushes(sq, color)
}
moves_getter!(knight_moves); moves_getter!(knight_moves);
moves_getter!(bishop_moves); moves_getter!(bishop_moves);
moves_getter!(rook_moves); moves_getter!(rook_moves);

View file

@ -58,6 +58,7 @@ pub(super) struct MoveLibrary {
// Piecewise move tables // Piecewise move tables
pawn_attacks: [[BitBoard; 64]; 2], pawn_attacks: [[BitBoard; 64]; 2],
pawn_pushes: [[BitBoard; 64]; 2],
knight_moves: [BitBoard; 64], knight_moves: [BitBoard; 64],
bishop_moves: [BitBoard; 64], bishop_moves: [BitBoard; 64],
rook_moves: [BitBoard; 64], rook_moves: [BitBoard; 64],
@ -70,6 +71,7 @@ impl MoveLibrary {
MoveLibrary { MoveLibrary {
rays: [[BitBoard::empty(); 8]; Square::NUM], rays: [[BitBoard::empty(); 8]; Square::NUM],
pawn_attacks: [[BitBoard::empty(); 64]; 2], pawn_attacks: [[BitBoard::empty(); 64]; 2],
pawn_pushes: [[BitBoard::empty(); 64]; 2],
knight_moves: [BitBoard::empty(); 64], knight_moves: [BitBoard::empty(); 64],
bishop_moves: [BitBoard::empty(); 64], bishop_moves: [BitBoard::empty(); 64],
rook_moves: [BitBoard::empty(); 64], rook_moves: [BitBoard::empty(); 64],
@ -169,10 +171,29 @@ impl MoveLibrary {
fn init_pawn_moves(&mut self, sq: Square) { fn init_pawn_moves(&mut self, sq: Square) {
let bitboard: BitBoard = sq.into(); let bitboard: BitBoard = sq.into();
self.pawn_attacks[Color::White as usize][sq as usize] = self.pawn_attacks[Color::White as usize][sq as usize] =
bitboard.shift_north_west_one() | bitboard.shift_north_east_one(); bitboard.shift_north_west_one() | bitboard.shift_north_east_one();
self.pawn_attacks[Color::Black as usize][sq as usize] = self.pawn_attacks[Color::Black as usize][sq as usize] =
bitboard.shift_south_west_one() | bitboard.shift_south_east_one(); bitboard.shift_south_west_one() | bitboard.shift_south_east_one();
self.pawn_pushes[Color::White as usize][sq as usize] = {
let mut push = bitboard.shift_north_one();
if !(bitboard & RANKS[1]).is_empty() {
push |= push.shift_north_one();
}
push
};
self.pawn_pushes[Color::Black as usize][sq as usize] = {
let mut push = bitboard.shift_south_one();
if !(bitboard & RANKS[6]).is_empty() {
push |= push.shift_south_one();
}
push
};
} }
#[inline] #[inline]
@ -192,6 +213,10 @@ impl MoveLibrary {
self.rays[sq as usize][dir as usize] self.rays[sq as usize][dir as usize]
} }
pub(super) fn pawn_pushes(&self, sq: Square, color: Color) -> BitBoard {
self.pawn_pushes[color as usize][sq as usize]
}
pub(super) fn pawn_attacks(&self, sq: Square, color: Color) -> BitBoard { pub(super) fn pawn_attacks(&self, sq: Square, color: Color) -> BitBoard {
self.pawn_attacks[color as usize][sq as usize] self.pawn_attacks[color as usize][sq as usize]
} }

View file

@ -1,27 +1,13 @@
// Eryn Wells <eryn@erynwells.me> // Eryn Wells <eryn@erynwells.me>
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet}; use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
use crate::{Move, MoveBuilder, Position}; use crate::{MoveBuilder, Position};
use chessfriend_bitboard::BitBoard; use chessfriend_bitboard::BitBoard;
use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square}; use chessfriend_core::{Color, Piece, PlacedPiece, Square};
enum MoveList {
Quiet = 0,
Promotions = 1,
Captures = 2,
}
#[derive(Debug)] #[derive(Debug)]
struct MoveIterator(usize, usize); struct MoveIterator(usize, usize);
struct MoveGenerationParameters {
starting_rank: BitBoard,
promotion_rank: BitBoard,
push_shift: fn(&BitBoard) -> BitBoard,
left_capture_shift: fn(&BitBoard) -> BitBoard,
right_capture_shift: fn(&BitBoard) -> BitBoard,
}
move_generator_declaration!(PawnMoveGenerator); move_generator_declaration!(PawnMoveGenerator);
impl MoveGeneratorInternal for PawnMoveGenerator { impl MoveGeneratorInternal for PawnMoveGenerator {
@ -31,12 +17,9 @@ impl MoveGeneratorInternal for PawnMoveGenerator {
fn move_set_for_piece(position: &Position, placed_piece: PlacedPiece) -> MoveSet { fn move_set_for_piece(position: &Position, placed_piece: PlacedPiece) -> MoveSet {
let from_square = placed_piece.square(); let from_square = placed_piece.square();
let parameters = Self::move_generation_parameters(placed_piece.color());
let opposing_pieces = position.opposing_pieces(); let captures_bitboard = Self::attacks(position, placed_piece);
let quiet_moves_bitboard = Self::pushes(position, placed_piece);
let captures_bitboard = Self::attacks(position, placed_piece, &parameters);
let quiet_moves_bitboard = Self::pushes(position, placed_piece, &parameters);
let quiet_moves = quiet_moves_bitboard.occupied_squares().map(|to_square| { let quiet_moves = quiet_moves_bitboard.occupied_squares().map(|to_square| {
MoveBuilder::new(*placed_piece.piece(), from_square, to_square).build() MoveBuilder::new(*placed_piece.piece(), from_square, to_square).build()
@ -55,67 +38,34 @@ impl MoveGeneratorInternal for PawnMoveGenerator {
} }
impl PawnMoveGenerator { impl PawnMoveGenerator {
fn move_generation_parameters(color: Color) -> MoveGenerationParameters { fn pushes(position: &Position, piece: PlacedPiece) -> BitBoard {
match color {
Color::White => MoveGenerationParameters {
starting_rank: BitBoard::rank(1),
promotion_rank: BitBoard::rank(7),
push_shift: BitBoard::shift_north_one,
left_capture_shift: BitBoard::shift_north_west_one,
right_capture_shift: BitBoard::shift_north_east_one,
},
Color::Black => MoveGenerationParameters {
starting_rank: BitBoard::rank(6),
promotion_rank: BitBoard::rank(0),
push_shift: BitBoard::shift_south_one,
left_capture_shift: BitBoard::shift_south_east_one,
right_capture_shift: BitBoard::shift_south_west_one,
},
}
}
fn pushes(
position: &Position,
piece: PlacedPiece,
parameters: &MoveGenerationParameters,
) -> BitBoard {
let empty_squares = position.empty_squares(); let empty_squares = position.empty_squares();
let from_square: BitBoard = piece.square().into(); BitBoard::pawn_pushes(piece.square(), piece.color()) & empty_squares
(parameters.push_shift)(&from_square) & empty_squares
} }
fn attacks( fn attacks(position: &Position, piece: PlacedPiece) -> BitBoard {
position: &Position,
piece: PlacedPiece,
parameters: &MoveGenerationParameters,
) -> BitBoard {
let color = piece.color(); let color = piece.color();
let opponent_pieces = position.bitboard_for_color(color.other()); let opponent_pieces = position.bitboard_for_color(color.other());
let en_passant_square = position let en_passant_bitboard = match position.en_passant_square() {
.en_passant_square() Some(square) => <Square as Into<BitBoard>>::into(square),
.map(|square| <Square as Into<BitBoard>>::into(square)) None => BitBoard::empty(),
.unwrap_or(BitBoard::empty()); };
let from_square: BitBoard = piece.square().into(); BitBoard::pawn_attacks(piece.square(), color) & (opponent_pieces | en_passant_bitboard)
((parameters.left_capture_shift)(&from_square)
| (parameters.right_capture_shift)(&from_square))
& (opponent_pieces | en_passant_square)
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{position, position::DiagramFormatter}; use crate::{position::DiagramFormatter, test_position, Move};
use chessfriend_core::{piece, Square}; use chessfriend_core::{piece, Square};
use std::collections::HashSet; use std::collections::HashSet;
#[test] #[test]
fn one_2square_push() { fn one_2square_push() {
let pos = position![White Pawn on E2]; let pos = test_position![White Pawn on E2];
let generator = PawnMoveGenerator::new(&pos, Color::White); let generator = PawnMoveGenerator::new(&pos, Color::White);
@ -131,7 +81,7 @@ mod tests {
#[test] #[test]
fn one_1square_push() { fn one_1square_push() {
let mut pos = position![White Pawn on E3]; let pos = test_position![White Pawn on E3];
let generator = PawnMoveGenerator::new(&pos, Color::White); let generator = PawnMoveGenerator::new(&pos, Color::White);
@ -149,7 +99,7 @@ mod tests {
#[test] #[test]
fn one_obstructed_2square_push() { fn one_obstructed_2square_push() {
let pos = position![ let pos = test_position![
White Pawn on E2, White Pawn on E2,
White Knight on E4, White Knight on E4,
]; ];
@ -172,11 +122,10 @@ mod tests {
#[test] #[test]
fn one_obstructed_1square_push() { fn one_obstructed_1square_push() {
let mut pos = position![ let pos = test_position![
White Pawn on E2, White Pawn on E2,
White Knight on E3, White Knight on E3,
]; ];
println!("{}", DiagramFormatter::new(&pos));
let generator = PawnMoveGenerator::new(&pos, Color::White); let generator = PawnMoveGenerator::new(&pos, Color::White);
@ -187,12 +136,11 @@ mod tests {
#[test] #[test]
fn one_attack() { fn one_attack() {
let pos = position![ let pos = test_position![
White Pawn on E4, White Pawn on E4,
White Bishop on E5, White Bishop on E5,
Black Knight on D5, Black Knight on D5,
]; ];
println!("{}", DiagramFormatter::new(&pos));
let generator = PawnMoveGenerator::new(&pos, Color::White); let generator = PawnMoveGenerator::new(&pos, Color::White);
@ -210,13 +158,12 @@ mod tests {
#[test] #[test]
fn one_double_attack() { fn one_double_attack() {
let pos = position![ let pos = test_position![
White Pawn on E4, White Pawn on E4,
White Bishop on E5, White Bishop on E5,
Black Knight on D5, Black Knight on D5,
Black Queen on F5, Black Queen on F5,
]; ];
println!("{}", DiagramFormatter::new(&pos));
let generator = PawnMoveGenerator::new(&pos, Color::White); let generator = PawnMoveGenerator::new(&pos, Color::White);