LCOV - code coverage report
Current view: top level - Source - position.cpp (source / functions) Coverage Total Hit
Test: coverage Lines: 86.8 % 151 131
Test Date: 2026-03-02 16:42:41 Functions: 85.7 % 7 6

            Line data    Source code
       1              : #include "position.hpp"
       2              : 
       3              : #include "bitboardTools.hpp"
       4              : #include "dynamicConfig.hpp"
       5              : #include "hash.hpp"
       6              : #include "logging.hpp"
       7              : #include "material.hpp"
       8              : #include "moveGen.hpp"
       9              : #include "tools.hpp"
      10              : 
      11          450 : template<typename T> T readFromString(const std::string& s) {
      12          450 :    std::stringstream ss(s);
      13              :    T tmp;
      14          450 :    ss >> tmp;
      15          450 :    return tmp;
      16          450 : }
      17              : 
      18          308 : bool readFEN(const std::string& fen, RootPosition& p, bool silent, bool withMoveCount) {
      19          308 :    if (fen.find_first_not_of(' ') == std::string::npos) {
      20            0 :       Logging::LogIt(Logging::logError) << "Empty fen";
      21            0 :       return false;
      22              :    }
      23              : 
      24              :    p.clear(); // "clear" position
      25              : 
      26          308 :    std::vector<std::string> strList;
      27          308 :    std::stringstream iss(fen);
      28          616 :    std::copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(), back_inserter(strList));
      29              : 
      30          365 :    if (!silent) Logging::LogIt(Logging::logInfo) << "Reading fen " << fen;
      31              : 
      32              :    // reset position
      33          308 :    p.h  = nullHash;
      34          308 :    p.ph = nullHash;
      35        20020 :    for (Square k = 0; k < NbSquare; ++k) p.board(k) = P_none;
      36              : 
      37              :    Square j = 1;
      38              :    Square i = 0;
      39        12419 :    while ((j <= NbSquare) && (i < (char)strList[0].length())) {
      40        12111 :       char letter = strList[0].at(i);
      41        12111 :       ++i;
      42        12111 :       const Square k = static_cast<Square>((7 - (j - 1) / 8) * 8 + ((j - 1) % 8));
      43        12111 :       switch (letter) {
      44         1313 :          case 'p': p.board(k) = P_bp; break;
      45          370 :          case 'r': p.board(k) = P_br; break;
      46          282 :          case 'n': p.board(k) = P_bn; break;
      47          382 :          case 'b': p.board(k) = P_bb; break;
      48          144 :          case 'q': p.board(k) = P_bq; break;
      49          308 :          case 'k':
      50          308 :             p.board(k) = P_bk;
      51          308 :             p.king[Co_Black] = k;
      52          308 :             break;
      53         1379 :          case 'P': p.board(k) = P_wp; break;
      54          482 :          case 'R': p.board(k) = P_wr; break;
      55          462 :          case 'N': p.board(k) = P_wn; break;
      56          224 :          case 'B': p.board(k) = P_wb; break;
      57          155 :          case 'Q': p.board(k) = P_wq; break;
      58          308 :          case 'K':
      59          308 :             p.board(k) = P_wk;
      60          308 :             p.king[Co_White] = k;
      61          308 :             break;
      62         2156 :          case '/': --j; break;
      63              :          case '1': break;
      64          874 :          case '2': ++j; break;
      65          596 :          case '3': j += 2; break;
      66          410 :          case '4': j += 3; break;
      67          184 :          case '5': j += 4; break;
      68          126 :          case '6': j += 5; break;
      69           92 :          case '7': j += 6; break;
      70          649 :          case '8': j += 7; break;
      71            0 :          default: Logging::LogIt(Logging::logError) << "FEN ERROR -1 : invalid character in fen string :" << letter << "\n" << fen; return false;
      72              :       }
      73        12111 :       ++j;
      74              :    }
      75              : 
      76          308 :    if (DynamicConfig::isKingMandatory() && (p.king[Co_White] == INVALIDSQUARE || p.king[Co_Black] == INVALIDSQUARE)) {
      77            0 :       Logging::LogIt(Logging::logError) << "FEN ERROR 0 : missing king";
      78            0 :       return false;
      79              :    }
      80              : 
      81          308 :    p.c = Co_White; // set the turn; default is white
      82          308 :    if (strList.size() >= 2) {
      83          308 :       if (strList[1] == "w") p.c = Co_White;
      84          117 :       else if (strList[1] == "b")
      85          117 :          p.c = Co_Black;
      86              :       else {
      87            0 :          Logging::LogIt(Logging::logError) << "FEN ERROR 1 : bad Color";
      88            0 :          return false;
      89              :       }
      90              :    }
      91              : 
      92              :    // Initialize all castle possibilities (default is none)
      93          308 :    p.castling = C_none;
      94          308 :    if (strList.size() >= 3) {
      95              :       bool found = false;
      96              :       //Logging::LogIt(Logging::logInfo) << "is not FRC";
      97          308 :       if (strList[2].find('K') != std::string::npos) {
      98              :          p.castling |= C_wks;
      99              :          found = true;
     100              :       }
     101          308 :       if (strList[2].find('Q') != std::string::npos) {
     102              :          p.castling |= C_wqs;
     103              :          found = true;
     104              :       }
     105          308 :       if (strList[2].find('k') != std::string::npos) {
     106              :          p.castling |= C_bks;
     107              :          found = true;
     108              :       }
     109          308 :       if (strList[2].find('q') != std::string::npos) {
     110              :          p.castling |= C_bqs;
     111              :          found = true;
     112              :       }
     113              : 
     114          237 :       if (!found) {
     115          378 :          for (const char& cr : strList[2]) {
     116          192 :             if ((cr >= 'A' && cr <= 'H') || (cr >= 'a' && cr <= 'h')) {
     117              :                //Logging::LogIt(Logging::logInfo) << "Found FRC like castling " << cr;
     118            8 :                const Color c  = std::isupper(cr) ? Co_White : Co_Black;
     119            8 :                const char  kf = static_cast<char>(std::toupper(FileNames[SQFILE(p.king[c])].at(0)));
     120            8 :                if (std::toupper(cr) > kf) { p.castling |= (c == Co_White ? C_wks : C_bks); }
     121              :                else {
     122            4 :                   p.castling |= (c == Co_White ? C_wqs : C_bqs);
     123              :                }
     124            8 :                if (found && !DynamicConfig::FRC) {
     125            0 :                   Logging::LogIt(Logging::logInfo) << "FRC position found (castling letters), activating FRC";
     126            0 :                   DynamicConfig::FRC = true; // force FRC !
     127              :                }
     128              :                found = true;
     129              :             }
     130              :          }
     131              :       }
     132              :       bool noCastling = false;
     133          308 :       if (strList[2].find('-') != std::string::npos) {
     134              :          found = true;
     135              :          noCastling = true;
     136              :          /*Logging::LogIt(Logging::logInfo) << "No castling right given" ;*/
     137              :       }
     138          308 :       if (!found) {
     139            0 :          if (!silent) Logging::LogIt(Logging::logWarn) << "No castling right given";
     140              :       }
     141              :       else { ///@todo detect illegal stuff in here
     142              :          bool possibleFRC = false;
     143          308 :          p.rootInfo().kingInit[Co_White] = p.king[Co_White];
     144          308 :          p.rootInfo().kingInit[Co_Black] = p.king[Co_Black];
     145          308 :          if (p.king[Co_White] != Sq_e1 && p.castling & C_w_all) possibleFRC = true;
     146          308 :          if (p.king[Co_Black] != Sq_e8 && p.castling & C_b_all) possibleFRC = true;
     147          308 :          if (p.castling & C_wqs) {
     148          107 :             for (Square s = Sq_a1; s <= Sq_h1; ++s) {
     149          107 :                if (s < p.king[Co_White] && p.board_const(s) == P_wr) {
     150          107 :                   p.rootInfo().rooksInit[Co_White][CT_OOO] = s;
     151          107 :                   if (s != Sq_a1) possibleFRC = true;
     152              :                   break;
     153              :                }
     154              :             }
     155              :          }
     156          308 :          if (p.castling & C_wks) {
     157          928 :             for (Square s = Sq_a1; s <= Sq_h1; ++s) {
     158          928 :                if (s > p.king[Co_White] && p.board_const(s) == P_wr) {
     159          116 :                   p.rootInfo().rooksInit[Co_White][CT_OO] = s;
     160          116 :                   if (s != Sq_h1) possibleFRC = true;
     161              :                   break;
     162              :                }
     163              :             }
     164              :          }
     165          308 :          if (p.castling & C_bqs) {
     166           73 :             for (Square s = Sq_a8; s <= Sq_h8; ++s) {
     167           73 :                if (s < p.king[Co_Black] && p.board_const(s) == P_br) {
     168           73 :                   p.rootInfo().rooksInit[Co_Black][CT_OOO] = s;
     169           73 :                   if (s != Sq_a8) possibleFRC = true;
     170              :                   break;
     171              :                }
     172              :             }
     173              :          }
     174          308 :          if (p.castling & C_bks) {
     175          648 :             for (Square s = Sq_a8; s <= Sq_h8; ++s) {
     176          648 :                if (s > p.king[Co_Black] && p.board_const(s) == P_br) {
     177           81 :                   p.rootInfo().rooksInit[Co_Black][CT_OO] = s;
     178           81 :                   if (s != Sq_h8) possibleFRC = true;
     179              :                   break;
     180              :                }
     181              :             }
     182              :          }
     183          308 :          if (possibleFRC && !noCastling && !DynamicConfig::FRC) {
     184            0 :             Logging::LogIt(Logging::logInfo) << "FRC position found (pieces position), activating FRC";
     185            0 :             DynamicConfig::FRC = true; // force FRC !
     186              :          }
     187              :       }
     188              :    }
     189            0 :    else if (!silent)
     190            0 :       Logging::LogIt(Logging::logInfo) << "No castling right given";
     191              : 
     192              :    /*
     193              :    std::cout << SquareNames[p.rootInfo().kingInit[Co_White]] << std::endl;
     194              :    std::cout << SquareNames[p.rootInfo().kingInit[Co_Black]] << std::endl;
     195              :    std::cout << SquareNames[p.rootInfo().rooksInit[Co_White][CT_OOO]] << std::endl;
     196              :    std::cout << SquareNames[p.rootInfo().rooksInit[Co_White][CT_OO]] << std::endl;
     197              :    std::cout << SquareNames[p.rootInfo().rooksInit[Co_Black][CT_OOO]] << std::endl;
     198              :    std::cout << SquareNames[p.rootInfo().rooksInit[Co_Black][CT_OO]] << std::endl;
     199              :    */
     200              : 
     201              :    // read en passant and save it (default is invalid)
     202          308 :    p.ep = INVALIDSQUARE;
     203          308 :    if ((strList.size() >= 4) && strList[3] != "-") {
     204            2 :       if (strList[3].length() >= 2) {
     205            2 :          if ((strList[3].at(0) >= 'a') && (strList[3].at(0) <= 'h') && ((strList[3].at(1) == '3') || (strList[3].at(1) == '6')))
     206            2 :             p.ep = stringToSquare(strList[3]);
     207              :          else {
     208            0 :             Logging::LogIt(Logging::logError) << "FEN ERROR 2 : bad en passant square : " << strList[3];
     209            0 :             return false;
     210              :          }
     211              :       }
     212              :       else {
     213            0 :          Logging::LogIt(Logging::logError) << "FEN ERROR 3 : bad en passant square : " << strList[3];
     214            0 :          return false;
     215              :       }
     216              :    }
     217          306 :    else if (!silent)
     218           57 :       Logging::LogIt(Logging::logDebug) << "No en passant square given";
     219              : 
     220          308 :    assert(p.ep == INVALIDSQUARE || (SQRANK(p.ep) == 2 || SQRANK(p.ep) == 5));
     221              : 
     222              :    // read 50 moves rules
     223          308 :    if (withMoveCount && strList.size() >= 5) p.fifty = static_cast<decltype(p.fifty)>(readFromString<int>(strList[4]));
     224              :    else
     225           83 :       p.fifty = 0;
     226              : 
     227              :    // read number of move
     228          308 :    if (withMoveCount && strList.size() >= 6) p.moves = static_cast<decltype(p.moves)>(readFromString<int>(strList[5]));
     229              :    else
     230           83 :       p.moves = 1;
     231              : 
     232          308 :    if (p.moves == 0) { // fix a LittleBlitzer bug here ...
     233            0 :       Logging::LogIt(Logging::logInfo) << "Wrong move counter " << static_cast<int>(p.moves) << " using 1 instead";
     234            0 :       p.moves = 1;
     235              :    }
     236              : 
     237          308 :    p.halfmoves = static_cast<decltype(p.halfmoves)>((p.moves - 1) * 2 + 1 + (p.c == Co_Black ? 1 : 0));
     238              : 
     239          308 :    BBTools::setBitBoards(p);
     240          308 :    MaterialHash::initMaterial(p);
     241          308 :    p.h  = computeHash(p);
     242          308 :    p.ph = computePHash(p);
     243              : 
     244              : #ifdef WITH_NNUE
     245              :    // If position is associated with an NNUE evaluator,
     246              :    // we reset the evaluator using the new position state.
     247          308 :    if (DynamicConfig::useNNUE && p.hasEvaluator()) p.resetNNUEEvaluator(p.evaluator());
     248              : #endif
     249              : 
     250          308 :    p.rootInfo().initCaslingPermHashTable();
     251              : 
     252          308 :    return true;
     253          308 : }
     254              : 
     255          308 : void RootInformation::initCaslingPermHashTable() {
     256              :    // works also for FRC !
     257              :    castlePermHashTable.fill(C_all);
     258          308 :    if (isValidSquare(kingInit[Co_White])){
     259          308 :        castlePermHashTable[kingInit[Co_White]] = C_all_but_w;
     260          308 :        if (isValidSquare(rooksInit[Co_White][CT_OOO])) castlePermHashTable[rooksInit[Co_White][CT_OOO]] = C_all_but_wqs;
     261          308 :        if (isValidSquare(rooksInit[Co_White][CT_OO] )) castlePermHashTable[rooksInit[Co_White][CT_OO]]  = C_all_but_wks;
     262              :    }
     263          308 :    if (isValidSquare(kingInit[Co_Black])){
     264          308 :        castlePermHashTable[kingInit[Co_Black]] = C_all_but_b;
     265          308 :        if (isValidSquare(rooksInit[Co_Black][CT_OOO])) castlePermHashTable[rooksInit[Co_Black][CT_OOO]] = C_all_but_bqs;
     266          308 :        if (isValidSquare(rooksInit[Co_Black][CT_OO] )) castlePermHashTable[rooksInit[Co_Black][CT_OO]]  = C_all_but_bks;
     267              :    }
     268          308 : }
     269              : 
     270    180142912 : Position::~Position() {
     271              :    //I do not delete the root pointer, only a RootPosition can do that
     272    180142912 : }
     273              : 
     274       160336 : Position::Position() {}
     275              : 
     276              : /*
     277              : #include <immintrin.h>
     278              : 
     279              : Position & Position::operator=(const Position & p){
     280              :    if(&p != this){
     281              :       constexpr size_t n = 7; // => sizeof(Position) == 224b
     282              :       static_assert(sizeof(Position) == n*sizeof(__m256i));
     283              :       __m256i * me_256 = reinterpret_cast<__m256i *>(this);
     284              :       const __m256i * other_256 = reinterpret_cast<const __m256i *>(&p);
     285              :       for(size_t i = 0; i < n; ++i, ++me_256, ++other_256){
     286              :          _mm256_store_si256(me_256, _mm256_load_si256(other_256));
     287              :       }
     288              :    }
     289              :    return *this;
     290              : }
     291              : 
     292              : Position::Position(const Position& p) {
     293              :    if(&p != this){
     294              :       this->operator=(p);     
     295              :    }
     296              : }
     297              : */
     298              : 
     299            2 : RootPosition::RootPosition(const std::string& fen, bool withMoveCount) : RootPosition() {
     300            2 :    readFEN(fen, *this, true, withMoveCount);
     301            2 : }
     302              : 
        

Generated by: LCOV version 2.0-1