BenBot 1.7.5
A chess engine
Loading...
Searching...
No Matches
MoveGen.hpp
Go to the documentation of this file.
1/*
2 * ======================================================================================
3 *
4 * ░▒▓███████▓▒░░▒▓████████▓▒░▒▓███████▓▒░ ░▒▓███████▓▒░ ░▒▓██████▓▒░▒▓████████▓▒░
5 * ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
6 * ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
7 * ░▒▓███████▓▒░░▒▓██████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓███████▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
8 * ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
9 * ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
10 * ░▒▓███████▓▒░░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓███████▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░
11 *
12 * ======================================================================================
13 */
14
19
20#pragma once
21
22#include <algorithm>
23#include <array>
24#include <beman/inplace_vector/inplace_vector.hpp>
25#include <cassert>
26#include <iterator>
39#include <optional>
40#include <ranges>
41#include <utility>
42
43namespace chess::moves {
44
45using game::Position;
47
50
58inline constexpr auto MAX_MOVES = 256uz;
59
63using MoveList = beman::inplace_vector::inplace_vector<Move, MAX_MOVES>;
64
70template <bool CapturesOnly = false>
71void generate(
72 const Position& position,
73 std::output_iterator<Move> auto outputIt);
74
81template <bool CapturesOnly = false>
82[[nodiscard]] auto generate(const Position& position) -> MoveList;
83
91template <bool CapturesOnly = false>
92void generate_for(
93 const Position& position, PieceType piece,
94 std::output_iterator<Move> auto outputIt);
95
103template <bool CapturesOnly = false>
104[[nodiscard]] auto generate_for(
105 const Position& position, PieceType piece) -> MoveList;
106
108[[nodiscard]] auto any_legal_moves(const Position& position) -> bool;
109
111
112/*
113 ___ ,--,
114 ,---, ,--.'|_ ,--, ,--.'|
115 ,---.'| | | :,' ,--.'| | | :
116 | | : : : ' : | |, : : ' .--.--.
117 | | | ,---. .;__,' / ,--.--. `--'_ | ' | / / '
118 ,--.__| | / \ | | | / \ ,' ,'| ' | | | : /`./
119 / ,' | / / |:__,'| : .--. .-. | ' | | | | : | : ;_
120. ' / |. ' / | ' : |__ \__\/: . . | | : ' : |__ \ \ `.
121' ; |: |' ; /| | | '.'| ," .--.; | ' : |__ | | '.'| `----. \
122| | '/ '' | / | ; : ;/ / ,. | | | '.'|; : ;/ /`--' /__ ___ ___
123| : :|| : | | , /; : .' \; : ;| , /'--'. / .\/ .\/ .\
124 \ \ / \ \ / ---`-' | , .-./| , / ---`-' `--'---'\ ; \ ; \ ; |
125 `----' `----' `--`---' ---`-' `--" `--" `--"
126
127 */
128
129#ifndef DOXYGEN
130namespace detail {
131
132 using board::Bitboard;
133 using board::Rank;
134 using board::Square;
135 using pieces::Color;
136
138
139 namespace stdv = std::views;
140 namespace rank_masks = board::masks::ranks;
141 namespace shifts = board::shifts;
142
143 inline constexpr auto PROMOTION_MASK = rank_masks::ONE | rank_masks::EIGHT;
144 inline constexpr auto NOT_PROMOTION_MASK = PROMOTION_MASK.inverse();
145
146 inline constexpr std::array possiblePromotedTypes {
147 PieceType::Knight, PieceType::Bishop, PieceType::Rook, PieceType::Queen
148 };
149
150 template <Color Side>
151 [[nodiscard, gnu::const]] constexpr auto get_pawn_pushes(
152 const Position& position, const Bitboard emptySquares)
153 {
154 using Pushes = beman::inplace_vector::inplace_vector<Move, 32uz>;
155
156 const auto allPushes = pseudo_legal::pawn_pushes<Side>(
157 position.pieces_for<Side>().pawns,
158 emptySquares);
159
160 auto nonPromotingPushes = (allPushes & NOT_PROMOTION_MASK).squares()
161 | stdv::transform([](const Square target) {
162 return Move {
163 Square {
164 .file = target.file,
165 .rank = prev_pawn_rank<Side>(target.rank) },
166 target, PieceType::Pawn
167 };
168 })
169 | std::ranges::to<Pushes>();
170
171 auto promotingPushes = possiblePromotedTypes
172 | stdv::transform([pushes = (allPushes & PROMOTION_MASK).squares()](
173 const PieceType promotedType) {
174 return pushes
175 | stdv::transform([promotedType](const Square target) {
176 return Move {
177 Square {
178 .file = target.file,
179 .rank = prev_pawn_rank<Side>(target.rank) },
180 target, PieceType::Pawn, promotedType
181 };
182 });
183 })
184 | stdv::join
185 | std::ranges::to<Pushes>();
186
187 std::array moveLists {
188 std::move(nonPromotingPushes), std::move(promotingPushes)
189 };
190
191 return stdv::join(std::move(moveLists))
192 | stdv::filter([&position](const Move move) {
193 return position.is_legal(move);
194 });
195 }
196
197 template <Color Side>
198 [[nodiscard, gnu::const]] constexpr auto get_pawn_double_pushes(
199 const Position& position, const Bitboard allOccupied)
200 {
201 static constexpr auto pawnStartingRank = Side == Color::White ? Rank::Two : Rank::Seven;
202
203 const auto pushes = pseudo_legal::pawn_double_pushes<Side>(
204 position.pieces_for<Side>().pawns,
205 allOccupied);
206
207 return pushes.squares()
208 | stdv::transform([](const Square target) {
209 return Move {
210 Square {
211 .file = target.file,
212 .rank = pawnStartingRank },
213 target, PieceType::Pawn
214 };
215 })
216 | stdv::filter([&position](const Move move) {
217 return position.is_legal(move);
218 });
219 }
220
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)
225 {
226 return stdv::zip(
227 startingBoard.squares(), targetBoard.squares())
228 | stdv::transform([promotedType](const auto& tuple) {
229 const auto [starting, target] = tuple;
230
231 return Move { starting, target, PieceType::Pawn, promotedType };
232 });
233 }
234
235 template <Color Side>
236 [[nodiscard, gnu::const]] constexpr auto get_pawn_captures(
237 const Position& position)
238 {
239 // We handle east & west captures separately to make set-wise operations easier.
240 // This way, there is always a 1-1 relationship between a target square and a
241 // starting square.
242
243 const auto ourPawns = position.pieces_for<Side>().pawns;
244 const auto enemyPieces = position.pieces_for<pieces::other_side<Side>()>().occupied;
245
246 const auto eastCaptures = shifts::pawn_capture_east<Side>(ourPawns) & enemyPieces;
247 const auto westCaptures = shifts::pawn_capture_west<Side>(ourPawns) & enemyPieces;
248
249 const auto eastPromotionCaptures = eastCaptures & PROMOTION_MASK;
250 const auto westPromotionCaptures = westCaptures & PROMOTION_MASK;
251
252 const auto eastRegCaptures = eastCaptures & NOT_PROMOTION_MASK;
253 const auto westRegCaptures = westCaptures & NOT_PROMOTION_MASK;
254
255 // starting positions of pawns that can make captures
256 const auto canCapturePromoteEast = shifts::pawn_inv_capture_east<Side>(eastPromotionCaptures);
257 const auto canCapturePromoteWest = shifts::pawn_inv_capture_west<Side>(westPromotionCaptures);
258
259 const auto canRegCaptureEast = shifts::pawn_inv_capture_east<Side>(eastRegCaptures);
260 const auto canRegCaptureWest = shifts::pawn_inv_capture_west<Side>(westRegCaptures);
261
262 // max number of possible pawn captures is 16 * 4 possible promoted types
263 using PawnCaptures = beman::inplace_vector::inplace_vector<Move, 64uz>;
264
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>();
268 };
269
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);
274 })
275 | stdv::join
276 | std::ranges::to<PawnCaptures>();
277 };
278
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)
284 };
285
286 return stdv::join(std::move(moveLists))
287 | stdv::filter([&position](const Move move) {
288 return position.is_legal(move);
289 });
290 }
291
292 template <Color Side>
293 [[nodiscard, gnu::const]] constexpr auto get_en_passant(
294 const Position& position)
295 {
296 // at most 2 captures are possible at a time
297 using EPMoves = beman::inplace_vector::inplace_vector<Move, 2uz>;
298
299 return position.enPassantTargetSquare
300 .transform([&position](const Square targetSquare) {
301 const auto targetSquareBoard = Bitboard::from_square(targetSquare);
302
303 const auto startSquares = shifts::pawn_inv_capture_east<Side>(targetSquareBoard)
304 | shifts::pawn_inv_capture_west<Side>(targetSquareBoard);
305
306 return (position.pieces_for<Side>().pawns & startSquares).squares()
307 | stdv::transform([targetSquare](const Square square) {
308 return Move {
309 square, targetSquare, PieceType::Pawn
310 };
311 })
312 | stdv::filter([&position](const Move move) {
313 return position.is_legal(move);
314 })
315 | std::ranges::to<EPMoves>();
316 })
317 .value_or(EPMoves { });
318 }
319
320 template <Color Side, bool CapturesOnly>
321 constexpr void add_all_pawn_moves(
322 const Position& position, const Bitboard allOccupied,
323 std::output_iterator<Move> auto outputIt)
324 {
325 if constexpr (not CapturesOnly) {
326 std::ranges::copy(
327 get_pawn_pushes<Side>(position, allOccupied.inverse()),
328 outputIt);
329
330 std::ranges::copy(
331 get_pawn_double_pushes<Side>(position, allOccupied),
332 outputIt);
333 }
334
335 std::ranges::copy(
336 get_pawn_captures<Side>(position),
337 outputIt);
338
339 std::ranges::copy(
340 get_en_passant<Side>(position),
341 outputIt);
342 }
343
344 template <Color Side, bool CapturesOnly>
345 [[nodiscard, gnu::const]] constexpr auto get_knight_moves(
346 const Position& position)
347 {
348 const auto& ourPieces = position.pieces_for<Side>();
349
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);
355
356 if constexpr (CapturesOnly) {
357 knightMoves &= theirOccupied;
358 }
359
360 return knightMoves.squares()
361 | stdv::transform([knightPos](const Square targetSquare) {
362 return Move {
363 Square::from_index(knightPos.first()),
364 targetSquare, PieceType::Knight
365 };
366 });
367 })
368 | stdv::join
369 | stdv::filter([&position](const Move move) {
370 return position.is_legal(move);
371 });
372 }
373
374 template <Color Side, bool CapturesOnly>
375 [[nodiscard, gnu::const]] auto get_bishop_moves(
376 const Position& position,
377 const Bitboard occupiedSquares)
378 {
379 const auto& ourPieces = position.pieces_for<Side>();
380
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);
387
388 if constexpr (CapturesOnly) {
389 bishopMoves &= theirOccupied;
390 }
391
392 return bishopMoves.squares()
393 | stdv::transform([bishopPos](const Square targetSquare) {
394 return Move {
395 bishopPos, targetSquare, PieceType::Bishop
396 };
397 });
398 })
399 | stdv::join
400 | stdv::filter([&position](const Move move) {
401 return position.is_legal(move);
402 });
403 }
404
405 template <Color Side, bool CapturesOnly>
406 [[nodiscard, gnu::const]] auto get_rook_moves(
407 const Position& position,
408 const Bitboard occupiedSquares)
409 {
410 const auto& ourPieces = position.pieces_for<Side>();
411
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);
418
419 if constexpr (CapturesOnly) {
420 rookMoves &= theirOccupied;
421 }
422
423 return rookMoves.squares()
424 | stdv::transform([rookPos](const Square targetSquare) {
425 return Move {
426 rookPos, targetSquare, PieceType::Rook
427 };
428 });
429 })
430 | stdv::join
431 | stdv::filter([&position](const Move move) {
432 return position.is_legal(move);
433 });
434 }
435
436 template <Color Side, bool CapturesOnly>
437 [[nodiscard, gnu::const]] auto get_queen_moves(
438 const Position& position,
439 const Bitboard occupiedSquares)
440 {
441 const auto& ourPieces = position.pieces_for<Side>();
442
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);
449
450 if constexpr (CapturesOnly) {
451 queenMoves &= theirOccupied;
452 }
453
454 return queenMoves.squares()
455 | stdv::transform([queenPos](const Square targetSquare) {
456 return Move {
457 queenPos, targetSquare, PieceType::Queen
458 };
459 });
460 })
461 | stdv::join
462 | stdv::filter([&position](const Move move) {
463 return position.is_legal(move);
464 });
465 }
466
467 template <Color Side, bool CapturesOnly>
468 [[nodiscard, gnu::const]] constexpr auto get_king_moves(
469 const Position& position)
470 {
471 const auto& ourPieces = position.pieces_for<Side>();
472
473 auto kingMoves = pseudo_legal::king(ourPieces.king, ourPieces.occupied);
474
475 if constexpr (CapturesOnly) {
476 kingMoves &= position.pieces_for<pieces::other_side<Side>()>().occupied;
477 }
478
479 return kingMoves.squares()
480 | stdv::transform([kingSquare = ourPieces.get_king_location()](
481 const Square targetSquare) {
482 return Move {
483 kingSquare, targetSquare, PieceType::King
484 };
485 })
486 | stdv::filter([&position](const Move move) {
487 return position.is_legal(move);
488 });
489 }
490
491 // the two functions below are masks containing the set of squares that
492 // must not be attacked/occupied in order for castling to be allowed
493
494 template <Color Side>
495 [[nodiscard, gnu::const]] consteval auto kingside_castle_mask() noexcept -> Bitboard
496 {
497 static constexpr auto rank = board::back_rank_for(Side);
498
499 Bitboard board;
500
501 board.set(Square { .file = File::F, .rank = rank });
502 board.set(Square { .file = File::G, .rank = rank });
503
504 return board;
505 }
506
507 // NB. with queenside castling, the set of squares that must be free/not attacked differ,
508 // since castling is possible if the B1/B8 squares are attacked, but not if they are occupied
509 template <Color Side, bool Occupied>
510 [[nodiscard, gnu::const]] consteval auto queenside_castle_mask() noexcept -> Bitboard
511 {
512 static constexpr auto rank = board::back_rank_for(Side);
513
514 Bitboard board;
515
516 board.set(Square { .file = File::C, .rank = rank });
517 board.set(Square { .file = File::D, .rank = rank });
518
519 if constexpr (Occupied) {
520 board.set(Square { .file = File::B, .rank = rank });
521 }
522
523 return board;
524 }
525
526 template <Color Side>
527 [[nodiscard, gnu::const]] constexpr auto get_kingside_castling(
528 const Position& position, const Bitboard allOccupied)
529 -> std::optional<Move>
530 {
531 if (not position.castling_rights_for<Side>().kingside)
532 return std::nullopt;
533
534 static constexpr auto OppositeColor = pieces::other_side<Side>();
535
536 const auto& ourPieces = position.pieces_for<Side>();
537 const auto& theirPieces = position.pieces_for<OppositeColor>();
538
539 assert(ourPieces.rooks.test(Square { File::H, board::back_rank_for(Side) }));
540
541 static constexpr auto requiredSquares = kingside_castle_mask<Side>();
542
543 if ((requiredSquares & allOccupied).any()
544 or squares_attacked<OppositeColor>(theirPieces, requiredSquares, ourPieces.occupied)) {
545 return std::nullopt;
546 }
547
548 if (const auto move = castle_kingside(Side);
549 position.is_legal(move)) {
550 return move;
551 }
552
553 return std::nullopt;
554 }
555
556 template <Color Side>
557 [[nodiscard, gnu::const]] constexpr auto get_queenside_castling(
558 const Position& position, const Bitboard allOccupied)
559 -> std::optional<Move>
560 {
561 if (not position.castling_rights_for<Side>().queenside)
562 return std::nullopt;
563
564 static constexpr auto OppositeColor = pieces::other_side<Side>();
565
566 const auto& ourPieces = position.pieces_for<Side>();
567 const auto& theirPieces = position.pieces_for<OppositeColor>();
568
569 assert(ourPieces.rooks.test(Square { File::A, board::back_rank_for(Side) }));
570
571 static constexpr auto occupiedMask = queenside_castle_mask<Side, true>();
572 static constexpr auto attackedMask = queenside_castle_mask<Side, false>();
573
574 if ((allOccupied & occupiedMask).any()
575 or squares_attacked<OppositeColor>(theirPieces, attackedMask, ourPieces.occupied)) {
576 return std::nullopt;
577 }
578
579 if (const auto move = castle_queenside(Side);
580 position.is_legal(move)) {
581 return move;
582 }
583
584 return std::nullopt;
585 }
586
587 template <Color Side>
588 [[nodiscard, gnu::const]] constexpr auto get_castling(
589 const Position& position, const Bitboard allOccupied)
590 {
591 using Moves = beman::inplace_vector::inplace_vector<Move, 2uz>;
592
593 // castling out of check is not allowed
594 if (position.is_check())
595 return Moves { };
596
597 beman::inplace_vector::inplace_vector<Move, 2uz> moves;
598
599 auto add_move = [&moves](const Move move) {
600 moves.emplace_back(move);
601 return std::monostate { };
602 };
603
604 get_kingside_castling<Side>(position, allOccupied).transform(add_move);
605 get_queenside_castling<Side>(position, allOccupied).transform(add_move);
606
607 return moves;
608 }
609
610 template <Color Side, bool CapturesOnly>
611 void generate_internal(
612 const Position& position,
613 std::output_iterator<Move> auto outputIt)
614 {
615 const auto& ourPieces = position.pieces_for<Side>();
616 const auto& theirPieces = position.pieces_for<pieces::other_side<Side>()>();
617
618 const auto allOccupied = ourPieces.occupied | theirPieces.occupied;
619
620 add_all_pawn_moves<Side, CapturesOnly>(position, allOccupied, outputIt);
621
622 std::ranges::copy(
623 get_knight_moves<Side, CapturesOnly>(position),
624 outputIt);
625
626 std::ranges::copy(
627 get_bishop_moves<Side, CapturesOnly>(position, allOccupied),
628 outputIt);
629
630 std::ranges::copy(
631 get_rook_moves<Side, CapturesOnly>(position, allOccupied),
632 outputIt);
633
634 std::ranges::copy(
635 get_queen_moves<Side, CapturesOnly>(position, allOccupied),
636 outputIt);
637
638 std::ranges::copy(
639 get_king_moves<Side, CapturesOnly>(position),
640 outputIt);
641
642 if constexpr (not CapturesOnly) {
643 std::ranges::copy(
644 get_castling<Side>(position, allOccupied),
645 outputIt);
646 }
647 }
648
649 template <Color Side, bool CapturesOnly>
650 void generate_for_internal(
651 const Position& position, const PieceType piece,
652 std::output_iterator<Move> auto outputIt)
653 {
654 const auto& ourPieces = position.pieces_for<Side>();
655 const auto& theirPieces = position.pieces_for<pieces::other_side<Side>()>();
656
657 const auto allOccupied = ourPieces.occupied | theirPieces.occupied;
658
659 switch (piece) {
660 case PieceType::Pawn: {
661 add_all_pawn_moves<Side, CapturesOnly>(position, allOccupied, outputIt);
662 return;
663 }
664
665 case PieceType::Knight: {
666 std::ranges::copy(
667 get_knight_moves<Side, CapturesOnly>(position),
668 outputIt);
669 return;
670 }
671
672 case PieceType::Bishop: {
673 std::ranges::copy(
674 get_bishop_moves<Side, CapturesOnly>(position, allOccupied),
675 outputIt);
676 return;
677 }
678
679 case PieceType::Rook: {
680 std::ranges::copy(
681 get_rook_moves<Side, CapturesOnly>(position, allOccupied),
682 outputIt);
683 return;
684 }
685
686 case PieceType::Queen: {
687 std::ranges::copy(
688 get_queen_moves<Side, CapturesOnly>(position, allOccupied),
689 outputIt);
690 return;
691 }
692
693 default : [[fallthrough]];
694 case PieceType::King: {
695 std::ranges::copy(
696 get_king_moves<Side, CapturesOnly>(position),
697 outputIt);
698
699 if constexpr (not CapturesOnly) {
700 // castling is considered a King move
701 std::ranges::copy(
702 get_castling<Side>(position, allOccupied),
703 outputIt);
704 }
705
706 return;
707 }
708 }
709 }
710
711 template <Color Side>
712 [[nodiscard]] auto any_legal_moves_internal(const Position& position) -> bool
713 {
714 MoveList moves;
715
716 // as an optimization, check for king moves first, because in a double check,
717 // a king move would be the only valid response
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));
720
721 if (not moves.empty())
722 return true;
723
724 moves.clear();
725 }
726
727 return false;
728 }
729
730} // namespace detail
731#endif // DOXYGEN
732
733template <bool CapturesOnly>
735 const Position& position,
736 std::output_iterator<Move> auto outputIt)
737{
738 if (position.is_white_to_move())
739 detail::generate_internal<Color::White, CapturesOnly>(position, outputIt);
740 else
741 detail::generate_internal<Color::Black, CapturesOnly>(position, outputIt);
742}
743
744template <bool CapturesOnly>
745auto generate(const Position& position) -> MoveList
746{
748
749 generate<CapturesOnly>(position, std::back_inserter(moves));
750
751 return moves;
752}
753
754template <bool CapturesOnly>
756 const Position& position, const PieceType piece,
757 std::output_iterator<Move> auto outputIt)
758{
759 if (position.is_white_to_move())
760 detail::generate_for_internal<Color::White, CapturesOnly>(position, piece, outputIt);
761 else
762 detail::generate_for_internal<Color::Black, CapturesOnly>(position, piece, outputIt);
763}
764
765template <bool CapturesOnly>
767 const Position& position, const PieceType piece)
768 -> MoveList
769{
771
772 generate_for<CapturesOnly>(position, piece, std::back_inserter(moves));
773
774 return moves;
775}
776
777inline auto any_legal_moves(const Position& position) -> bool
778{
779 if (position.is_white_to_move())
780 return detail::any_legal_moves_internal<Color::White>(position);
781
782 return detail::any_legal_moves_internal<Color::Black>(position);
783}
784
785} // namespace chess::moves
constexpr auto prev_pawn_rank(Rank rank) noexcept -> Rank
Definition Rank.hpp:153
constexpr auto rank(Bitboard starting) noexcept -> Bitboard
Definition Fills.hpp:218
constexpr auto MAX_MOVES
Definition MoveGen.hpp:58
constexpr auto castle_queenside(const Color color) noexcept -> Move
Definition Move.hpp:315
constexpr auto castle_kingside(const Color color) noexcept -> Move
Definition Move.hpp:304
void generate_for(const Position &position, PieceType piece, std::output_iterator< Move > auto outputIt)
Definition MoveGen.hpp:755
auto any_legal_moves(const Position &position) -> bool
Definition MoveGen.hpp:777
pieces::Type PieceType
Definition Move.hpp:54
void generate(const Position &position, std::output_iterator< Move > auto outputIt)
Definition MoveGen.hpp:734
beman::inplace_vector::inplace_vector< Move, MAX_MOVES > MoveList
Definition MoveGen.hpp:63
auto is_white_to_move() const noexcept -> bool
Definition Position.hpp:217
std::optional< Square > enPassantTargetSquare
Definition Position.hpp:109
auto is_legal(Move move) const -> bool
Definition Position.hpp:455
auto pieces_for() noexcept -> Pieces &
Definition Position.hpp:150
auto castling_rights_for() const noexcept -> CastlingRights
Definition Position.hpp:170
auto is_check() const noexcept -> bool
Definition Position.hpp:266
auto is_white_to_move() const noexcept -> bool
Definition Position.hpp:217