[moves] Implement a move generator for pawns
I used Claude to help me figure this out. First time using AI for coding. It was actually rather helpful! Calculate BitBoards representing the various kinds of pawn moves when the move generator is created, and then iterate through them. En passant still isn't implemented here. This code has not been tested yet either.
This commit is contained in:
parent
af2bff348f
commit
574ab803dd
3 changed files with 193 additions and 0 deletions
12
moves/src/generators.rs
Normal file
12
moves/src/generators.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
mod pawn;
|
||||
|
||||
pub use pawn::PawnMoveGenerator;
|
||||
|
||||
use crate::Move;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct GeneratedMove {
|
||||
pub(crate) ply: Move,
|
||||
}
|
180
moves/src/generators/pawn.rs
Normal file
180
moves/src/generators/pawn.rs
Normal file
|
@ -0,0 +1,180 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
//! Implements a move generator for pawns.
|
||||
|
||||
use crate::Move;
|
||||
|
||||
use super::GeneratedMove;
|
||||
use chessfriend_bitboard::{bit_scanner::TrailingBitScanner, BitBoard};
|
||||
use chessfriend_board::Board;
|
||||
use chessfriend_core::{Color, Direction, Rank, Square};
|
||||
|
||||
pub struct PawnMoveGenerator {
|
||||
color: Color,
|
||||
single_pushes: BitBoard,
|
||||
double_pushes: BitBoard,
|
||||
left_captures: BitBoard,
|
||||
right_captures: BitBoard,
|
||||
en_passant: BitBoard,
|
||||
iterator: TrailingBitScanner,
|
||||
move_type: MoveType,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum MoveType {
|
||||
SinglePushes,
|
||||
DoublePushes,
|
||||
LeftCaptures,
|
||||
RightCaptures,
|
||||
EnPassant,
|
||||
}
|
||||
|
||||
impl PawnMoveGenerator {
|
||||
#[must_use]
|
||||
pub fn new(board: &Board, color: Option<Color>) -> Self {
|
||||
let color = board.unwrap_color(color);
|
||||
|
||||
let pawns = board.pawns(color);
|
||||
let occupied = board.occupancy();
|
||||
let empty = !occupied;
|
||||
let enemies = board.enemies(color);
|
||||
|
||||
let (single_pushes, double_pushes) = Self::pushes(pawns, color, empty);
|
||||
let (left_captures, right_captures) = Self::captures(pawns, color, enemies);
|
||||
let en_passant: BitBoard = board.en_passant_target.into();
|
||||
|
||||
Self {
|
||||
color,
|
||||
single_pushes,
|
||||
double_pushes,
|
||||
left_captures,
|
||||
right_captures,
|
||||
en_passant,
|
||||
iterator: single_pushes.occupied_squares_trailing(),
|
||||
move_type: MoveType::SinglePushes,
|
||||
}
|
||||
}
|
||||
|
||||
fn pushes(pawns: BitBoard, color: Color, empty: BitBoard) -> (BitBoard, BitBoard) {
|
||||
match color {
|
||||
Color::White => {
|
||||
const THIRD_RANK: BitBoard = BitBoard::rank(Rank::THREE);
|
||||
|
||||
let single_pushes = pawns.shift_north_one() & empty;
|
||||
let double_pushes = (single_pushes & THIRD_RANK).shift_north_one() & empty;
|
||||
|
||||
(single_pushes, double_pushes)
|
||||
}
|
||||
Color::Black => {
|
||||
const SIXTH_RANK: BitBoard = BitBoard::rank(Rank::SIX);
|
||||
|
||||
let single_pushes = pawns.shift_south_one() & empty;
|
||||
let double_pushes = (single_pushes & SIXTH_RANK).shift_south_one() & empty;
|
||||
|
||||
(single_pushes, double_pushes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn captures(pawns: BitBoard, color: Color, enemies: BitBoard) -> (BitBoard, BitBoard) {
|
||||
match color {
|
||||
Color::White => {
|
||||
let left_captures = pawns.shift_north_west_one() & enemies;
|
||||
let right_captures = pawns.shift_north_east_one() & enemies;
|
||||
(left_captures, right_captures)
|
||||
}
|
||||
Color::Black => {
|
||||
let left_captures = pawns.shift_south_east_one() & enemies;
|
||||
let right_captures = pawns.shift_south_west_one() & enemies;
|
||||
(left_captures, right_captures)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_origin_square(&self, target: Square) -> Option<Square> {
|
||||
match self.move_type {
|
||||
MoveType::SinglePushes => match self.color {
|
||||
Color::White => target.neighbor(Direction::South, None),
|
||||
Color::Black => target.neighbor(Direction::North, None),
|
||||
},
|
||||
MoveType::DoublePushes => match self.color {
|
||||
Color::White => target.neighbor(Direction::South, Some(2)),
|
||||
Color::Black => target.neighbor(Direction::North, Some(2)),
|
||||
},
|
||||
MoveType::LeftCaptures => match self.color {
|
||||
Color::White => target.neighbor(Direction::NorthWest, None),
|
||||
Color::Black => target.neighbor(Direction::SouthEast, None),
|
||||
},
|
||||
MoveType::RightCaptures => match self.color {
|
||||
Color::White => target.neighbor(Direction::NorthEast, None),
|
||||
Color::Black => target.neighbor(Direction::SouthWest, None),
|
||||
},
|
||||
MoveType::EnPassant => match self.color {
|
||||
Color::White => unimplemented!(),
|
||||
Color::Black => unimplemented!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn next_move_type(&mut self) -> Option<MoveType> {
|
||||
let next_move_type = self.move_type.next();
|
||||
|
||||
if let Some(next_move_type) = next_move_type {
|
||||
let next_bitboard = match next_move_type {
|
||||
MoveType::SinglePushes => self.single_pushes,
|
||||
MoveType::DoublePushes => self.double_pushes,
|
||||
MoveType::LeftCaptures => self.left_captures,
|
||||
MoveType::RightCaptures => self.right_captures,
|
||||
MoveType::EnPassant => self.en_passant,
|
||||
};
|
||||
|
||||
self.iterator = next_bitboard.occupied_squares_trailing();
|
||||
self.move_type = next_move_type;
|
||||
}
|
||||
|
||||
next_move_type
|
||||
}
|
||||
}
|
||||
|
||||
impl std::iter::Iterator for PawnMoveGenerator {
|
||||
type Item = GeneratedMove;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(target) = self.iterator.next() {
|
||||
let origin = self
|
||||
.calculate_origin_square(target)
|
||||
.expect("Bogus origin square");
|
||||
|
||||
match self.move_type {
|
||||
MoveType::SinglePushes => Some(GeneratedMove {
|
||||
ply: Move::quiet(origin, target),
|
||||
}),
|
||||
MoveType::DoublePushes => Some(GeneratedMove {
|
||||
ply: Move::double_push(origin, target),
|
||||
}),
|
||||
MoveType::LeftCaptures | MoveType::RightCaptures => Some(GeneratedMove {
|
||||
ply: Move::capture(origin, target),
|
||||
}),
|
||||
MoveType::EnPassant => Some(GeneratedMove {
|
||||
ply: Move::en_passant_capture(origin, target),
|
||||
}),
|
||||
}
|
||||
} else if self.next_move_type().is_some() {
|
||||
self.next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MoveType {
|
||||
fn next(self) -> Option<Self> {
|
||||
match self {
|
||||
MoveType::SinglePushes => Some(MoveType::DoublePushes),
|
||||
MoveType::DoublePushes => Some(MoveType::LeftCaptures),
|
||||
MoveType::LeftCaptures => Some(MoveType::RightCaptures),
|
||||
MoveType::RightCaptures => Some(MoveType::EnPassant),
|
||||
MoveType::EnPassant => None,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
pub mod generators;
|
||||
pub mod testing;
|
||||
|
||||
mod builder;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue