LCOV - code coverage report
Current view: top level - Source - moveGen.hpp (source / functions) Coverage Total Hit
Test: coverage Lines: 84.7 % 85 72
Test Date: 2026-03-02 16:42:41 Functions: 100.0 % 29 29

            Line data    Source code
       1              : #pragma once
       2              : 
       3              : #include "attack.hpp"
       4              : #include "bitboardTools.hpp"
       5              : #include "definition.hpp"
       6              : #include "dynamicConfig.hpp"
       7              : #include "hash.hpp"
       8              : #include "logging.hpp"
       9              : #include "movePseudoLegal.hpp"
      10              : #include "positionTools.hpp"
      11              : #include "timers.hpp"
      12              : #include "tools.hpp"
      13              : 
      14              : struct Searcher;
      15              : 
      16              : template<MType MT>
      17              : struct CastlingTraits{
      18              : };
      19              : 
      20              : template<>
      21              : struct CastlingTraits<T_wqs>{
      22              :    static constexpr CastlingTypes ct = CT_OOO;
      23              :    static constexpr MType mt = T_wqs;
      24              :    static constexpr CastlingRights cr = C_wqs;
      25              :    static constexpr Color c = Co_White;
      26              :    static constexpr Square kingLanding = Sq_c1;
      27              :    static constexpr Square rookLanding = Sq_d1;
      28              :    static constexpr BitBoard kingLandingBB = BB::BBSq_c1;
      29              :    static constexpr BitBoard rookLandingBB = BB::BBSq_d1;
      30              :    static constexpr Piece fromP = P_wk;
      31              :    static constexpr Piece toP = P_wr;
      32              : };
      33              : 
      34              : template<>
      35              : struct CastlingTraits<T_wks>{
      36              :    static constexpr CastlingTypes ct = CT_OO;
      37              :    static constexpr MType mt = T_wks;
      38              :    static constexpr CastlingRights cr = C_wks;
      39              :    static constexpr Color c = Co_White;
      40              :    static constexpr Square kingLanding = Sq_g1;
      41              :    static constexpr Square rookLanding = Sq_f1;
      42              :    static constexpr BitBoard kingLandingBB = BB::BBSq_g1;
      43              :    static constexpr BitBoard rookLandingBB = BB::BBSq_f1;
      44              :    static constexpr Piece fromP = P_wk;
      45              :    static constexpr Piece toP = P_wr;
      46              : };
      47              : 
      48              : template<>
      49              : struct CastlingTraits<T_bqs>{
      50              :    static constexpr CastlingTypes ct = CT_OOO;
      51              :    static constexpr MType mt = T_bqs;
      52              :    static constexpr CastlingRights cr = C_bqs;
      53              :    static constexpr Color c = Co_Black;
      54              :    static constexpr Square kingLanding = Sq_c8;
      55              :    static constexpr Square rookLanding = Sq_d8;
      56              :    static constexpr BitBoard kingLandingBB = BB::BBSq_c8;
      57              :    static constexpr BitBoard rookLandingBB = BB::BBSq_d8;
      58              :    static constexpr Piece fromP = P_bk;
      59              :    static constexpr Piece toP = P_br;
      60              : };
      61              : 
      62              : template<>
      63              : struct CastlingTraits<T_bks>{
      64              :    static constexpr CastlingTypes ct = CT_OO;
      65              :    static constexpr MType mt = T_bks;
      66              :    static constexpr CastlingRights cr = C_bks;
      67              :    static constexpr Color c = Co_Black;
      68              :    static constexpr Square kingLanding = Sq_g8;
      69              :    static constexpr Square rookLanding = Sq_f8;
      70              :    static constexpr BitBoard kingLandingBB = BB::BBSq_g8;
      71              :    static constexpr BitBoard rookLandingBB = BB::BBSq_f8;
      72              :    static constexpr Piece fromP = P_bk;
      73              :    static constexpr Piece toP = P_br;
      74              : };
      75              : 
      76              : [[nodiscard]] ScoreType randomMover(const Position& p, PVList& pv, const bool isInCheck);
      77              : 
      78              : namespace MoveGen {
      79              : 
      80              : enum GenPhase { GP_all = 0, GP_cap = 1, GP_quiet = 2, GP_evasion = 3 };
      81              : 
      82              : FORCE_FINLINE void addMove(const Square from, const Square to, const MType type, MoveList& moves) {
      83            0 :    assert(isValidSquare(from));
      84      4339414 :    assert(isValidSquare(to));
      85    194631767 :    moves.emplace_back(ToMove(from, to, type, 0));
      86    190432344 : }
      87              : 
      88              : template<GenPhase phase = GP_all> 
      89    129641277 : void generateSquare(const Position& p, MoveList& moves, const Square from) {
      90    129641277 :    assert(isValidSquare(from));
      91              : #ifdef DEBUG_GENERATION
      92              :    if (from == INVALIDSQUARE) Logging::LogIt(Logging::logFatal) << "invalid square";
      93              : #endif
      94    129641277 :    const Color     side       = p.c;
      95    129641277 :    const BitBoard& myPieceBB  = p.allPieces[side];
      96    129641277 :    const BitBoard& oppPieceBB = p.allPieces[~side];
      97    129641277 :    const Piece     piece      = p.board_const(from);
      98    129641277 :    const Piece     ptype      = Abs(piece);
      99            0 :    assert(isValidPieceNotNone(ptype));
     100              : #ifdef DEBUG_GENERATION
     101              :    if (!isValidPieceNotNone(ptype)) {
     102              :       Logging::LogIt(Logging::logWarn) << ToString(myPieceBB);
     103              :       Logging::LogIt(Logging::logWarn) << ToString(oppPieceBB);
     104              :       Logging::LogIt(Logging::logWarn) << "piece " << static_cast<int>(piece);
     105              :       Logging::LogIt(Logging::logWarn) << SquareNames[from];
     106              :       Logging::LogIt(Logging::logWarn) << ToString(p);
     107              :       Logging::LogIt(Logging::logWarn) << ToString(p.lastMove);
     108              :       Logging::LogIt(Logging::logFatal) << "invalid type " << ptype;
     109              :    }
     110              : #endif
     111    102507048 :    const BitBoard occupancy = p.occupancy();
     112      6298971 :    BitBoard sliderRay = emptyBitBoard;
     113              :    BitBoard attacker = emptyBitBoard;
     114              :    if (phase == GP_evasion){
     115      6298971 :       const Square kingSq = kingSquare(p);
     116     12597942 :       const BitBoard slider = BBTools::attack<P_wb>(kingSq, p.pieces_const<P_wb>(~p.c) | p.pieces_const<P_wq>(~p.c), occupancy)|
     117      6298971 :                               BBTools::attack<P_wr>(kingSq, p.pieces_const<P_wr>(~p.c) | p.pieces_const<P_wq>(~p.c), occupancy);
     118              :       attacker = slider;
     119              :       BB::applyOn(slider, [&](const Square & k){
     120      5731042 :          sliderRay |= BBTools::between(k, kingSq);
     121              :       });
     122     12597942 :       attacker |= BBTools::attack<P_wn>(kingSq, p.pieces_const<P_wn>(~p.c));
     123      6298971 :       attacker |= BBTools::attack<P_wp>(kingSq, p.pieces_const<P_wp>(~p.c), occupancy, p.c);
     124      6298971 :       if (!attacker){
     125              :          std::cout << "Error ! no attaker but evasion requested !" << std::endl;
     126            0 :          std::cout << ToString(p) << std::endl;
     127              :       }
     128              :       //std::cout << ToString(attacker) << std::endl;
     129              :       //std::cout << ToString(sliderRay) << std::endl;
     130              :    }
     131    129641277 :    if (ptype != P_wp) {
     132     59225849 :       BitBoard bb = BBTools::pfCoverage[ptype - 1](from, occupancy, p.c) & ~myPieceBB;
     133              :       if (phase == GP_cap){
     134              :          // only target opponent piece
     135      6770902 :          bb &= oppPieceBB;
     136              :       }
     137              :       else if (phase == GP_quiet){
     138              :          // do not target opponent piece
     139         6446 :          bb &= ~oppPieceBB;
     140              :       }
     141              :       else if (phase == GP_evasion){
     142              :          // king don't want to stay in check
     143      2530326 :          if ( ptype == P_wk )
     144       695993 :             bb &= ~sliderRay;
     145              :          // other pieces only target king attakers and sliders path
     146              :          else
     147      1834333 :             bb &= attacker | sliderRay;
     148              :       }
     149    273558017 :       BB::applyOn(bb, [&](const Square & to){
     150    107166084 :          const bool isCap = (phase == GP_cap) || ((oppPieceBB & SquareToBitboard(to)) != emptyBitBoard);
     151    110936491 :          if (isCap) addMove(from, to, T_capture, moves);
     152              :          else
     153    100051192 :             addMove(from, to, T_std, moves);
     154              :       });
     155              : 
     156              :       // attack on castling king destination square will be checked by applyMove
     157     49924621 :       if (phase != GP_cap && phase != GP_evasion && ptype == P_wk) { // add castling
     158              :          // Be carefull, here rooksInit are only accessed if the corresponding p.castling is set
     159              :          // This way rooksInit is not INVALIDSQUARE, and thus mask[] is not out of bound
     160              :          // Moreover, king[] also is assumed to be not INVALIDSQUARE which is verified in readFen
     161              :          /*
     162              :          std::cout << int(p.rootInfo().kingInit[Co_White]) << std::endl;
     163              :          std::cout << int(p.rootInfo().kingInit[Co_Black]) << std::endl;
     164              :          std::cout << int(p.rootInfo().rooksInit[Co_White][CT_OOO]) << std::endl;
     165              :          std::cout << int(p.rootInfo().rooksInit[Co_White][CT_OO]) << std::endl;
     166              :          std::cout << int(p.rootInfo().rooksInit[Co_Black][CT_OOO]) << std::endl;
     167              :          std::cout << int(p.rootInfo().rooksInit[Co_Black][CT_OO]) << std::endl;         
     168              :          */
     169              :          
     170     36132180 :          auto addIfCastlingOk = [&]<MType mt>() mutable {
     171              :             const Color c = CastlingTraits<mt>::c;
     172     14452872 :             const Square to = p.rootInfo().rooksInit[c][CastlingTraits<mt>::ct];
     173     14452872 :             if (p.castling & CastlingTraits<mt>::cr) {
     174     10433855 :                const BitBoard kingPath = BBTools::between(p.king[c], CastlingTraits<mt>::kingLanding) | CastlingTraits<mt>::kingLandingBB;
     175     10433855 :                const BitBoard pathOccupancy = (kingPath | BBTools::between(to, CastlingTraits<mt>::rookLanding) | CastlingTraits<mt>::rookLandingBB) 
     176     10433855 :                                               & ~BBTools::mask[to].bbsquare & ~BBTools::mask[p.king[c]].bbsquare;
     177     10595888 :                if ((pathOccupancy & occupancy) == emptyBitBoard && !isAttacked(p, kingPath | SquareToBitboard(p.king[c]))){
     178       139991 :                   addMove(from, to, mt, moves);
     179              :                }
     180              :             }
     181              :          };
     182              : 
     183      7226436 :          if (side == Co_White) {
     184      1259301 :             addIfCastlingOk.template operator()<T_wqs>();
     185      1259301 :             addIfCastlingOk.template operator()<T_wks>();
     186              :          }
     187              :          else {
     188      5967135 :             addIfCastlingOk.template operator()<T_bqs>();
     189      5967135 :             addIfCastlingOk.template operator()<T_bks>();
     190              :          }
     191              :       }
     192              :    }
     193              :    else {
     194              :       BitBoard pawnmoves = emptyBitBoard;
     195     70403528 :       if (phase != GP_quiet) pawnmoves = BBTools::mask[from].pawnAttack[p.c] & ~myPieceBB & oppPieceBB;
     196      3768645 :       if (phase == GP_evasion) pawnmoves &= attacker;
     197     76627584 :       BB::applyOn(pawnmoves, [&](const Square & to){
     198      3112028 :          if ((SquareToBitboard(to) & BB::rank1_or_rank8) == emptyBitBoard) addMove(from, to, T_capture, moves);
     199              :          else {
     200        31183 :             addMove(from, to, T_cappromq, moves); // pawn capture with promotion
     201        31183 :             addMove(from, to, T_cappromr, moves); // pawn capture with promotion
     202        31183 :             addMove(from, to, T_cappromb, moves); // pawn capture with promotion
     203        31183 :             addMove(from, to, T_cappromn, moves); // pawn capture with promotion
     204              :          }
     205              :       });
     206              :       
     207              :       pawnmoves = emptyBitBoard;
     208     56351072 :       if (phase != GP_cap) pawnmoves |= BBTools::mask[from].push[p.c] & ~occupancy;
     209     56351072 :       if ((phase != GP_cap) && (BBTools::mask[from].push[p.c] & occupancy) == emptyBitBoard) pawnmoves |= BBTools::mask[from].dpush[p.c] & ~occupancy;
     210      3768645 :       if (phase == GP_evasion) pawnmoves &= sliderRay;
     211    216379554 :       BB::applyOn(pawnmoves, [&](const Square & to){
     212     80014241 :          if ((SquareToBitboard(to) & BB::rank1_or_rank8) == emptyBitBoard) addMove(from, to, T_std, moves);
     213              :          else {
     214        79993 :             addMove(from, to, T_promq, moves); // promotion Q
     215        79993 :             addMove(from, to, T_promr, moves); // promotion R
     216        79993 :             addMove(from, to, T_promb, moves); // promotion B
     217        79993 :             addMove(from, to, T_promn, moves); // promotion N
     218              :          }
     219              :       });
     220              :       
     221              :       ///@todo evasion ep special case ?
     222              :       pawnmoves = emptyBitBoard;
     223     70403528 :       if (p.ep != INVALIDSQUARE && phase != GP_quiet) pawnmoves = BBTools::mask[from].pawnAttack[p.c] & ~myPieceBB & SquareToBitboard(p.ep);
     224     70411458 :       BB::applyOn(pawnmoves, [&](const Square & to){
     225        95488 :          addMove(from, to, T_ep, moves);
     226              :       });
     227              :    }
     228    129641277 : }
     229              : 
     230              : template<GenPhase phase = GP_all> 
     231     10357044 : void generate(const Position& p, MoveList& moves, const bool doNotClear = false) {
     232              :    START_TIMER
     233     10357044 :    if (!doNotClear) moves.clear();
     234     10357044 :    BitBoard myPieceBBiterator = p.allPieces[p.c];
     235    129641277 :    BB::applyOn(myPieceBBiterator, [&](const Square & k){ generateSquare<phase>(p, moves, k); });
     236              : #ifdef DEBUG_GENERATION_LEGAL
     237              :    for (auto m : moves) {
     238              :       if (!isPseudoLegal(p, m)) {
     239              :          Logging::LogIt(Logging::logError) << "Generation error, move not legal " << ToString(p);
     240              :          Logging::LogIt(Logging::logError) << "move " << ToString(m);
     241              :          Logging::LogIt(Logging::logFatal) << "previous " << ToString(p.lastMove);
     242              :          assert(false);
     243              :       }
     244              :    }
     245              : #endif
     246              :    // in anarchy chess, EP is mandatory
     247     10357044 :    if ( DynamicConfig::anarchy && p.ep != INVALIDSQUARE ){ // will be slow ... ///@todo better
     248              :       bool foundEP = false;
     249            0 :       MoveList eps;
     250            0 :       for (auto m : moves){
     251            0 :          if ( Move2Type(m) == T_ep ){
     252              :             foundEP = true;
     253            0 :             eps.emplace_back(m);
     254              :          }
     255              :       }
     256            0 :       if ( foundEP ){
     257              :          moves = eps;
     258              :       }
     259              :    }
     260              :    // in antichess, capture is mandatory
     261     10357044 :    if ( DynamicConfig::antichess ){
     262              :      bool foundCap = false;
     263            0 :       MoveList caps;
     264            0 :       for (auto m : moves){
     265            0 :          if ( isCapture(m) ){
     266              :             foundCap = true;
     267            0 :             caps.emplace_back(m);
     268              :          }
     269              :       }
     270            0 :       if ( foundCap ){
     271              :          moves = caps;
     272              :       }     
     273              :    }
     274              :    STOP_AND_SUM_TIMER(Generate)
     275     10357044 : }
     276              : 
     277              : } // namespace MoveGen
        

Generated by: LCOV version 2.0-1