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

            Line data    Source code
       1              : #include "moveApply.hpp"
       2              : 
       3              : #include "attack.hpp"
       4              : #include "bitboard.hpp"
       5              : #include "bitboardTools.hpp"
       6              : #include "hash.hpp"
       7              : #include "material.hpp"
       8              : #include "movePseudoLegal.hpp"
       9              : #include "position.hpp"
      10              : #include "tools.hpp"
      11              : 
      12    179492925 : MoveInfo::MoveInfo(const Position& p, const Move _m):
      13    179492925 :     m(_m),
      14    179492925 :     from(Move2From(_m)),
      15    179492925 :     to(correctedMove2ToKingDest(_m)),
      16    179492925 :     king{p.king[Co_White], p.king[Co_Black]},
      17    179492925 :     ep(p.ep),
      18    179492925 :     type(Move2Type(_m)),
      19    179492925 :     fromP(p.board_const(from)),
      20    179492925 :     toP(p.board_const(to)), // won't be used for EP
      21    179492925 :     fromId(PieceIdx(fromP)),
      22    179492925 :     isCapNoEP(toP != P_none) {
      23    179492925 :     assert(isValidSquare(from));
      24    179492925 :     assert(isValidSquare(to));
      25              :     assert(isValidMoveType(type));
      26    179492925 : }
      27              : 
      28              : template<Color c> 
      29              : FORCE_FINLINE void movePieceCastle(Position& p, const CastlingTypes ct, const Square kingDest, const Square rookDest) {
      30              :    START_TIMER
      31              :    constexpr Piece          pk = c == Co_White ? P_wk  : P_bk;
      32              :    constexpr Piece          pr = c == Co_White ? P_wr  : P_br;
      33              :    constexpr CastlingRights ks = c == Co_White ? C_wks : C_bks;
      34              :    constexpr CastlingRights qs = c == Co_White ? C_wqs : C_bqs;
      35       139703 :    BBTools::unSetBit(p, p.king[c]);
      36       139703 :    BB::_unSetBit(p.allPieces[c], p.king[c]);
      37       139703 :    BBTools::unSetBit(p, p.rootInfo().rooksInit[c][ct]);
      38       139703 :    BB::_unSetBit(p.allPieces[c], p.rootInfo().rooksInit[c][ct]);
      39              :    BBTools::setBit(p, kingDest, pk);
      40              :    BB::_setBit(p.allPieces[c], kingDest);
      41              :    BBTools::setBit(p, rookDest, pr);
      42              :    BB::_setBit(p.allPieces[c], rookDest);
      43       139703 :    p.board(p.king[c]) = P_none;
      44       139703 :    p.board(p.rootInfo().rooksInit[c][ct]) = P_none;
      45       139703 :    p.board(kingDest) = pk;
      46       139703 :    p.board(rookDest) = pr;
      47       139703 :    p.h  ^= Zobrist::ZT[p.king[c]][pk + PieceShift];
      48       139703 :    p.ph ^= Zobrist::ZT[p.king[c]][pk + PieceShift];
      49       139703 :    p.h  ^= Zobrist::ZT[p.rootInfo().rooksInit[c][ct]][pr + PieceShift];
      50       139703 :    p.h  ^= Zobrist::ZT[kingDest][pk + PieceShift];
      51       139703 :    p.ph ^= Zobrist::ZT[kingDest][pk + PieceShift];
      52       139703 :    p.h  ^= Zobrist::ZT[rookDest][pr + PieceShift];
      53       139703 :    p.king[c] = kingDest;
      54       139703 :    p.h  ^= Zobrist::ZTCastling[p.castling];
      55              :    p.castling &= ~(ks | qs);
      56       139703 :    p.h  ^= Zobrist::ZTCastling[p.castling];
      57              :    STOP_AND_SUM_TIMER(MovePiece)
      58       139703 : }
      59              : 
      60              : 
      61    179272992 : void movePiece(Position& p, const Square from, const Square to, const Piece fromP, const Piece toP, const bool isCapture = false, const Piece prom = P_none) {
      62    179272992 :    assert(isValidSquare(from));
      63    179272992 :    assert(isValidSquare(to));
      64            0 :    assert(isValidPieceNotNone(fromP));
      65              :    START_TIMER
      66              :    const int   fromId  = PieceIdx(fromP);
      67              :    const int   toId    = PieceIdx(toP);
      68    179272992 :    const Piece toPnew  = prom != P_none ? prom : fromP;
      69              :    const int   toIdnew = PieceIdx(toPnew);
      70              :    // update board
      71    179272992 :    p.board(from) = P_none;
      72    179272992 :    p.board(to)   = toPnew;
      73              :    // update bitboard
      74              :    BBTools::unSetBit(p, from, fromP);
      75    179272992 :    BB::_unSetBit(p.allPieces[p.c], from);
      76    179272992 :    if (toP != P_none) {
      77              :       BBTools::unSetBit(p, to, toP);
      78     10789606 :       BB::_unSetBit(p.allPieces[~p.c], to);
      79              :    }
      80              :    BBTools::setBit(p, to, toPnew);
      81              :    BB::_setBit(p.allPieces[p.c], to);
      82              :    // update Zobrist hash
      83    179272992 :    p.h ^= Zobrist::ZT[from][fromId]; // remove fromP at from
      84    179272992 :    p.h ^= Zobrist::ZT[to][toIdnew];  // add fromP (or prom) at to
      85              :    // piece that have influence on pawn hash
      86              :    static constexpr array1d<bool,NbPiece> helperPawnHash_ = {true, false, false, false, false, true, false, true, false, false, false, false, true};
      87    179272992 :    if (helperPawnHash_[fromId]) {
      88     94089875 :       p.ph ^= Zobrist::ZT[from][fromId];                    // remove fromP at from
      89     94089875 :       if (prom == P_none) p.ph ^= Zobrist::ZT[to][toIdnew]; // add fromP (if not prom) at to
      90              :    }
      91    179272992 :    if (isCapture) { // if capture remove toP at to
      92     10789606 :       p.h ^= Zobrist::ZT[to][toId];
      93     10789606 :       if (abs(toP) == P_wp || abs(toP) == P_wk) p.ph ^= Zobrist::ZT[to][toId];
      94              :    }
      95              :    // consequences on castling
      96    328225352 :    if (p.castling && (p.rootInfo().castlePermHashTable[from] ^ p.rootInfo().castlePermHashTable[to])) {
      97      9919325 :       p.h ^= Zobrist::ZTCastling[p.castling];
      98              :       p.castling &= (p.rootInfo().castlePermHashTable[from] & p.rootInfo().castlePermHashTable[to]);
      99      9919325 :       p.h ^= Zobrist::ZTCastling[p.castling];
     100              :    }
     101              : 
     102              :    // update king position
     103    179272992 :    if (fromP == P_wk) p.king[Co_White] = to;
     104    176282919 :    else if (fromP == P_bk)
     105      9044008 :       p.king[Co_Black] = to;
     106              : 
     107              :    // king capture : this can append in some chess variants
     108    179272992 :    if (toP == P_wk) p.king[Co_White] = INVALIDSQUARE;
     109    179272992 :    else if (toP == P_bk) p.king[Co_Black] = INVALIDSQUARE;
     110              : 
     111              :    STOP_AND_SUM_TIMER(MovePiece)
     112    179272992 : }
     113              : 
     114       491394 : void applyNull(Searcher&, Position& pN) {
     115              :    START_TIMER
     116       491394 :    pN.c = ~pN.c;
     117       491394 :    pN.h ^= Zobrist::ZT[3][13];
     118       491394 :    pN.h ^= Zobrist::ZT[4][13];
     119       491394 :    if (pN.ep != INVALIDSQUARE) pN.h ^= Zobrist::ZT[pN.ep][13];
     120       491394 :    pN.ep = INVALIDSQUARE;
     121       491394 :    pN.lastMove = NULLMOVE;
     122       491394 :    if (pN.c == Co_White) ++pN.moves;
     123       491394 :    ++pN.halfmoves;
     124              :    STOP_AND_SUM_TIMER(Apply)
     125       491394 : }
     126              : 
     127    179492925 : bool applyMove(Position& p, const MoveInfo & moveInfo, [[maybe_unused]] const bool noNNUEUpdate) {
     128            0 :    assert(isValidMove(moveInfo.m));
     129              :    START_TIMER
     130              : #ifdef DEBUG_MATERIAL
     131              :    Position previous = p;
     132              : #endif
     133              : #ifdef DEBUG_APPLY
     134              :    if (!isPseudoLegal(p, moveInfo.m)) {
     135              :       Logging::LogIt(Logging::logError) << ToString(moveInfo.m) << " " << moveInfo.type;
     136              :       Logging::LogIt(Logging::logFatal) << "Apply error, not legal " << ToString(p);
     137              :       assert(false);
     138              :    }
     139              : #endif
     140              : 
     141    179492925 :    switch (moveInfo.type) {
     142              :       case T_reserved:
     143            0 :          Logging::LogIt(Logging::logError) << ToString(moveInfo.m) << " " << static_cast<int>(moveInfo.type);
     144            0 :          Logging::LogIt(Logging::logFatal) << "Apply error, move is not legal (reserved). " << ToString(p);
     145            0 :          break; 
     146    178850098 :       case T_std:
     147              :       case T_capture:
     148              :          // update material
     149    178850098 :          if (moveInfo.isCapNoEP) { --p.mat[~p.c][Abs(moveInfo.toP)]; }
     150              :          // update hash, BB, board and castling rights
     151    178850098 :          movePiece(p, moveInfo.from, moveInfo.to, moveInfo.fromP, moveInfo.toP, moveInfo.type == T_capture);
     152    178850098 :          break;
     153        80230 :       case T_ep:
     154              :          {
     155        80230 :             assert(p.ep != INVALIDSQUARE);
     156        80230 :             assert(SQRANK(p.ep) == EPRank[p.c]);
     157        80230 :             const auto epCapSq = static_cast<Square>(p.ep + (p.c == Co_White ? -8 : +8));
     158        80230 :             assert(isValidSquare(epCapSq));
     159        80230 :             BBTools::unSetBit(p, epCapSq, ~moveInfo.fromP); // BEFORE setting p.b new shape !!!
     160        80230 :             BB::_unSetBit(p.allPieces[~p.c], epCapSq);
     161        80230 :             BBTools::unSetBit(p, moveInfo.from, moveInfo.fromP);
     162              :             BB::_unSetBit(p.allPieces[p.c], moveInfo.from);
     163        80230 :             BBTools::setBit(p, moveInfo.to, moveInfo.fromP);
     164              :             BB::_setBit(p.allPieces[p.c], moveInfo.to);
     165        80230 :             p.board(moveInfo.from)    = P_none;
     166        80230 :             p.board(moveInfo.to)      = moveInfo.fromP;
     167        80230 :             p.board(epCapSq) = P_none;
     168              : 
     169        80230 :             p.h ^= Zobrist::ZT[moveInfo.from][moveInfo.fromId];                     // remove fromP at from
     170        80230 :             p.h ^= Zobrist::ZT[epCapSq][PieceIdx(p.c == Co_White ? P_bp : P_wp)];   // remove captured pawn
     171        80230 :             p.h ^= Zobrist::ZT[moveInfo.to][moveInfo.fromId];                       // add fromP at to
     172              : 
     173        80230 :             p.ph ^= Zobrist::ZT[moveInfo.from][moveInfo.fromId];                    // remove fromP at from
     174        80230 :             p.ph ^= Zobrist::ZT[epCapSq][PieceIdx(p.c == Co_White ? P_bp : P_wp)];  // remove captured pawn
     175        80230 :             p.ph ^= Zobrist::ZT[moveInfo.to][moveInfo.fromId];                      // add fromP at to
     176              : 
     177        80230 :             --p.mat[~p.c][M_p];
     178              :          }
     179        80230 :          break;
     180       107590 :       case T_promq:
     181              :       case T_cappromq:
     182       107590 :          MaterialHash::updateMaterialProm(p, moveInfo.to, moveInfo.type);
     183       128901 :          movePiece(p, moveInfo.from, moveInfo.to, moveInfo.fromP, moveInfo.toP, moveInfo.type == T_cappromq, (p.c == Co_White ? P_wq : P_bq));
     184       107590 :          break;
     185       105128 :       case T_promr:
     186              :       case T_cappromr:
     187       105128 :          MaterialHash::updateMaterialProm(p, moveInfo.to, moveInfo.type);
     188       126053 :          movePiece(p, moveInfo.from, moveInfo.to, moveInfo.fromP, moveInfo.toP, moveInfo.type == T_cappromr, (p.c == Co_White ? P_wr : P_br));
     189       105128 :          break;
     190       105089 :       case T_promb:
     191              :       case T_cappromb:
     192       105089 :          MaterialHash::updateMaterialProm(p, moveInfo.to, moveInfo.type);
     193       126014 :          movePiece(p, moveInfo.from, moveInfo.to, moveInfo.fromP, moveInfo.toP, moveInfo.type == T_cappromb, (p.c == Co_White ? P_wb : P_bb));
     194       105089 :          break;
     195       105087 :       case T_promn:
     196              :       case T_cappromn:
     197       105087 :          MaterialHash::updateMaterialProm(p, moveInfo.to, moveInfo.type);
     198       126012 :          movePiece(p, moveInfo.from, moveInfo.to, moveInfo.fromP, moveInfo.toP, moveInfo.type == T_cappromn, (p.c == Co_White ? P_wn : P_bn));
     199       105087 :          break;
     200              :       case T_wks: movePieceCastle<Co_White>(p, CT_OO,  Sq_g1, Sq_f1); break;
     201              :       case T_wqs: movePieceCastle<Co_White>(p, CT_OOO, Sq_c1, Sq_d1); break;
     202              :       case T_bks: movePieceCastle<Co_Black>(p, CT_OO,  Sq_g8, Sq_f8); break;
     203              :       case T_bqs: movePieceCastle<Co_Black>(p, CT_OOO, Sq_c8, Sq_d8); break;
     204              :       case T_max: break;
     205              :    }
     206              : 
     207    179492925 :    if (/*!noValidation &&*/ isPosInCheck(p)) {
     208              :       STOP_AND_SUM_TIMER(Apply)
     209              :       return false; // this is the only legal move validation needed
     210              :    }
     211              : 
     212    175492987 :    const bool pawnMove = abs(moveInfo.fromP) == P_wp;
     213              :    // update EP
     214    175492987 :    if (p.ep != INVALIDSQUARE) p.h ^= Zobrist::ZT[p.ep][13];
     215    175492987 :    p.ep = INVALIDSQUARE;
     216    209330229 :    if (pawnMove && abs(moveInfo.to - moveInfo.from) == 16 && (BBTools::adjacent(SquareToBitboard(moveInfo.to)) & p.pieces_const(~p.c,P_wp)) != emptyBitBoard) {
     217      1125149 :       p.ep = static_cast<Square>((moveInfo.from + moveInfo.to) / 2);
     218      1125149 :       p.h ^= Zobrist::ZT[p.ep][13];
     219              :    }
     220    175492987 :    assert(p.ep == INVALIDSQUARE || SQRANK(p.ep) == EPRank[~p.c]);
     221              : 
     222              :    // update Color
     223    175492987 :    p.c = ~p.c;
     224    175492987 :    p.h ^= Zobrist::ZT[3][13];
     225    175492987 :    p.h ^= Zobrist::ZT[4][13];
     226              : 
     227              :    // update game state
     228    175492987 :    if (moveInfo.isCapNoEP || pawnMove) p.fifty = 0; // E.p covered by pawn move here ...
     229              :    else
     230     86271261 :       ++p.fifty;
     231    175492987 :    if (p.c == Co_White) ++p.moves;
     232    175492987 :    ++p.halfmoves;
     233              : 
     234    175492987 :    if (isCaptureOrProm(moveInfo.type)) MaterialHash::updateMaterialOther(p);
     235              : 
     236              : #ifdef WITH_NNUE
     237    175492987 :    if (!noNNUEUpdate){
     238    161409957 :       applyMoveNNUEUpdate(p, moveInfo);
     239              :    }
     240              : #endif
     241              : 
     242              : #ifdef DEBUG_MATERIAL
     243              :    const Position::Material mat = p.mat;
     244              :    MaterialHash::initMaterial(p);
     245              :    if (p.mat != mat) {
     246              :       Logging::LogIt(Logging::logWarn) << "Material update error";
     247              :       Logging::LogIt(Logging::logWarn) << "Material previous " << ToString(previous) << ToString(previous.mat);
     248              :       Logging::LogIt(Logging::logWarn) << "Material computed " << ToString(p) << ToString(p.mat);
     249              :       Logging::LogIt(Logging::logWarn) << "Material incrementally updated " << ToString(mat);
     250              :       Logging::LogIt(Logging::logFatal) << "Last move " << ToString(p.lastMove) << " current move " << ToString(moveInfo.m);
     251              :    }
     252              : #endif
     253              : 
     254              : #ifdef DEBUG_BITBOARD
     255              :    int count_bb1   = BB::countBit(p.occupancy());
     256              :    int count_bb2   = 0;
     257              :    int count_board = 0;
     258              :    for (Square s = Sq_a1; s <= Sq_h8; ++s) {
     259              :       if (p.board_const(s) != P_none) ++count_board;
     260              :    }
     261              :    for (Piece pp = P_bk; pp <= P_wk; ++pp) {
     262              :       if (pp == P_none) continue;
     263              :       BitBoard       b  = p.pieces_const(pp);
     264              :       const BitBoard bb = b;
     265              :       applyOn(b, [&](const Square & k){ {
     266              :          ++count_bb2;
     267              :          if (p.board_const(k) != pp) {
     268              :             Logging::LogIt(Logging::logWarn) << SquareNames[k];
     269              :             Logging::LogIt(Logging::logWarn) << ToString(p);
     270              :             Logging::LogIt(Logging::logWarn) << ToString(bb);
     271              :             Logging::LogIt(Logging::logWarn) << static_cast<int>(pp);
     272              :             Logging::LogIt(Logging::logWarn) << static_cast<int>(p.board_const(k));
     273              :             Logging::LogIt(Logging::logWarn) << "last move " << ToString(p.lastMove);
     274              :             Logging::LogIt(Logging::logWarn) << " current move " << ToString(moveInfo.m);
     275              :             Logging::LogIt(Logging::logFatal) << "Wrong bitboard ";
     276              :          }
     277              :       });
     278              :    }
     279              :    if (count_bb1 != count_bb2) {
     280              :       Logging::LogIt(Logging::logFatal) << "Wrong bitboard count (all/piece)" << count_bb1 << " " << count_bb2;
     281              :       Logging::LogIt(Logging::logWarn) << ToString(p);
     282              :       Logging::LogIt(Logging::logWarn) << ToString(p.occupancy());
     283              :       for (Piece pp = P_bk; pp <= P_wk; ++pp) {
     284              :          if (pp == P_none) continue;
     285              :          Logging::LogIt(Logging::logWarn) << ToString(p.pieces_const(pp));
     286              :       }
     287              :    }
     288              :    if (count_board != count_bb1) {
     289              :       Logging::LogIt(Logging::logFatal) << "Wrong bitboard count (board)" << count_board << " " << count_bb1;
     290              :       Logging::LogIt(Logging::logWarn) << ToString(p);
     291              :       Logging::LogIt(Logging::logWarn) << ToString(p.occupancy());
     292              :    }
     293              : #endif
     294    175492987 :    p.lastMove = Move2MiniMove(moveInfo.m);
     295              :    STOP_AND_SUM_TIMER(Apply)
     296    175492987 :    return true;
     297              : }
     298              : 
     299    166812699 : void applyMoveNNUEUpdate([[maybe_unused]] Position & p, [[maybe_unused]] const MoveInfo & moveInfo){
     300              : #ifdef WITH_NNUE
     301    166812699 :    if (!DynamicConfig::useNNUE) return;
     302              :    // if king is not moving, update nnue evaluator
     303              :    // this is based on initial position state (most notably ep square), 
     304              :    // all is available in the previously built moveInfo)
     305    166812699 :    if (Abs(moveInfo.fromP) != P_wk || (getBlock(moveInfo.from) == getBlock(moveInfo.to))) {
     306              :       // ***be carefull here***, p.c has already been updated !!!
     307    185851172 :       if (p.c == Co_Black) updateNNUEEvaluator<Co_White>(p.evaluator(), moveInfo);
     308    130788956 :       else updateNNUEEvaluator<Co_Black>(p.evaluator(), moveInfo);
     309              :    }
     310              :    // if a king was moved (including castling!!), reset nnue evaluator
     311              :    // this is based on new position state !
     312              :    else { 
     313      8492635 :       p.resetNNUEEvaluator(p.evaluator(), ~p.c);
     314      8492635 :       if (isCastling(moveInfo.type)){
     315       139703 :          p.resetNNUEEvaluator(p.evaluator(), p.c);
     316              :       }
     317              :       else{
     318              :          // ***be carefull here***, p.c has already been updated !!!
     319      9948940 :          if (p.c == Co_Black) updateNNUEEvaluatorThemOnly<Co_White>(p.evaluator(), moveInfo);
     320      6756924 :          else updateNNUEEvaluatorThemOnly<Co_Black>(p.evaluator(), moveInfo);
     321              :       }
     322              :    }
     323              : 
     324              : #ifdef DEBUG_NNUE_UPDATE
     325              :    Position      p2 = p;
     326              :    NNUEEvaluator evaluator;
     327              :    p2.associateEvaluator(evaluator);
     328              :    p2.resetNNUEEvaluator(p2.evaluator());
     329              :    if (p2.evaluator() != p.evaluator()) {
     330              :       Logging::LogIt(Logging::logWarn) << "evaluator update error";
     331              :       Logging::LogIt(Logging::logWarn) << ToString(p);
     332              :       Logging::LogIt(Logging::logWarn) << ToString(moveInfo.m);
     333              :       Logging::LogIt(Logging::logWarn) << ToString(p.lastMove);
     334              :       Logging::LogIt(Logging::logWarn) << p2.evaluator().white.active();
     335              :       Logging::LogIt(Logging::logWarn) << p2.evaluator().black.active();
     336              :       Logging::LogIt(Logging::logWarn) << "--------------------";
     337              :       Logging::LogIt(Logging::logWarn) << p.evaluator().white.active();
     338              :       Logging::LogIt(Logging::logWarn) << p.evaluator().black.active();
     339              :       Logging::LogIt(Logging::logWarn) << backtrace();
     340              :    }
     341              : #endif // DEBUG_NNUE_UPDATE
     342              : 
     343              : #endif // WITH_NNUE
     344              : 
     345              : 
     346              : }
        

Generated by: LCOV version 2.0-1