LCOV - code coverage report
Current view: top level - Source - movePseudoLegal.cpp (source / functions) Coverage Total Hit
Test: coverage Lines: 96.4 % 55 53
Test Date: 2026-03-02 16:42:41 Functions: 100.0 % 5 5

            Line data    Source code
       1              : #include "movePseudoLegal.hpp"
       2              : 
       3              : #include "moveGen.hpp"
       4              : #include "position.hpp"
       5              : 
       6              : #ifdef DEBUG_PSEUDO_LEGAL
       7              : bool isPseudoLegal2(const Position& p, Move m); // forward decl
       8              : bool isPseudoLegal(const Position& p, Move m) {
       9              :    const bool b = isPseudoLegal2(p, m);
      10              :    if (b) {
      11              :       MoveList moves;
      12              :       MoveGen::generate<MoveGen::GP_all>(p, moves);
      13              :       bool found = false;
      14              :       for (auto it = moves.begin(); it != moves.end() && !found; ++it)
      15              :          if (sameMove(*it, m)) found = true;
      16              :       if (!found) {
      17              :          std::cout << ToString(p) << "\n" << ToString(m) << "\t" << m << std::endl;
      18              :          std::cout << SquareNames[Move2From(m)] << std::endl;
      19              :          std::cout << SquareNames[Move2To(m)] << std::endl;
      20              :          std::cout << static_cast<int>(Move2Type(m)) << std::endl;
      21              :          std::cout << Move2Score(m) << std::endl;
      22              :          std::cout << static_cast<int>(m & 0x0000FFFF) << std::endl;
      23              :          for (const auto & it : moves) std::cout << ToString(it) << "\t" << *it << "\t";
      24              :          std::cout << std::endl;
      25              :          std::cout << "Not a generated move !" << std::endl;
      26              :       }
      27              :    }
      28              :    return b;
      29              : }
      30              : 
      31              : bool isPseudoLegal2(const Position& p, const Move m) { // validate TT move
      32              : #else
      33      1693146 : bool isPseudoLegal(const Position& p, const Move m) { // validate TT move
      34              : #endif
      35              : #ifdef DEBUG_PSEUDO_LEGAL
      36              : #define PSEUDO_LEGAL_RETURN(b, r)                                \
      37              :    {                                                             \
      38              :       if (!b) std::cout << "isPseudoLegal2: " << r << std::endl; \
      39              :       STOP_AND_SUM_TIMER(PseudoLegal) return b;                  \
      40              :    }
      41              : #else
      42              : #define PSEUDO_LEGAL_RETURN(b, r) \
      43              :    { STOP_AND_SUM_TIMER(PseudoLegal) return b; }
      44              : #endif
      45              :    START_TIMER
      46              : 
      47              :    if (!isValidMove(m)) PSEUDO_LEGAL_RETURN(false, -1)
      48      1693146 :    const MType t = Move2Type(m);
      49              :    
      50              :    if (!isValidMoveType(t)) PSEUDO_LEGAL_RETURN(false, -4)
      51      1693146 :    if (t == T_reserved) PSEUDO_LEGAL_RETURN(false, 3)
      52              :    
      53      1693146 :    const Square from = Move2From(m);
      54      1693146 :    if (!isValidSquare(from)) PSEUDO_LEGAL_RETURN(false, -2)
      55              :    
      56      1693146 :    const Piece fromP = p.board_const(from);
      57      1693146 :    if (fromP == P_none || (fromP > 0 && p.c == Co_Black) || (fromP < 0 && p.c == Co_White)) PSEUDO_LEGAL_RETURN(false, 0)
      58              :    
      59      1693144 :    const Square to = Move2To(m);
      60      1693144 :    if (!isValidSquare(to)) PSEUDO_LEGAL_RETURN(false, -3)
      61              :    
      62              :    // cannot capture own piece, except for castling where it is checked later
      63      1693144 :    const Piece toP = p.board_const(to);
      64      1693144 :    if ((toP > 0 && p.c == Co_White) || (toP < 0 && p.c == Co_Black)){
      65          300 :       if(!DynamicConfig::FRC){
      66          300 :          if (!isCastling(t)) PSEUDO_LEGAL_RETURN(false, 1)
      67              :          else{
      68              :             ;// see castling
      69              :          }
      70              :       }
      71              :       else{
      72            0 :          if (!isCastling(t)) PSEUDO_LEGAL_RETURN(false, 1)
      73              :          else{
      74              :             ;// see castling
      75              :          }
      76              :       }
      77              :    }
      78              : 
      79              :    // opponent king cannot be captured
      80              :    ///@todo variant were king can be captured   
      81      2558421 :    if (toP == (p.c == Co_White ? P_bk : P_wk)) PSEUDO_LEGAL_RETURN(false, 2)
      82              :    
      83              :    // empty destination square cannot be a capture (except EP)
      84      1693143 :    if (toP == P_none && isCaptureNoEP(t)) PSEUDO_LEGAL_RETURN(false, 4)
      85              : 
      86              :    // none empty destination square must be capture, except for castling where it is checked later
      87              :    ///@todo variant ?
      88      1693143 :    if (toP != P_none && !isCapture(t)){
      89          299 :       if (!DynamicConfig::FRC){
      90          299 :          if (!isCastling(t)) PSEUDO_LEGAL_RETURN(false, 5)
      91              :          else{
      92              :             ;// see castling
      93              :          }
      94              :       }
      95              :       else{
      96            0 :          if (!isCastling(t)) PSEUDO_LEGAL_RETURN(false, 5)
      97              :          else{
      98              :             ;// see castling
      99              :          }
     100              :       }
     101              :    }
     102              : 
     103      1693143 :    const Piece fromPieceType = Abs(fromP);
     104              : 
     105              :    // EP comes from a pawn
     106      1693143 :    if (t == T_ep && (p.ep == INVALIDSQUARE || fromPieceType != P_wp)) PSEUDO_LEGAL_RETURN(false, 6)
     107              :    // EP geometry checks
     108      1697109 :    if (t == T_ep && p.board_const(static_cast<Square>(p.ep + (p.c == Co_White ? -8 : +8))) != (p.c == Co_White ? P_bp : P_wp)) PSEUDO_LEGAL_RETURN(false, 7)
     109              : 
     110              :    // Promotion comes from a pawn
     111      1693143 :    if (isPromotion(m) && fromPieceType != P_wp) PSEUDO_LEGAL_RETURN(false, 8)
     112              : 
     113      1693143 :    const BitBoard occupancy = p.occupancy();
     114              : 
     115          597 :    auto isCastlingOk = [&]<MType mt>(){
     116              :       // Be carefull, here rooksInit are only accessed if the corresponding p.castling is set
     117              :       // This way rooksInit is not INVALIDSQUARE, and thus mask[] is not out of bound
     118              :       // Moreover, king[] also is assumed to be not INVALIDSQUARE which is verified in readFen
     119              :       constexpr Color c = CastlingTraits<mt>::c;
     120          299 :       return t == mt && 
     121          299 :              (p.castling & CastlingTraits<mt>::cr) && 
     122          299 :              from == p.rootInfo().kingInit[c] && 
     123          299 :              to == p.rootInfo().rooksInit[c][CastlingTraits<mt>::ct] &&
     124          299 :              fromP == CastlingTraits<mt>::fromP && 
     125          299 :              (((BBTools::between(p.king[c], CastlingTraits<mt>::kingLanding) | CastlingTraits<mt>::kingLandingBB | BBTools::between(to, CastlingTraits<mt>::rookLanding) | CastlingTraits<mt>::rookLandingBB) &
     126          299 :                ~BBTools::mask[to].bbsquare & ~BBTools::mask[p.king[c]].bbsquare) &
     127          896 :               occupancy) == emptyBitBoard &&
     128          299 :              !isAttacked(p, BBTools::between(p.king[c], CastlingTraits<mt>::kingLanding) | SquareToBitboard(p.king[c]) | CastlingTraits<mt>::kingLandingBB);
     129      1693143 :    };
     130              : 
     131      1693143 :    if (isCastling(t)) {
     132          299 :       if (p.c == Co_White) {
     133          238 :          if (isCastlingOk.operator()<T_wqs>()) PSEUDO_LEGAL_RETURN(true, 9)
     134          237 :          if (isCastlingOk.operator()<T_wks>()) PSEUDO_LEGAL_RETURN(true, 10)
     135              :          PSEUDO_LEGAL_RETURN(false, 11)
     136              :       }
     137              :       else {
     138           61 :          if (isCastlingOk.operator()<T_bqs>()) PSEUDO_LEGAL_RETURN(true, 12)
     139           61 :          if (isCastlingOk.operator()<T_bks>()) PSEUDO_LEGAL_RETURN(true, 13)
     140              :          PSEUDO_LEGAL_RETURN(false, 14)
     141              :       }
     142              :    }
     143              : 
     144      1692844 :    if (fromPieceType == P_wp) {
     145              :       // ep must land on good square
     146       373942 :       if (t == T_ep && to != p.ep) PSEUDO_LEGAL_RETURN(false, 15)
     147              : 
     148       373942 :       if (t != T_ep && p.ep != INVALIDSQUARE && to == p.ep) PSEUDO_LEGAL_RETURN(false, 16)
     149              :       // non promotion pawn move cannot reach 8th rank
     150       373942 :       if (!isPromotion(m) && SQRANK(to) == PromRank[p.c]) PSEUDO_LEGAL_RETURN(false, 17)
     151              :       // promotion must be 8th rank
     152       373942 :       if (isPromotion(m) && SQRANK(to) != PromRank[p.c]) PSEUDO_LEGAL_RETURN(false, 18)
     153              : 
     154              :       // (double)pawn push must be legal
     155       373942 :       BitBoard validPush = BBTools::mask[from].push[p.c] & ~occupancy;
     156       373942 :       if ((BBTools::mask[from].push[p.c] & occupancy) == emptyBitBoard) validPush |= BBTools::mask[from].dpush[p.c] & ~occupancy;
     157       373942 :       if (validPush & SquareToBitboard(to)) PSEUDO_LEGAL_RETURN(true, 19)
     158              : 
     159              :       // pawn capture (including ep)
     160       269067 :       const BitBoard validCap = BBTools::mask[from].pawnAttack[p.c] & ~p.allPieces[p.c];
     161       269067 :       if ((validCap & SquareToBitboard(to)) && ((t != T_ep && toP != P_none) || (t == T_ep && to == p.ep && toP == P_none)))
     162              :          PSEUDO_LEGAL_RETURN(true, 20)
     163              : 
     164              :       PSEUDO_LEGAL_RETURN(false, 21)
     165              :    }
     166              : 
     167              :    // possible piece move (excluding king)
     168      1318902 :    if (fromPieceType != P_wk) {
     169       980582 :       if ((BBTools::pfCoverage[fromPieceType - 1](from, occupancy, p.c) & SquareToBitboard(to)) != emptyBitBoard) PSEUDO_LEGAL_RETURN(true, 22)
     170              : 
     171              :       PSEUDO_LEGAL_RETURN(false, 23)
     172              :    }
     173              : 
     174              :    // only king moving is not verified yet
     175       338320 :    if ((BBTools::mask[p.king[p.c]].kingZone & SquareToBitboard(to)) != emptyBitBoard) PSEUDO_LEGAL_RETURN(true, 24)
     176              : 
     177              :    PSEUDO_LEGAL_RETURN(false, 25)
     178              : }
        

Generated by: LCOV version 2.0-1