Files
JoshHeaps.Net/native/chess_engine/src/movegen.cpp
T
2026-06-08 13:58:22 -06:00

135 lines
4.3 KiB
C++

#include "movegen.h"
namespace chess {
namespace {
void add_promotions(MoveList& list, Square from, Square to) {
list.add(Move::make(from, to, PROMOTION, QUEEN));
list.add(Move::make(from, to, PROMOTION, ROOK));
list.add(Move::make(from, to, PROMOTION, BISHOP));
list.add(Move::make(from, to, PROMOTION, KNIGHT));
}
void generate_castling(const Position& pos, MoveList& list) {
if (pos.in_check()) return;
Color us = pos.side_to_move(), them = ~us;
Bitboard occ = pos.pieces();
auto attacked = [&](Square sq) { return (pos.attackers_to(sq) & pos.pieces(them)) != 0; };
if (us == WHITE) {
if (pos.can_castle(WHITE, KINGSIDE) &&
!(occ & (square_bb(F1) | square_bb(G1))) && !attacked(F1) && !attacked(G1))
list.add(Move::make(E1, G1, CASTLING));
if (pos.can_castle(WHITE, QUEENSIDE) &&
!(occ & (square_bb(B1) | square_bb(C1) | square_bb(D1))) && !attacked(D1) && !attacked(C1))
list.add(Move::make(E1, C1, CASTLING));
} else {
if (pos.can_castle(BLACK, KINGSIDE) &&
!(occ & (square_bb(F8) | square_bb(G8))) && !attacked(F8) && !attacked(G8))
list.add(Move::make(E8, G8, CASTLING));
if (pos.can_castle(BLACK, QUEENSIDE) &&
!(occ & (square_bb(B8) | square_bb(C8) | square_bb(D8))) && !attacked(D8) && !attacked(C8))
list.add(Move::make(E8, C8, CASTLING));
}
}
} // namespace
void generate_pseudo(const Position& pos, MoveList& list) {
Color us = pos.side_to_move(), them = ~us;
Bitboard occ = pos.pieces();
Bitboard targets = ~pos.pieces(us); // empty squares or enemy pieces
Bitboard theirs = pos.pieces(them);
// Pawns
int push = (us == WHITE) ? 8 : -8;
Rank promoRank = (us == WHITE) ? RANK_8 : RANK_1;
Rank startRank = (us == WHITE) ? RANK_2 : RANK_7;
Bitboard b = pos.pieces(us, PAWN);
while (b) {
Square s = pop_lsb(b);
Square t = Square(int(s) + push);
if (!(occ & square_bb(t))) {
if (rank_of(t) == promoRank) {
add_promotions(list, s, t);
} else {
list.add(Move::make(s, t));
if (rank_of(s) == startRank) {
Square t2 = Square(int(t) + push);
if (!(occ & square_bb(t2))) list.add(Move::make(s, t2));
}
}
}
Bitboard caps = PawnAttacks[us][s] & theirs;
while (caps) {
Square c = pop_lsb(caps);
if (rank_of(c) == promoRank) add_promotions(list, s, c);
else list.add(Move::make(s, c));
}
if (pos.ep_square() != SQ_NONE && (PawnAttacks[us][s] & square_bb(pos.ep_square())))
list.add(Move::make(s, pos.ep_square(), EN_PASSANT));
}
// Knights
b = pos.pieces(us, KNIGHT);
while (b) {
Square s = pop_lsb(b);
Bitboard a = KnightAttacks[s] & targets;
while (a) list.add(Move::make(s, pop_lsb(a)));
}
// Bishops
b = pos.pieces(us, BISHOP);
while (b) {
Square s = pop_lsb(b);
Bitboard a = bishop_attacks(s, occ) & targets;
while (a) list.add(Move::make(s, pop_lsb(a)));
}
// Rooks
b = pos.pieces(us, ROOK);
while (b) {
Square s = pop_lsb(b);
Bitboard a = rook_attacks(s, occ) & targets;
while (a) list.add(Move::make(s, pop_lsb(a)));
}
// Queens
b = pos.pieces(us, QUEEN);
while (b) {
Square s = pop_lsb(b);
Bitboard a = queen_attacks(s, occ) & targets;
while (a) list.add(Move::make(s, pop_lsb(a)));
}
// King (non-castling)
{
Square s = pos.king_square(us);
Bitboard a = KingAttacks[s] & targets;
while (a) list.add(Move::make(s, pop_lsb(a)));
}
}
void Position::generate_legal(MoveList& list) {
list.count = 0;
MoveList pseudo;
generate_pseudo(*this, pseudo);
Color us = sideToMove;
for (Move m : pseudo) {
do_move(m);
// After do_move, sideToMove is the opponent; the move is legal iff the
// side that just moved did not leave its own king attacked.
bool legal = (attackers_to(king_square(us)) & pieces(sideToMove)) == 0;
undo_move(m);
if (legal) list.add(m);
}
generate_castling(*this, list); // already fully legal
}
} // namespace chess