HooverChessUtils_PgnReader 0.9.0
Loading...
Searching...
No Matches
bitboard-attacks.h
Go to the documentation of this file.
1// Hoover Chess Utilities / PGN reader
2// Copyright (C) 2023-2025 Sami Kiminki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17#ifndef HOOVER_CHESS_UTILS__PGN_READER__BITBOARD_ATTACKS_H_INCLUDED
18#define HOOVER_CHESS_UTILS__PGN_READER__BITBOARD_ATTACKS_H_INCLUDED
19
20#include "pgnreader-config.h"
21
22#include "chessboard-types.h"
24
25// the baseline
26#include "bitboard-tables.h"
28
29#if BITBOARD_TABLES_HAVE_ELEMENTARY
31#endif
32
33#if BITBOARD_TABLES_HAVE_BLACK_MAGIC
35#endif
36
37#if BITBOARD_TABLES_HAVE_AARCH64_SVE2_BITPERM
39#endif
40
41#if BITBOARD_TABLES_HAVE_X86_BMI2
43#endif
44
45#if HAVE_X86_AVX512F
47#endif
48
49#include <array>
50#include <cinttypes>
51
53{
54
58{
59public:
81 static inline SquareSet getPawnAttackMask(Square sq, Color pawnColor) noexcept
82 {
83 static_assert(static_cast<std::uint64_t>(Color::WHITE) == 0U);
84 static_assert(static_cast<std::uint64_t>(Color::BLACK) == 8U);
85
86 return Attacks_Portable::getPawnAttackMask(sq, pawnColor);
87 }
88
111 static inline SquareSet getPawnAttackerMask(Square sq, Color pawnColor) noexcept
112 {
113 static_assert(static_cast<std::uint64_t>(Color::WHITE) == 0U);
114 static_assert(static_cast<std::uint64_t>(Color::BLACK) == 8U);
115
116 return Attacks_Portable::getPawnAttackerMask(sq, pawnColor);
117 }
118
147 template <Color pawnColor, bool captureToRight>
148 static constexpr inline SquareSet getPawnAttackersMask(SquareSet capturable) noexcept
149 {
150 return Attacks_Portable::getPawnAttackersMask<pawnColor, captureToRight>(capturable);
151 }
152
170 static inline SquareSet getKnightAttackMask(Square sq) noexcept
171 {
173 }
174
219 static inline SquareSet getBishopAttackMask(Square sq, SquareSet occupancyMask) noexcept
220 {
221#if BITBOARD_TABLES_HAVE_X86_BMI2
222 return
223 Attacks_BMI2::getBishopAttackMask(sq, occupancyMask);
224#elif BITBOARD_TABLES_HAVE_AARCH64_SVE2_BITPERM
225 return
227#elif BITBOARD_TABLES_HAVE_BLACK_MAGIC
228 return
230#elif BITBOARD_TABLES_HAVE_ELEMENTARY
231 return
233#else
234 return
235 Attacks_Portable::getBishopAttackMask(sq, occupancyMask);
236#endif
237 }
238
281 static inline SquareSet getRookAttackMask(Square sq, SquareSet occupancyMask) noexcept
282 {
283#if BITBOARD_TABLES_HAVE_X86_BMI2
284 return
285 Attacks_BMI2::getRookAttackMask(sq, occupancyMask);
286#elif BITBOARD_TABLES_HAVE_AARCH64_SVE2_BITPERM
287 return
289#elif BITBOARD_TABLES_HAVE_BLACK_MAGIC
290 return
291 Attacks_BlackMagic::getRookAttackMask(sq, occupancyMask);
292#elif BITBOARD_TABLES_HAVE_ELEMENTARY
293 return
295#else
296 return
297 Attacks_Portable::getRookAttackMask(sq, occupancyMask);
298#endif
299 }
300
301 static inline SquareSet getQueenAttackMask(Square sq, SquareSet occupancyMask) noexcept
302 {
303#if BITBOARD_TABLES_HAVE_X86_BMI2
304 return
305 Attacks_BMI2::getBishopAttackMask(sq, occupancyMask) |
306 Attacks_BMI2::getRookAttackMask(sq, occupancyMask);
307#elif BITBOARD_TABLES_HAVE_AARCH64_SVE2_BITPERM
308 return
310#elif BITBOARD_TABLES_HAVE_BLACK_MAGIC
311 return
312 Attacks_BlackMagic::getBishopAttackMask(sq, occupancyMask) |
313 Attacks_BlackMagic::getRookAttackMask(sq, occupancyMask);
314#elif BITBOARD_TABLES_HAVE_ELEMENTARY
315 return
318#else
319 return
320 Attacks_Portable::getBishopAttackMask(sq, occupancyMask) |
321 Attacks_Portable::getRookAttackMask(sq, occupancyMask);
322#endif
323 }
324
340 static inline SquareSet getKingAttackMask(Square sq) noexcept
341 {
343 }
344
359 static inline bool pinCheck(Square src, SquareSet dstBit, Square kingSq, SquareSet pinnedPieces) noexcept
360 {
361 return ((pinnedPieces & SquareSet { src }) == SquareSet { } ||
362 (Intercepts::getPinRestiction<true>(kingSq, src) & dstBit) != SquareSet { });
363 }
364
366 const SquareSet occupancyMask,
367 const SquareSet turnColorMask,
368 const SquareSet pawns,
369 const SquareSet knights,
370 const SquareSet bishops,
371 const SquareSet rooks,
372 const SquareSet kings,
373 const Square sq,
374 const Color turn) noexcept
375 {
376 SquareSet attackers { };
377
378#if BITBOARD_TABLES_HAVE_AARCH64_SVE2_BITPERM
379
380 const auto [ diagHits, horizVertHits ] = Attacks_AArch64_SVE2_BitPerm::getBishopAndRookAttackMasks(
381 sq,
382 occupancyMask);
383
384#else
385 // rooks and queens
386 const SquareSet horizVertHits { Attacks::getRookAttackMask(sq, occupancyMask) };
387
388 // bishops and queens
389 const SquareSet diagHits { Attacks::getBishopAttackMask(sq, occupancyMask) };
390#endif
391 const SquareSet attackers1 { horizVertHits & rooks };
392 const SquareSet attackers2 { diagHits & bishops };
393
394 // pawn attackers
395 const SquareSet attackers3 { Attacks::getPawnAttackMask(sq, turn) & pawns };
396
397 // king
398 const SquareSet attackers4 { Attacks::getKingAttackMask(sq) & kings };
399
400 // knights
401 const SquareSet attackers5 { Attacks::getKnightAttackMask(sq) & knights };
402
403 attackers = (attackers1 | attackers2 | attackers3 | attackers4 | attackers5) & ~turnColorMask;
404
405 return attackers;
406 }
407
426 static inline void determineCheckersAndPins(
427 SquareSet occupancyMask,
428 SquareSet turnColorMask,
429 SquareSet pawns,
430 SquareSet knights,
431 SquareSet bishops,
432 SquareSet rooks,
433 Square epSquare,
434 SquareSet epCapturable,
435 Square kingSq,
436 Color turn,
437 SquareSet &out_checkers,
438 SquareSet &out_pinnedPieces) noexcept
439 {
440
441#if 0 && HAVE_X86_AVX512F // quite a lot slower than the PDEP/PEXT implementation on Zen4, so disable for now
442 Attacks_AVX512F::determineSliderCheckersAndPins(
443 occupancyMask,
444 turnColorMask,
445 bishops,
446 rooks,
447 epCapturable,
448 kingSq,
449 out_checkers,
450 out_pinnedPieces);
451
452 const SquareSet opponentPieces { occupancyMask &~ turnColorMask };
453
454 // pawn checkers
455 out_checkers |= Attacks::getPawnAttackMask(kingSq, turn) & pawns;
456
457 // knights
458 out_checkers |= Attacks::getKnightAttackMask(kingSq) & knights;
459
460 out_checkers &= opponentPieces;
461
462#else
463 const SquareSet opponentPieces { occupancyMask ^ turnColorMask };
464 SquareSet pinners { };
465
466#if HAVE_AARCH64_SVE2_BITPERM && 0 // Somewhat slower on Neoverse V2, so disable for now
467 if constexpr (false)
468 {
470 kingSq,
471 occupancyMask,
472 rooks,
473 bishops,
474 opponentPieces,
475 epCapturable,
476 out_checkers,
477 pinners);
478
479 // pawn checkers
480 out_checkers |= Attacks::getPawnAttackMask(kingSq, turn) & pawns;
481
482 // knights
483 out_checkers |= Attacks::getKnightAttackMask(kingSq) & knights;
484
485 out_checkers &= opponentPieces;
486 }
487 else
488 {
489 // rooks and queens
490 const auto [ firstDiagHits, firstHVHits ] =
492 kingSq,
493 occupancyMask);
494
495 // pawn checkers
496 out_checkers = Attacks::getPawnAttackMask(kingSq, turn) & pawns;
497
498 // bishops and queens
499 out_checkers |= firstDiagHits & bishops;
500
501 // rooks and queens
502 out_checkers |= firstHVHits & rooks;
503
504 // Resolve pinned pieces. Notes:
505 // - We'll remove only pinnable pieces from the first hits. The idea is to avoid
506 // x-rays over non-pinnable pieces in order to minimize the number of pinners
507 // - In the second hit check, we'll remove pieces that are already determined to be checkers.
508 // The reason is the same as the above.
509
510 const auto [ secondDiagHits, secondHVHits ] =
512 kingSq,
513 (occupancyMask &~ firstDiagHits) | (opponentPieces &~ epCapturable),
514 (occupancyMask &~ firstHVHits) | opponentPieces);
515
516 // knights
517 out_checkers |= Attacks::getKnightAttackMask(kingSq) & knights;
518
519 out_checkers &= opponentPieces;
520
521 pinners = ((rooks & secondHVHits) | (bishops & secondDiagHits)) & opponentPieces &~ out_checkers;
522 }
523#else
524 {
525 // pawn checkers
526 out_checkers = Attacks::getPawnAttackMask(kingSq, turn) & pawns;
527
528 // knights
529 out_checkers |= Attacks::getKnightAttackMask(kingSq) & knights;
530
531 // rooks and queens
532 const SquareSet firstHVHits { Attacks::getRookAttackMask(kingSq, occupancyMask) };
533 out_checkers |= firstHVHits & rooks;
534
535 // bishops and queens
536 const SquareSet firstDiagHits { Attacks::getBishopAttackMask(kingSq, occupancyMask) };
537 out_checkers |= firstDiagHits & bishops;
538
539 out_checkers &= opponentPieces;
540
541 // Resolve pinned pieces. Notes:
542 // - We'll remove only pinnable pieces from the first hits. The idea is to avoid
543 // x-rays over non-pinnable pieces in order to minimize the number of pinners
544 // - In the second hit check, we'll remove pieces that are already determined to be checkers.
545 // The reason is the same as the above.
546 const SquareSet secondHVHits {
547 Attacks::getRookAttackMask(kingSq, (occupancyMask &~ firstHVHits) | opponentPieces) };
548 const SquareSet secondDiagHits {
549 Attacks::getBishopAttackMask(kingSq, (occupancyMask &~ firstDiagHits) | (opponentPieces &~ epCapturable)) };
550
551 pinners = ((rooks & secondHVHits) | (bishops & secondDiagHits)) & opponentPieces &~ out_checkers;
552 }
553#endif
554 out_pinnedPieces = SquareSet { };
556 pinner,
557 pinners,
558 {
559 SquareSet inBetween { Intercepts::getInterceptSquares(kingSq, pinner) };
560 out_pinnedPieces |= inBetween & (turnColorMask | epCapturable);
561 });
562#endif
563 // The rest of this function is EP capture legality checking. Short-circuit if there's no EP to capture
564 if (epCapturable == SquareSet { })
565 return;
566
567 // If we're in check and the only checker is not the EP pawn, EP capture
568 // is not legal
569 if ((out_checkers &~ epCapturable) != SquareSet { })
570 out_pinnedPieces |= epCapturable;
571
572 // If not marked illegal yet, check whether the EP capture is legal.
573 if ((epCapturable &~ out_pinnedPieces) != SquareSet { })
574 {
575 // Adjacent pawns. This leaves one pawn in case there's
576 // adjacent pawns both sides.
577 const SquareSet adjacentPawns {
578 ((((epCapturable & ~SquareSet::column(0U)) >> 1U) |
579 ((epCapturable & ~SquareSet::column(7U)) << 1U))
580
581 & pawns & turnColorMask)
582 };
583
584 // If all (usually 1) adjacent pawns are pinned and can't capture
585 // along the pin direction, mark the EP pawn pinned, too. Capture is
586 // not legal.
587 SquareSet epCaptureLegalMask { };
589 epCapturer,
590 adjacentPawns,
591 {
592 if (pinCheck(epCapturer, SquareSet { epSquare }, kingSq, out_pinnedPieces))
593 epCaptureLegalMask = SquareSet::all();
594 });
595
596 // add EP capturable to pinned if neither capture is legal
597 out_pinnedPieces |= epCapturable &~ epCaptureLegalMask;
598
599 // Check whether the EP capturable pawn is horizontally pinned. This can only happen
600 // when the king is on the same row
601 if ((epCapturable & (SquareSet::row(0U) << (static_cast<std::uint64_t>(kingSq) & 56U)))
602 != SquareSet { })
603 {
604 const SquareSet adjacentPawnsMinus1 { adjacentPawns.removeFirstSquare() };
605
606 const SquareSet exposedHorizLine {
608 epCapturable.firstSquare(),
609 occupancyMask &~ (adjacentPawns &~ adjacentPawnsMinus1)) };
610
611 const SquareSet kingBit { kingSq };
612 const SquareSet oppRooks { rooks & ~turnColorMask };
613
614 const SquareSet pinnedEp { epCapturable &
615 (kingBit & exposedHorizLine).allIfAny() &
616 (oppRooks & exposedHorizLine).allIfAny() };
617
618 // add the EP pawn in pinned pieces if capturing it would
619 // horizontally expose the king to a rook
620 out_pinnedPieces |= pinnedEp;
621 }
622 }
623 }
624
636 SquareSet occupancyMask,
637 SquareSet pawns,
638 SquareSet knights,
639 SquareSet bishops,
640 SquareSet rooks,
641 Square king,
642 Color turn) noexcept
643 {
644#if HAVE_X86_AVX512F
646 occupancyMask,
647 pawns,
648 knights,
649 bishops,
650 rooks,
651 king,
652 turn);
653#else
654 SquareSet attacks { };
655
656 // pawn attacks
657 static_assert(static_cast<std::int8_t>(Color::WHITE) == 0);
658 static_assert(static_cast<std::int8_t>(Color::BLACK) == 8);
659
660 // for rotl -- pawn color is opposite to turn
661 std::int8_t pawnAdvanceShiftLeft = -9 + 2 * static_cast<std::int8_t>(turn);
662
663 // captures to left
664 attacks |= (pawns &~ SquareSet::column(0)).rotl(pawnAdvanceShiftLeft);
665
666 // captures to right
667 attacks |= (pawns &~ SquareSet::column(7)).rotl(pawnAdvanceShiftLeft + 2);
668
669
671 piece,
672 knights,
673 attacks |= getKnightAttackMask(piece));
674
676 piece,
677 bishops,
678 attacks |= getBishopAttackMask(piece, occupancyMask));
679
681 piece,
682 rooks,
683 attacks |= getRookAttackMask(piece, occupancyMask));
684
685 attacks |= getKingAttackMask(king);
686
687 return attacks;
688#endif
689 }
690};
691
692}
693
694#endif
static SquareSet getQueenAttackMask(Square sq, SquareSet occupancyMask) noexcept
See Attacks::getQueenAttackMask() for documentation.
Definition bitboard-attacks-aarch64-sve2-bitperm.h:80
static std::pair< SquareSet, SquareSet > getBishopAndRookAttackMasks(Square sq, SquareSet occupancyMask) noexcept
Definition bitboard-attacks-aarch64-sve2-bitperm.h:106
static SquareSet getBishopAttackMask(Square sq, SquareSet occupancyMask) noexcept
See Attacks::getBishopAttackMask() for documentation.
Definition bitboard-attacks-aarch64-sve2-bitperm.h:52
static void determineSliderCheckersAndPinners(Square kingSq, SquareSet occupancyMask, SquareSet rooks, SquareSet bishops, SquareSet opponentPieces, SquareSet epCapturable, SquareSet &out_checkers, SquareSet &out_pinners) noexcept
Definition bitboard-attacks-aarch64-sve2-bitperm.h:157
static SquareSet getRookAttackMask(Square sq, SquareSet occupancyMask) noexcept
See Attacks::getRookAttackMask() for documentation.
Definition bitboard-attacks-aarch64-sve2-bitperm.h:66
static SquareSet determineAttackedSquares(SquareSet occupancyMask, SquareSet pawns, SquareSet knights, SquareSet bishops, SquareSet rooks, Square king, Color turn) noexcept
Definition bitboard-attacks-x86-avx512f.h:191
static SquareSet getRookAttackMask(Square sq, SquareSet occupancyMask) noexcept
See Attacks::getRookAttackMask() for documentation.
Definition bitboard-attacks-x86-bmi2.h:53
static SquareSet getBishopAttackMask(Square sq, SquareSet occupancyMask) noexcept
See Attacks::getBishopAttackMask() for documentation.
Definition bitboard-attacks-x86-bmi2.h:40
static SquareSet getBishopAttackMask(Square sq, SquareSet occupancyMask) noexcept
See Attacks::getBishopAttackMask() for documentation.
Definition bitboard-attacks-black-magic.h:38
static SquareSet getRookAttackMask(Square sq, SquareSet occupancyMask) noexcept
See Attacks::getRookAttackMask() for documentation.
Definition bitboard-attacks-black-magic.h:50
static SquareSet getBishopAttackMask(Square sq, SquareSet occupancyMask) noexcept
See Attacks::getBishopAttackMask() for documentation.
Definition bitboard-attacks-elementary.h:38
static SquareSet getRookAttackMask(Square sq, SquareSet occupancyMask) noexcept
See Attacks::getRookAttackMask() for documentation.
Definition bitboard-attacks-elementary.h:53
static SquareSet getBishopAttackMask(Square sq, SquareSet occupancyMask) noexcept
See Attacks::getBishopAttackMask() for usage documentation.
static SquareSet getHorizRookAttackMask(Square sq, SquareSet occupancyMask) noexcept
Returns horizontal rook attack mask.
Definition bitboard-attacks-portable.h:303
static SquareSet getPawnAttackMask(Square sq, Color pawnColor) noexcept
Definition bitboard-attacks-portable.h:211
static SquareSet getKingAttackMask(Square sq) noexcept
Definition bitboard-attacks-portable.h:266
static SquareSet getKnightAttackMask(Square sq) noexcept
Definition bitboard-attacks-portable.h:261
static SquareSet getPawnAttackerMask(Square sq, Color pawnColor) noexcept
Definition bitboard-attacks-portable.h:219
static SquareSet getRookAttackMask(Square sq, SquareSet occupancyMask) noexcept
See Attacks::getRookAttackMask() for usage documentation.
Piece attack tables.
Definition bitboard-attacks.h:58
static SquareSet getBishopAttackMask(Square sq, SquareSet occupancyMask) noexcept
Returns the set of squares a bishop can attack, given also a set of occupied squares on board.
Definition bitboard-attacks.h:219
static SquareSet determineAttackers(const SquareSet occupancyMask, const SquareSet turnColorMask, const SquareSet pawns, const SquareSet knights, const SquareSet bishops, const SquareSet rooks, const SquareSet kings, const Square sq, const Color turn) noexcept
Definition bitboard-attacks.h:365
static SquareSet determineAttackedSquares(SquareSet occupancyMask, SquareSet pawns, SquareSet knights, SquareSet bishops, SquareSet rooks, Square king, Color turn) noexcept
Determines all attacked squares.
Definition bitboard-attacks.h:635
static SquareSet getPawnAttackMask(Square sq, Color pawnColor) noexcept
Returns a set of squares that a pawn can attack.
Definition bitboard-attacks.h:81
static SquareSet getPawnAttackerMask(Square sq, Color pawnColor) noexcept
Returns a set of squares from which a pawn can attack.
Definition bitboard-attacks.h:111
static SquareSet getQueenAttackMask(Square sq, SquareSet occupancyMask) noexcept
Definition bitboard-attacks.h:301
static constexpr SquareSet getPawnAttackersMask(SquareSet capturable) noexcept
For a given set of squares, returns the squares where pawns can attack the given squares.
Definition bitboard-attacks.h:148
static bool pinCheck(Square src, SquareSet dstBit, Square kingSq, SquareSet pinnedPieces) noexcept
Checks whether a move by a possibly pinned piece does not expose a check.
Definition bitboard-attacks.h:359
static SquareSet getKingAttackMask(Square sq) noexcept
Returns the set of squares a king can attack.
Definition bitboard-attacks.h:340
static SquareSet getRookAttackMask(Square sq, SquareSet occupancyMask) noexcept
Returns the set of squares a rook can attack, given also a set of occupied squares on board.
Definition bitboard-attacks.h:281
static SquareSet getKnightAttackMask(Square sq) noexcept
Returns the set of squares a knight can attack.
Definition bitboard-attacks.h:170
static void determineCheckersAndPins(SquareSet occupancyMask, SquareSet turnColorMask, SquareSet pawns, SquareSet knights, SquareSet bishops, SquareSet rooks, Square epSquare, SquareSet epCapturable, Square kingSq, Color turn, SquareSet &out_checkers, SquareSet &out_pinnedPieces) noexcept
Determines all checkers and pinners.
Definition bitboard-attacks.h:426
static SquareSet getInterceptSquares(Square kingSq, Square checkerSq) noexcept
Returns the set of squares that intercepts a check.
Definition bitboard-intercepts.h:66
Set of squares. Implemented using a bit-mask.
Definition chessboard-types-squareset.h:35
static constexpr SquareSet all() noexcept
Returns a set of all squares.
Definition chessboard-types-squareset.h:460
static constexpr SquareSet row(RowColumn row) noexcept
Returns a set of squares in row number row.
Definition chessboard-types-squareset.h:485
constexpr SquareSet removeFirstSquare() const noexcept
Returns a square set with the first square (if any) removed.
Definition chessboard-types-squareset.h:124
Color
Color of a piece or side to move.
Definition chessboard-types.h:194
Square
Named square.
Definition chessboard-types.h:122
#define SQUARESET_ENUMERATE(sq, squareSet,...)
Enumerates all squares in a square set.
Definition chessboard-types-squareset.h:623
@ BLACK
Black piece or black side to move.
@ WHITE
White piece or white side to move.
Definition chessboard-types-squareset.h:30