24#include <beman/inplace_vector/inplace_vector.hpp>
63using MoveList = beman::inplace_vector::inplace_vector<Move, MAX_MOVES>;
70template <
bool CapturesOnly = false>
73 std::output_iterator<Move>
auto outputIt);
81template <
bool CapturesOnly = false>
91template <
bool CapturesOnly = false>
94 std::output_iterator<Move>
auto outputIt);
103template <
bool CapturesOnly = false>
139 namespace stdv = std::views;
143 inline constexpr auto PROMOTION_MASK = rank_masks::ONE | rank_masks::EIGHT;
144 inline constexpr auto NOT_PROMOTION_MASK = PROMOTION_MASK.inverse();
146 inline constexpr std::array possiblePromotedTypes {
147 PieceType::Knight, PieceType::Bishop, PieceType::Rook, PieceType::Queen
150 template <Color S
ide>
151 [[nodiscard, gnu::const]]
constexpr auto get_pawn_pushes(
152 const Position& position,
const Bitboard emptySquares)
154 using Pushes = beman::inplace_vector::inplace_vector<Move, 32uz>;
156 const auto allPushes = pseudo_legal::pawn_pushes<Side>(
160 auto nonPromotingPushes = (allPushes & NOT_PROMOTION_MASK).squares()
161 | stdv::transform([](
const Square target) {
165 .rank = prev_pawn_rank<Side>(target.rank) },
166 target, PieceType::Pawn
169 | std::ranges::to<Pushes>();
171 auto promotingPushes = possiblePromotedTypes
172 | stdv::transform([pushes = (allPushes & PROMOTION_MASK).squares()](
173 const PieceType promotedType) {
175 | stdv::transform([promotedType](
const Square target) {
179 .rank = prev_pawn_rank<Side>(target.rank) },
180 target, PieceType::Pawn, promotedType
185 | std::ranges::to<Pushes>();
187 std::array moveLists {
188 std::move(nonPromotingPushes), std::move(promotingPushes)
191 return stdv::join(std::move(moveLists))
192 | stdv::filter([&position](
const Move move) {
197 template <Color S
ide>
198 [[nodiscard, gnu::const]]
constexpr auto get_pawn_double_pushes(
199 const Position& position,
const Bitboard allOccupied)
201 static constexpr auto pawnStartingRank = Side == Color::White ? Rank::Two : Rank::Seven;
203 const auto pushes = pseudo_legal::pawn_double_pushes<Side>(
207 return pushes.squares()
208 | stdv::transform([](
const Square target) {
212 .rank = pawnStartingRank },
213 target, PieceType::Pawn
216 | stdv::filter([&position](
const Move move) {
221 [[nodiscard, gnu::const]]
constexpr auto get_pawn_captures_internal(
222 const Bitboard startingBoard,
223 const Bitboard targetBoard,
224 const std::optional<PieceType> promotedType = std::nullopt)
227 startingBoard.squares(), targetBoard.squares())
228 | stdv::transform([promotedType](
const auto& tuple) {
229 const auto [starting, target] = tuple;
231 return Move { starting, target, PieceType::Pawn, promotedType };
235 template <Color S
ide>
236 [[nodiscard, gnu::const]]
constexpr auto get_pawn_captures(
237 const Position& position)
243 const auto ourPawns = position.
pieces_for<Side>().pawns;
244 const auto enemyPieces = position.
pieces_for<pieces::other_side<Side>()>().occupied;
246 const auto eastCaptures = shifts::pawn_capture_east<Side>(ourPawns) & enemyPieces;
247 const auto westCaptures = shifts::pawn_capture_west<Side>(ourPawns) & enemyPieces;
249 const auto eastPromotionCaptures = eastCaptures & PROMOTION_MASK;
250 const auto westPromotionCaptures = westCaptures & PROMOTION_MASK;
252 const auto eastRegCaptures = eastCaptures & NOT_PROMOTION_MASK;
253 const auto westRegCaptures = westCaptures & NOT_PROMOTION_MASK;
256 const auto canCapturePromoteEast = shifts::pawn_inv_capture_east<Side>(eastPromotionCaptures);
257 const auto canCapturePromoteWest = shifts::pawn_inv_capture_west<Side>(westPromotionCaptures);
259 const auto canRegCaptureEast = shifts::pawn_inv_capture_east<Side>(eastRegCaptures);
260 const auto canRegCaptureWest = shifts::pawn_inv_capture_west<Side>(westRegCaptures);
263 using PawnCaptures = beman::inplace_vector::inplace_vector<Move, 64uz>;
265 auto get_non_promoting_captures = [](
const Bitboard startingBoard,
const Bitboard targetBoard) {
266 return get_pawn_captures_internal(startingBoard, targetBoard)
267 | std::ranges::to<PawnCaptures>();
270 auto get_promotion_captures = [](
const Bitboard startingBoard,
const Bitboard targetBoard) {
271 return possiblePromotedTypes
272 | stdv::transform([startingBoard, targetBoard](
const PieceType type) {
273 return get_pawn_captures_internal(startingBoard, targetBoard, type);
276 | std::ranges::to<PawnCaptures>();
279 std::array moveLists {
280 get_non_promoting_captures(canRegCaptureEast, eastRegCaptures),
281 get_non_promoting_captures(canRegCaptureWest, westRegCaptures),
282 get_promotion_captures(canCapturePromoteEast, eastPromotionCaptures),
283 get_promotion_captures(canCapturePromoteWest, westPromotionCaptures)
286 return stdv::join(std::move(moveLists))
287 | stdv::filter([&position](
const Move move) {
292 template <Color S
ide>
293 [[nodiscard, gnu::const]]
constexpr auto get_en_passant(
294 const Position& position)
297 using EPMoves = beman::inplace_vector::inplace_vector<Move, 2uz>;
300 .transform([&position](
const Square targetSquare) {
301 const auto targetSquareBoard = Bitboard::from_square(targetSquare);
303 const auto startSquares = shifts::pawn_inv_capture_east<Side>(targetSquareBoard)
304 | shifts::pawn_inv_capture_west<Side>(targetSquareBoard);
306 return (position.
pieces_for<Side>().pawns & startSquares).squares()
307 | stdv::transform([targetSquare](
const Square square) {
309 square, targetSquare, PieceType::Pawn
312 | stdv::filter([&position](
const Move move) {
315 | std::ranges::to<EPMoves>();
317 .value_or(EPMoves { });
320 template <Color S
ide,
bool CapturesOnly>
321 constexpr void add_all_pawn_moves(
322 const Position& position,
const Bitboard allOccupied,
323 std::output_iterator<Move>
auto outputIt)
325 if constexpr (not CapturesOnly) {
327 get_pawn_pushes<Side>(position, allOccupied.inverse()),
331 get_pawn_double_pushes<Side>(position, allOccupied),
336 get_pawn_captures<Side>(position),
340 get_en_passant<Side>(position),
344 template <Color S
ide,
bool CapturesOnly>
345 [[nodiscard, gnu::const]]
constexpr auto get_knight_moves(
346 const Position& position)
348 const auto& ourPieces = position.
pieces_for<Side>();
350 return ourPieces.knights.subboards()
351 | stdv::transform([ourOccupied = ourPieces.occupied,
352 theirOccupied = position.
pieces_for<pieces::other_side<Side>()>().occupied](
353 const Bitboard knightPos) {
354 auto knightMoves = pseudo_legal::knight(knightPos, ourOccupied);
356 if constexpr (CapturesOnly) {
357 knightMoves &= theirOccupied;
360 return knightMoves.squares()
361 | stdv::transform([knightPos](
const Square targetSquare) {
363 Square::from_index(knightPos.first()),
364 targetSquare, PieceType::Knight
369 | stdv::filter([&position](
const Move move) {
374 template <Color S
ide,
bool CapturesOnly>
375 [[nodiscard, gnu::const]]
auto get_bishop_moves(
377 const Bitboard occupiedSquares)
379 const auto& ourPieces = position.
pieces_for<Side>();
381 return ourPieces.bishops.squares()
382 | stdv::transform([occupiedSquares,
383 ourOccupied = ourPieces.occupied,
384 theirOccupied = position.
pieces_for<pieces::other_side<Side>()>().occupied](
385 const Square bishopPos) {
386 auto bishopMoves = magics::bishop(bishopPos, occupiedSquares, ourOccupied);
388 if constexpr (CapturesOnly) {
389 bishopMoves &= theirOccupied;
392 return bishopMoves.squares()
393 | stdv::transform([bishopPos](
const Square targetSquare) {
395 bishopPos, targetSquare, PieceType::Bishop
400 | stdv::filter([&position](
const Move move) {
405 template <Color S
ide,
bool CapturesOnly>
406 [[nodiscard, gnu::const]]
auto get_rook_moves(
408 const Bitboard occupiedSquares)
410 const auto& ourPieces = position.
pieces_for<Side>();
412 return ourPieces.rooks.squares()
413 | stdv::transform([occupiedSquares,
414 ourOccupied = ourPieces.occupied,
415 theirOccupied = position.
pieces_for<pieces::other_side<Side>()>().occupied](
416 const Square rookPos) {
417 auto rookMoves = magics::rook(rookPos, occupiedSquares, ourOccupied);
419 if constexpr (CapturesOnly) {
420 rookMoves &= theirOccupied;
423 return rookMoves.squares()
424 | stdv::transform([rookPos](
const Square targetSquare) {
426 rookPos, targetSquare, PieceType::Rook
431 | stdv::filter([&position](
const Move move) {
436 template <Color S
ide,
bool CapturesOnly>
437 [[nodiscard, gnu::const]]
auto get_queen_moves(
439 const Bitboard occupiedSquares)
441 const auto& ourPieces = position.
pieces_for<Side>();
443 return ourPieces.queens.squares()
444 | stdv::transform([occupiedSquares,
445 ourOccupied = ourPieces.occupied,
446 theirOccupied = position.
pieces_for<pieces::other_side<Side>()>().occupied](
447 const Square queenPos) {
448 auto queenMoves = magics::queen(queenPos, occupiedSquares, ourOccupied);
450 if constexpr (CapturesOnly) {
451 queenMoves &= theirOccupied;
454 return queenMoves.squares()
455 | stdv::transform([queenPos](
const Square targetSquare) {
457 queenPos, targetSquare, PieceType::Queen
462 | stdv::filter([&position](
const Move move) {
467 template <Color S
ide,
bool CapturesOnly>
468 [[nodiscard, gnu::const]]
constexpr auto get_king_moves(
471 const auto& ourPieces = position.
pieces_for<Side>();
473 auto kingMoves = pseudo_legal::king(ourPieces.king, ourPieces.occupied);
475 if constexpr (CapturesOnly) {
476 kingMoves &= position.
pieces_for<pieces::other_side<Side>()>().occupied;
479 return kingMoves.squares()
480 | stdv::transform([kingSquare = ourPieces.get_king_location()](
481 const Square targetSquare) {
483 kingSquare, targetSquare, PieceType::King
486 | stdv::filter([&position](
const Move move) {
494 template <Color S
ide>
495 [[nodiscard, gnu::const]]
consteval auto kingside_castle_mask() noexcept -> Bitboard
497 static constexpr auto rank = board::back_rank_for(Side);
501 board.set(Square { .file = File::F, .rank =
rank });
502 board.set(Square { .file = File::G, .rank =
rank });
509 template <Color S
ide,
bool Occupied>
510 [[nodiscard, gnu::const]]
consteval auto queenside_castle_mask() noexcept -> Bitboard
512 static constexpr auto rank = board::back_rank_for(Side);
516 board.set(Square { .file = File::C, .rank =
rank });
517 board.set(Square { .file = File::D, .rank =
rank });
519 if constexpr (Occupied) {
520 board.set(Square { .file = File::B, .rank =
rank });
526 template <Color S
ide>
527 [[nodiscard, gnu::const]]
constexpr auto get_kingside_castling(
528 const Position& position,
const Bitboard allOccupied)
529 -> std::optional<Move>
534 static constexpr auto OppositeColor = pieces::other_side<Side>();
536 const auto& ourPieces = position.
pieces_for<Side>();
537 const auto& theirPieces = position.
pieces_for<OppositeColor>();
539 assert(ourPieces.rooks.test(Square { File::H, board::back_rank_for(Side) }));
541 static constexpr auto requiredSquares = kingside_castle_mask<Side>();
543 if ((requiredSquares & allOccupied).any()
544 or squares_attacked<OppositeColor>(theirPieces, requiredSquares, ourPieces.occupied)) {
556 template <Color S
ide>
557 [[nodiscard, gnu::const]]
constexpr auto get_queenside_castling(
558 const Position& position,
const Bitboard allOccupied)
559 -> std::optional<Move>
564 static constexpr auto OppositeColor = pieces::other_side<Side>();
566 const auto& ourPieces = position.
pieces_for<Side>();
567 const auto& theirPieces = position.
pieces_for<OppositeColor>();
569 assert(ourPieces.rooks.test(Square { File::A, board::back_rank_for(Side) }));
571 static constexpr auto occupiedMask = queenside_castle_mask<Side, true>();
572 static constexpr auto attackedMask = queenside_castle_mask<Side, false>();
574 if ((allOccupied & occupiedMask).any()
575 or squares_attacked<OppositeColor>(theirPieces, attackedMask, ourPieces.occupied)) {
587 template <Color S
ide>
588 [[nodiscard, gnu::const]]
constexpr auto get_castling(
589 const Position& position,
const Bitboard allOccupied)
591 using Moves = beman::inplace_vector::inplace_vector<Move, 2uz>;
597 beman::inplace_vector::inplace_vector<Move, 2uz> moves;
599 auto add_move = [&moves](
const Move move) {
600 moves.emplace_back(move);
601 return std::monostate { };
604 get_kingside_castling<Side>(position, allOccupied).transform(add_move);
605 get_queenside_castling<Side>(position, allOccupied).transform(add_move);
610 template <Color S
ide,
bool CapturesOnly>
611 void generate_internal(
613 std::output_iterator<Move>
auto outputIt)
615 const auto& ourPieces = position.
pieces_for<Side>();
616 const auto& theirPieces = position.
pieces_for<pieces::other_side<Side>()>();
618 const auto allOccupied = ourPieces.occupied | theirPieces.occupied;
620 add_all_pawn_moves<Side, CapturesOnly>(position, allOccupied, outputIt);
623 get_knight_moves<Side, CapturesOnly>(position),
627 get_bishop_moves<Side, CapturesOnly>(position, allOccupied),
631 get_rook_moves<Side, CapturesOnly>(position, allOccupied),
635 get_queen_moves<Side, CapturesOnly>(position, allOccupied),
639 get_king_moves<Side, CapturesOnly>(position),
642 if constexpr (not CapturesOnly) {
644 get_castling<Side>(position, allOccupied),
649 template <Color S
ide,
bool CapturesOnly>
650 void generate_for_internal(
651 const Position& position,
const PieceType piece,
652 std::output_iterator<Move>
auto outputIt)
654 const auto& ourPieces = position.
pieces_for<Side>();
655 const auto& theirPieces = position.
pieces_for<pieces::other_side<Side>()>();
657 const auto allOccupied = ourPieces.occupied | theirPieces.occupied;
660 case PieceType::Pawn: {
661 add_all_pawn_moves<Side, CapturesOnly>(position, allOccupied, outputIt);
665 case PieceType::Knight: {
667 get_knight_moves<Side, CapturesOnly>(position),
672 case PieceType::Bishop: {
674 get_bishop_moves<Side, CapturesOnly>(position, allOccupied),
679 case PieceType::Rook: {
681 get_rook_moves<Side, CapturesOnly>(position, allOccupied),
686 case PieceType::Queen: {
688 get_queen_moves<Side, CapturesOnly>(position, allOccupied),
693 default : [[fallthrough]];
694 case PieceType::King: {
696 get_king_moves<Side, CapturesOnly>(position),
699 if constexpr (not CapturesOnly) {
702 get_castling<Side>(position, allOccupied),
711 template <Color S
ide>
712 [[nodiscard]]
auto any_legal_moves_internal(
const Position& position) ->
bool
718 for (
const auto piece : { PieceType::King, PieceType::Pawn, PieceType::Knight, PieceType::Queen, PieceType::Rook, PieceType::Bishop }) {
719 generate_for_internal<Side, false>(position, piece, std::back_inserter(moves));
721 if (not moves.empty())
733template <
bool CapturesOnly>
736 std::output_iterator<Move>
auto outputIt)
739 detail::generate_internal<Color::White, CapturesOnly>(position, outputIt);
741 detail::generate_internal<Color::Black, CapturesOnly>(position, outputIt);
744template <
bool CapturesOnly>
754template <
bool CapturesOnly>
757 std::output_iterator<Move>
auto outputIt)
760 detail::generate_for_internal<Color::White, CapturesOnly>(position, piece, outputIt);
762 detail::generate_for_internal<Color::Black, CapturesOnly>(position, piece, outputIt);
765template <
bool CapturesOnly>
780 return detail::any_legal_moves_internal<Color::White>(position);
782 return detail::any_legal_moves_internal<Color::Black>(position);
constexpr auto prev_pawn_rank(Rank rank) noexcept -> Rank
constexpr auto rank(Bitboard starting) noexcept -> Bitboard
constexpr auto castle_queenside(const Color color) noexcept -> Move
constexpr auto castle_kingside(const Color color) noexcept -> Move
void generate_for(const Position &position, PieceType piece, std::output_iterator< Move > auto outputIt)
auto any_legal_moves(const Position &position) -> bool
void generate(const Position &position, std::output_iterator< Move > auto outputIt)
beman::inplace_vector::inplace_vector< Move, MAX_MOVES > MoveList
auto is_white_to_move() const noexcept -> bool
std::optional< Square > enPassantTargetSquare
auto is_legal(Move move) const -> bool
auto pieces_for() noexcept -> Pieces &
auto castling_rights_for() const noexcept -> CastlingRights
auto is_check() const noexcept -> bool
auto is_white_to_move() const noexcept -> bool