use crate::get_king_attacks;
use crate::get_knight_attacks;
use crate::get_pawn_attacks;
use crate::{DESTINATION, KING_SIDE, MEDIUM, PRESENCE, QUEEN_SIDE, SOURCE};
use crate::{get_between, get_bishop_rays, get_rook_rays};
use crate::{get_bishop_attacks, get_rook_attacks};
use crate::{BitBoard, Board, Call_Handler, Enumerate_Moves, Move, MoveList, MoveType, Square};
pub struct QuietMoves {}
pub struct TacticalMoves {}
pub struct AllMoves {}
pub trait MoveFilter {
const QUIETS: bool;
const TACTICALS: bool;
}
impl MoveFilter for QuietMoves {
const QUIETS: bool = true;
const TACTICALS: bool = false;
}
impl MoveFilter for TacticalMoves {
const QUIETS: bool = false;
const TACTICALS: bool = true;
}
impl MoveFilter for AllMoves {
const QUIETS: bool = true;
const TACTICALS: bool = true;
}
#[inline(always)]
pub fn gen_moves<M: MoveFilter>(board: &Board) -> MoveList {
let mut move_list: MoveList = MoveList::default();
enumerate_legal_moves::<M, _>(board, |mv| -> bool {
move_list.push(mv);
true
});
move_list
}
#[inline(always)]
pub fn enumerate_legal_moves<M, F>(board: &Board, mut handler: F) -> bool
where
M: MoveFilter,
F: FnMut(Move) -> bool,
{
let (diagonal_pins, linear_pins) = pinners(board);
match board.checkers.count_bits() {
0 => {
Enumerate_Moves!(false, board, diagonal_pins, linear_pins, handler);
if M::QUIETS {
enumerate_castling_moves(board, &mut handler);
}
}
1 => {
Enumerate_Moves!(true, board, diagonal_pins, linear_pins, handler);
}
_ => {}
}
enumerate_king_moves::<M, F>(
board,
board.allied_king().to_square().unwrap(),
&mut handler,
);
true
}
#[inline(always)]
fn enumerate_pawn_normal_moves<const IN_CHECK: bool, M, F>(
board: &Board,
src: BitBoard,
diagonal_pins: BitBoard,
linear_pins: BitBoard,
handler: &mut F,
) -> bool
where
M: MoveFilter,
F: FnMut(Move) -> bool,
{
const RANK_7: [BitBoard; 2] = [BitBoard::RANK_7, BitBoard::RANK_2];
const RANK_3: [BitBoard; 2] = [BitBoard::RANK_3, BitBoard::RANK_6];
if M::QUIETS {
let pawns: BitBoard = src & !RANK_7[board.side as usize] & !diagonal_pins;
let mut single_push: BitBoard = ((pawns & !linear_pins).forward(board.side)
| ((pawns & linear_pins).forward(board.side) & linear_pins))
& !board.combined_bitboard();
let mut double_push: BitBoard = (single_push & RANK_3[board.side as usize])
.forward(board.side)
& !board.combined_bitboard();
if IN_CHECK {
single_push &= check_mask::<IN_CHECK>(board);
double_push &= check_mask::<IN_CHECK>(board);
}
for dest in single_push {
let src: Square = dest.backward(board.side);
Call_Handler!(handler, src, dest, Quiet);
}
for dest in double_push {
let src: Square = (dest.backward(board.side)).backward(board.side);
Call_Handler!(handler, src, dest, DoublePawn);
}
}
if M::TACTICALS {
let pawns: BitBoard = src & !RANK_7[board.side as usize] & !linear_pins;
let mut capture_left: BitBoard = ((pawns & !diagonal_pins).up_left(board.side)
| ((pawns & diagonal_pins).up_left(board.side) & diagonal_pins))
& board.enemy_presence();
let mut capture_right: BitBoard = ((pawns & !diagonal_pins).up_right(board.side)
| ((pawns & diagonal_pins).up_right(board.side) & diagonal_pins))
& board.enemy_presence();
if IN_CHECK {
capture_left &= check_mask::<IN_CHECK>(board);
capture_right &= check_mask::<IN_CHECK>(board);
}
for dest in capture_left {
let src: Square = dest.backward(board.side).right_color(board.side);
Call_Handler!(handler, src, dest, Capture);
}
for dest in capture_right {
let src: Square = dest.backward(board.side).left_color(board.side);
Call_Handler!(handler, src, dest, Capture);
}
}
true
}
#[inline(always)]
fn enumerate_pawn_promotion_moves<const IN_CHECK: bool, M, F>(
board: &Board,
src: BitBoard,
diagonal_pins: BitBoard,
linear_pins: BitBoard,
handler: &mut F,
) -> bool
where
M: MoveFilter,
F: FnMut(Move) -> bool,
{
const RANK_7: [BitBoard; 2] = [BitBoard::RANK_7, BitBoard::RANK_2];
let pawns_to_promote: BitBoard = src & RANK_7[board.side as usize];
if pawns_to_promote.0 != 0 {
{
let pawns: BitBoard = pawns_to_promote & !linear_pins;
let mut capture_left_prom: BitBoard = ((pawns & !diagonal_pins).up_left(board.side)
| ((pawns & diagonal_pins).up_left(board.side) & diagonal_pins))
& board.enemy_presence();
let mut capture_right_prom: BitBoard = ((pawns & !diagonal_pins).up_right(board.side)
| ((pawns & diagonal_pins).up_right(board.side) & diagonal_pins))
& board.enemy_presence();
if IN_CHECK {
capture_left_prom &= check_mask::<IN_CHECK>(board);
capture_right_prom &= check_mask::<IN_CHECK>(board);
}
for dest in capture_left_prom {
let src: Square = dest.backward(board.side).right_color(board.side);
enumerate_promotions::<M, F>(src, dest, handler, true);
}
for dest in capture_right_prom {
let src: Square = dest.backward(board.side).left_color(board.side);
enumerate_promotions::<M, F>(src, dest, handler, true);
}
}
{
let pawns: BitBoard = pawns_to_promote & !diagonal_pins;
let mut quiet_promotions: BitBoard = ((pawns & !linear_pins).forward(board.side)
| ((pawns & linear_pins).forward(board.side) & linear_pins))
& !board.combined_bitboard();
if IN_CHECK {
quiet_promotions &= check_mask::<IN_CHECK>(board);
}
for dest in quiet_promotions {
let src: Square = dest.backward(board.side);
enumerate_promotions::<M, F>(src, dest, handler, false);
}
}
}
true
}
#[inline(always)]
fn enumerate_promotions<M, F>(src: Square, dest: Square, handler: &mut F, capture: bool) -> bool
where
M: MoveFilter,
F: FnMut(Move) -> bool,
{
macro_rules! Call_Promotion {
($promo_type:ident, $cap_type:ident) => {
if capture {
Call_Handler!(handler, src, dest, $cap_type);
} else {
Call_Handler!(handler, src, dest, $promo_type);
}
};
}
if M::TACTICALS {
Call_Promotion!(PromotionQueen, CapPromoQueen);
}
if M::QUIETS {
Call_Promotion!(PromotionRook, CapPromoRook);
Call_Promotion!(PromotionBishop, CapPromoBishop);
Call_Promotion!(PromotionKnight, CapPromoKnight);
}
true
}
#[inline(always)]
fn enumerate_pawn_en_passant_moves<F>(
board: &Board,
src: BitBoard,
linear_pins: BitBoard,
handler: &mut F,
) -> bool
where
F: FnMut(Move) -> bool,
{
let pawns: BitBoard = src & !linear_pins;
let king_square: Square = board.allied_king().to_square().unwrap();
if let Some(en_passant) = board.enpassant_square {
let dest: Square = en_passant;
let victim: Square = en_passant.forward(!board.side);
for src in pawns & get_pawn_attacks(!board.side, dest) {
let blockers: BitBoard =
board.combined_bitboard() ^ victim.to_bitboard() ^ src.to_bitboard()
| dest.to_bitboard();
let king_ray: bool =
!(get_rook_rays(king_square) & board.enemy_queen_rooks()).is_empty();
if king_ray
&& !(get_rook_attacks(king_square, blockers) & board.enemy_queen_rooks()).is_empty()
{
continue;
}
let king_ray: bool =
!(get_bishop_rays(king_square) & board.enemy_queen_bishops()).is_empty();
if king_ray
&& !(get_bishop_attacks(king_square, blockers) & board.enemy_queen_bishops())
.is_empty()
{
continue;
}
Call_Handler!(handler, src, dest, EnPassant);
}
}
true
}
#[inline(always)]
fn enumerate_pawn_moves<const IN_CHECK: bool, M, F>(
board: &Board,
src: BitBoard,
diagonal_pins: BitBoard,
linear_pins: BitBoard,
handler: &mut F,
) -> bool
where
M: MoveFilter,
F: FnMut(Move) -> bool,
{
enumerate_pawn_normal_moves::<IN_CHECK, M, F>(board, src, diagonal_pins, linear_pins, handler);
enumerate_pawn_promotion_moves::<IN_CHECK, M, F>(
board,
src,
diagonal_pins,
linear_pins,
handler,
);
if M::TACTICALS {
enumerate_pawn_en_passant_moves::<F>(board, src, linear_pins, handler);
}
true
}
#[inline(always)]
fn enumerate_castling_moves<F>(board: &Board, handler: &mut F) -> bool
where
F: FnMut(Move) -> bool,
{
if board.castling.has_kingside(board.side) {
let side: usize = board.side as usize;
let src: Square = SOURCE[side];
let dest: Square = DESTINATION[KING_SIDE][side];
if (board.combined_bitboard() & PRESENCE[KING_SIDE][side]).is_empty()
&& !board.attacked_square(MEDIUM[KING_SIDE][side], board.combined_bitboard())
&& !board.attacked_square(dest, board.combined_bitboard())
{
Call_Handler!(handler, src, dest, KingCastle);
}
}
if board.castling.has_queenside(board.side) {
let side: usize = board.side as usize;
let src: Square = SOURCE[side];
let dest: Square = DESTINATION[QUEEN_SIDE][side];
if (board.combined_bitboard() & PRESENCE[QUEEN_SIDE][side]).is_empty()
&& !board.attacked_square(MEDIUM[QUEEN_SIDE][side], board.combined_bitboard())
&& !board.attacked_square(dest, board.combined_bitboard())
{
Call_Handler!(handler, src, dest, QueenCastle);
}
}
true
}
#[inline(always)]
fn enumerate_king_moves<M, F>(board: &Board, src: Square, handler: &mut F) -> bool
where
M: MoveFilter,
F: FnMut(Move) -> bool,
{
let mut king: BitBoard = get_king_attacks(src) & !board.allied_presence();
let blockers: BitBoard = board.combined_bitboard().pop_square(src);
if !M::QUIETS {
king &= board.enemy_presence()
}
if !M::TACTICALS {
king &= !board.enemy_presence()
}
for dest in king {
if !board.attacked_square(dest, blockers) {
let is_capture: bool = (board.enemy_presence().0 & dest.to_bitboard().0) != 0;
let move_type: MoveType = if is_capture {
MoveType::Capture
} else {
MoveType::Quiet
};
handler(Move::new(src, dest, move_type));
}
}
true
}
#[inline(always)]
fn enumerate_knight_moves<const IN_CHECK: bool, M, F>(
board: &Board,
src: BitBoard,
diagonal_pins: BitBoard,
linear_pins: BitBoard,
handler: &mut F,
) -> bool
where
M: MoveFilter,
F: FnMut(Move) -> bool,
{
let knights: BitBoard = src & !(diagonal_pins | linear_pins);
for src in knights {
let mut attacks: BitBoard = get_knight_attacks(src) & !board.allied_presence();
if IN_CHECK {
attacks &= check_mask::<IN_CHECK>(board);
}
if !M::QUIETS {
attacks &= board.enemy_presence();
}
if !M::TACTICALS {
attacks &= !board.enemy_presence();
}
for dest in attacks {
let is_capture: bool = (board.enemy_presence().0 & dest.to_bitboard().0) != 0;
let move_type: MoveType = if is_capture {
MoveType::Capture
} else {
MoveType::Quiet
};
handler(Move::new(src, dest, move_type));
}
}
true
}
#[inline(always)]
fn enumerate_bishop_moves<const IN_CHECK: bool, M, F>(
board: &Board,
src: BitBoard,
diagonal_pins: BitBoard,
linear_pins: BitBoard,
handler: &mut F,
) -> bool
where
M: MoveFilter,
F: FnMut(Move) -> bool,
{
let bishops: BitBoard = src & !linear_pins & !diagonal_pins;
for src in bishops {
let mut attacks: BitBoard =
get_bishop_attacks(src, board.combined_bitboard()) & !board.allied_presence();
if IN_CHECK {
attacks &= check_mask::<IN_CHECK>(board);
}
if !M::QUIETS {
attacks &= board.enemy_presence();
}
if !M::TACTICALS {
attacks &= !board.enemy_presence();
}
for dest in attacks {
let is_capture: bool = (board.enemy_presence().0 & dest.to_bitboard().0) != 0;
let move_type: MoveType = if is_capture {
MoveType::Capture
} else {
MoveType::Quiet
};
handler(Move::new(src, dest, move_type));
}
}
let bishops: BitBoard = src & !linear_pins & diagonal_pins;
for src in bishops {
let mut attacks: BitBoard = get_bishop_attacks(src, board.combined_bitboard())
& !board.allied_presence()
& diagonal_pins;
if IN_CHECK {
attacks &= check_mask::<IN_CHECK>(board);
}
if !M::QUIETS {
attacks &= board.enemy_presence();
}
if !M::TACTICALS {
attacks &= !board.enemy_presence();
}
for dest in attacks {
let is_capture: bool = (board.enemy_presence().0 & dest.to_bitboard().0) != 0;
let move_type: MoveType = if is_capture {
MoveType::Capture
} else {
MoveType::Quiet
};
handler(Move::new(src, dest, move_type));
}
}
true
}
#[inline(always)]
fn enumerate_rook_moves<const IN_CHECK: bool, M, F>(
board: &Board,
src: BitBoard,
diagonal_pins: BitBoard,
linear_pins: BitBoard,
handler: &mut F,
) -> bool
where
M: MoveFilter,
F: FnMut(Move) -> bool,
{
let rooks: BitBoard = src & !diagonal_pins & !linear_pins;
for src in rooks {
let mut attacks: BitBoard =
get_rook_attacks(src, board.combined_bitboard()) & !board.allied_presence();
if IN_CHECK {
attacks &= check_mask::<IN_CHECK>(board);
}
if !M::QUIETS {
attacks &= board.enemy_presence();
}
if !M::TACTICALS {
attacks &= !board.enemy_presence();
}
for dest in attacks {
let is_capture: bool = (board.enemy_presence().0 & dest.to_bitboard().0) != 0;
let move_type: MoveType = if is_capture {
MoveType::Capture
} else {
MoveType::Quiet
};
handler(Move::new(src, dest, move_type));
}
}
let rooks: BitBoard = src & !diagonal_pins & linear_pins;
for src in rooks {
let mut attacks: BitBoard = get_rook_attacks(src, board.combined_bitboard())
& !board.allied_presence()
& linear_pins;
if IN_CHECK {
attacks &= check_mask::<IN_CHECK>(board);
}
if !M::QUIETS {
attacks &= board.enemy_presence();
}
if !M::TACTICALS {
attacks &= !board.enemy_presence();
}
for dest in attacks {
let is_capture: bool = (board.enemy_presence().0 & dest.to_bitboard().0) != 0;
let move_type: MoveType = if is_capture {
MoveType::Capture
} else {
MoveType::Quiet
};
handler(Move::new(src, dest, move_type));
}
}
true
}
#[inline(always)]
fn pinners(board: &Board) -> (BitBoard, BitBoard) {
let king_square: Square = board.allied_king().to_square().unwrap();
let blockers_mask: BitBoard = board.combined_bitboard();
let probe: BitBoard = (get_bishop_rays(king_square) | get_rook_rays(king_square))
& (board.enemy_queen_bishops() | board.enemy_queen_rooks());
if probe.is_empty() {
return (BitBoard::EMPTY, BitBoard::EMPTY);
}
let diagonal_pinned: BitBoard =
get_bishop_attacks(king_square, blockers_mask) & board.allied_presence();
let linnear_pinned: BitBoard =
get_rook_attacks(king_square, blockers_mask) & board.allied_presence();
let diagonal_pinned_removed: BitBoard = blockers_mask & !diagonal_pinned;
let linear_pinned_removed: BitBoard = blockers_mask & !linnear_pinned;
let diagonal_attackers: BitBoard =
get_bishop_attacks(king_square, diagonal_pinned_removed) & board.enemy_queen_bishops();
let linear_attackers: BitBoard =
get_rook_attacks(king_square, linear_pinned_removed) & board.enemy_queen_rooks();
let mut diagonal_pins: BitBoard = BitBoard::EMPTY;
for attacker in diagonal_attackers {
let pin: BitBoard = get_between(king_square, attacker);
diagonal_pins |= pin;
}
let mut linear_pins: BitBoard = BitBoard::EMPTY;
for attacker in linear_attackers {
let pin: BitBoard = get_between(king_square, attacker);
linear_pins |= pin;
}
(diagonal_pins, linear_pins)
}
#[inline(always)]
fn check_mask<const IN_CHECK: bool>(board: &Board) -> BitBoard {
if IN_CHECK {
get_between(
board.allied_king().to_square().unwrap(),
board.checkers.to_square().unwrap(),
) | board.checkers
} else {
BitBoard::FULL
}
}