[board] Implement PawnMoveGenerator

It generates pawn moves with bitboard bit operations!
This commit is contained in:
Eryn Wells 2023-12-29 09:03:02 -08:00
parent 65a7bec5f9
commit 852b7a848f
4 changed files with 183 additions and 1 deletions

View file

@ -1,5 +1,6 @@
// Eryn Wells <eryn@erynwells.me>
mod move_generator;
mod pawn;
pub use move_generator::MoveGenerator;

View file

@ -2,12 +2,19 @@
use crate::Position;
use super::pawn::PawnMoveGenerator;
pub struct MoveGenerator<'a> {
pub(super) position: &'a Position,
pawn_move_generator: PawnMoveGenerator<'a>,
}
impl<'a> MoveGenerator<'a> {
pub fn new(position: &Position) -> MoveGenerator {
MoveGenerator { position }
MoveGenerator {
position,
pawn_move_generator: PawnMoveGenerator::new(position),
}
}
}

172
board/src/moves/pawn.rs Normal file
View file

@ -0,0 +1,172 @@
// Eryn Wells <eryn@erynwells.me>
use crate::bitboard::BitBoard;
use crate::piece::{Color, Piece};
use crate::Position;
pub(super) struct PawnMoveGenerator<'a> {
position: &'a Position,
}
impl<'a> PawnMoveGenerator<'a> {
pub(super) fn new(position: &Position) -> PawnMoveGenerator {
PawnMoveGenerator { position }
}
pub(super) fn pawn_moves(&self, color: Color) -> BitBoard {
match color {
Color::White => self.white_pawn_pushes() & self.white_pawn_attacks(),
Color::Black => self.black_pawn_pushes() & self.black_pawn_attacks(),
}
}
pub(super) fn pawn_attacks(&self, color: Color) -> BitBoard {
match color {
Color::White => self.white_pawn_attacks(),
Color::Black => self.black_pawn_attacks(),
}
}
pub(super) fn pawn_pushes(&self, color: Color) -> BitBoard {
match color {
Color::White => self.white_pawn_pushes(),
Color::Black => self.black_pawn_pushes(),
}
}
fn white_pawn_pushes(&self) -> BitBoard {
let empty_squares = self.position.empty_squares();
let bb = self.position.bitboard_for_piece(Piece::pawn(Color::White));
let legal_1square_pushes = bb.shift_north_one() & empty_squares;
let legal_2square_pushes =
(legal_1square_pushes & BitBoard::rank(2)).shift_north_one() & empty_squares;
legal_1square_pushes | legal_2square_pushes
}
fn white_pawn_attacks(&self) -> BitBoard {
let black_pieces = self.position.bitboard_for_color(Color::Black);
let bb = self.position.bitboard_for_piece(Piece::pawn(Color::White));
(bb.shift_north_east_one() | bb.shift_north_west_one()) & black_pieces
}
fn black_pawn_pushes(&self) -> BitBoard {
let empty_squares = self.position.empty_squares();
let bb = self.position.bitboard_for_piece(Piece::pawn(Color::Black));
let legal_1square_pushes = bb.shift_south_one() & empty_squares;
let legal_2square_pushes =
(legal_1square_pushes & BitBoard::rank(5)).shift_south_one() & empty_squares;
legal_1square_pushes | legal_2square_pushes
}
fn black_pawn_attacks(&self) -> BitBoard {
let white_pieces = self.position.bitboard_for_color(Color::White);
let bb = self.position.bitboard_for_piece(Piece::pawn(Color::Black));
(bb.shift_south_east_one() | bb.shift_south_west_one()) & white_pieces
}
// TODO: En passant
}
#[cfg(test)]
mod tests {
use super::*;
use crate::position::DiagramFormatter;
use crate::{Position, Square};
#[test]
fn one_2square_push() {
let mut pos = Position::empty();
pos.place_piece(&Piece::pawn(Color::White), &Square::e2())
.expect("Failed to place pawn on e2");
let generator = PawnMoveGenerator::new(&pos);
let pawn_pushes = generator.pawn_pushes(Color::White);
let mut expected = BitBoard::empty();
expected.place_piece_at(&Square::e3());
expected.place_piece_at(&Square::e4());
assert_eq!(pawn_pushes, expected);
}
#[test]
fn one_1square_push() {
let mut pos = Position::empty();
pos.place_piece(&Piece::pawn(Color::White), &Square::e3())
.expect("Failed to place pawn on e3");
let generator = PawnMoveGenerator::new(&pos);
let pawn_pushes = generator.pawn_pushes(Color::White);
let mut expected = BitBoard::empty();
expected.place_piece_at(&Square::e4());
assert_eq!(pawn_pushes, expected);
}
#[test]
fn one_obstructed_2square_push() {
let mut pos = Position::empty();
pos.place_piece(&Piece::pawn(Color::White), &Square::e2())
.expect("Failed to place pawn on e2");
pos.place_piece(&Piece::knight(Color::White), &Square::e4())
.expect("Failed to place knight on e4");
println!("{}", DiagramFormatter::new(&pos));
let generator = PawnMoveGenerator::new(&pos);
let pawn_pushes = generator.white_pawn_pushes();
let mut expected = BitBoard::empty();
expected.place_piece_at(&Square::e3());
assert_eq!(pawn_pushes, expected);
}
#[test]
fn one_attack() {
let mut pos = Position::empty();
pos.place_piece(&Piece::pawn(Color::White), &Square::e4())
.expect("Failed to place pawn on e4");
pos.place_piece(&Piece::knight(Color::Black), &Square::d5())
.expect("Failed to place knight on d5");
println!("{}", DiagramFormatter::new(&pos));
let generator = PawnMoveGenerator::new(&pos);
let pawn_attacks = generator.white_pawn_attacks();
let mut expected = BitBoard::empty();
expected.place_piece_at(&Square::d5());
assert_eq!(pawn_attacks, expected);
}
#[test]
fn one_double_attack() {
let mut pos = Position::empty();
pos.place_piece(&Piece::pawn(Color::White), &Square::e4())
.expect("Failed to place pawn on e4");
pos.place_piece(&Piece::knight(Color::Black), &Square::d5())
.expect("Failed to place knight on d5");
pos.place_piece(&Piece::queen(Color::Black), &Square::f5())
.expect("Failed to place knight on f5");
println!("{}", DiagramFormatter::new(&pos));
let generator = PawnMoveGenerator::new(&pos);
let pawn_attacks = generator.white_pawn_attacks();
let mut expected = BitBoard::empty();
expected.place_piece_at(&Square::d5());
expected.place_piece_at(&Square::f5());
assert_eq!(pawn_attacks, expected);
}
}

View file

@ -14,7 +14,9 @@ macro_rules! sq_constructor {
}
impl Square {
sq_constructor!(d5);
sq_constructor!(e2);
sq_constructor!(e3);
sq_constructor!(e4);
sq_constructor!(f5);
}