LCOV - code coverage report
Current view: top level - Source - timeMan.cpp (source / functions) Coverage Total Hit
Test: coverage Lines: 81.8 % 121 99
Test Date: 2026-03-02 16:42:41 Functions: 100.0 % 3 3

            Line data    Source code
       1              : #include "timeMan.hpp"
       2              : 
       3              : #include "dynamicConfig.hpp"
       4              : #include "logging.hpp"
       5              : #include "moveApply.hpp"
       6              : #include "position.hpp"
       7              : #include "searcher.hpp"
       8              : #include "threading.hpp"
       9              : 
      10              : namespace TimeMan {
      11              : 
      12              : TimeType msecPerMove = 777, msecInTC = -1, msecInc = -1, msecUntilNextTC = -1, overHead = 0;
      13              : TimeType targetTime = 0, maxTime = 0;
      14              : int      moveToGo = -1, nbMoveInTC = -1;
      15              : uint64_t maxNodes = 0;
      16              : bool     isDynamic = false;
      17              : 
      18            2 : void init() {
      19            2 :    Logging::LogIt(Logging::logInfo) << "Init timeman";
      20              :    ///@todo shall be reset some stuff here ?
      21            2 : }
      22              : 
      23          455 : TimeType getNextMSecPerMove(const Position& p) {
      24              : 
      25          455 :    const TimeType msecMarginMin  = DynamicConfig::moveOverHead; // minimum margin
      26          455 :    const TimeType msecMarginMax  = 3000; // maximum margin
      27          455 :    const float    msecMarginCoef = 0.01f; // part of the remaining time use as margin
      28              :    auto getMargin = [&](TimeType remainingTime){
      29           94 :       return std::max(std::min(msecMarginMax, static_cast<TimeType>(msecMarginCoef * static_cast<float>(remainingTime))), msecMarginMin);
      30              :    };
      31              : 
      32              :    TimeType ms = -1; // this is what we will compute here
      33              : 
      34          455 :    Logging::LogIt(Logging::logInfo) << "msecPerMove     " << msecPerMove;
      35          455 :    Logging::LogIt(Logging::logInfo) << "msecInTC        " << msecInTC;
      36          455 :    Logging::LogIt(Logging::logInfo) << "msecInc         " << msecInc;
      37          455 :    Logging::LogIt(Logging::logInfo) << "nbMoveInTC      " << nbMoveInTC;
      38          455 :    Logging::LogIt(Logging::logInfo) << "msecUntilNextTC " << msecUntilNextTC;
      39          910 :    Logging::LogIt(Logging::logInfo) << "currentNbMoves  " << static_cast<int>(p.moves);
      40          910 :    Logging::LogIt(Logging::logInfo) << "moveToGo        " << static_cast<int>(moveToGo);
      41          455 :    Logging::LogIt(Logging::logInfo) << "maxNodes        " << maxNodes;
      42              : 
      43          455 :    const TimeType msecIncLoc = (msecInc > 0) ? msecInc : 0;
      44              : 
      45              :    TimeType msecMargin = msecMarginMin;
      46              : 
      47          455 :    if (maxNodes > 0) {
      48            0 :       Logging::LogIt(Logging::logInfo) << "Fixed nodes per move";
      49            0 :       targetTime = INFINITETIME;
      50            0 :       maxTime    = INFINITETIME;
      51            0 :       return targetTime;
      52              :    }
      53              : 
      54          455 :    else if (msecPerMove > 0) {
      55          408 :       Logging::LogIt(Logging::logInfo) << "Fixed time per move";
      56          408 :       targetTime = msecPerMove;
      57          408 :       maxTime    = msecPerMove;
      58          408 :       return targetTime;
      59              :    }
      60              : 
      61           47 :    else if (nbMoveInTC > 0) { // mps is given (xboard style)
      62              :       assert(nbMoveInTC > 0);
      63            1 :       Logging::LogIt(Logging::logInfo) << "Xboard style TC";
      64            1 :       if (!isDynamic){
      65            0 :          assert(msecInTC > 0);
      66              :          // we have all static information here : nbMoveInTC in a nbMoveInTC TC with msecIncLoc of increment at each move.
      67              :          // so we simply split time in equal parts
      68              :          // we use the minimal margin
      69              :          // WARNING note that this might be a bit stupid when used with a book
      70            0 :          const TimeType totalIncr = nbMoveInTC * msecIncLoc;
      71            0 :          ms = static_cast<TimeType>(static_cast<float>(msecInTC + totalIncr - msecMargin) / static_cast<float>(nbMoveInTC));
      72              :       }
      73              :       else {
      74            1 :          assert(msecUntilNextTC > 0);
      75              :          // we have some dynamic information here :
      76              :          // - the next TC will be in : (nbMoveInTC - ((p.moves - 1) % nbMoveInTC)) moves
      77              :          // - current remaining time is msecUntilNextTC
      78              :          // remainings increment sum is computed
      79              :          // a margin will be used
      80              :          // WARNING using this late moves in current TC can be a little longer depending on the margin used
      81            1 :          const int moveUntilNextTC = nbMoveInTC - ((p.moves - 1) % nbMoveInTC); // this modulus is a bit slow probably
      82            1 :          const TimeType remainingIncr = moveUntilNextTC * msecIncLoc;
      83              :          msecMargin = getMargin(msecUntilNextTC);
      84            1 :          ms = static_cast<TimeType>(static_cast<float>(msecUntilNextTC + remainingIncr - msecMargin) / static_cast<float>(moveUntilNextTC));
      85              :       }
      86              :    }
      87              : 
      88           46 :    else if (moveToGo > 0) { // moveToGo is given (uci style)
      89            0 :       assert(msecUntilNextTC > 0);
      90            0 :       Logging::LogIt(Logging::logInfo) << "UCI style TC";
      91            0 :       if (!isDynamic){
      92            0 :          Logging::LogIt(Logging::logFatal) << "bad timing configuration ... (missing dynamic UCI time info)";
      93              :       }
      94              :       else {
      95              :          // we have dynamic information here :
      96              :          // - move to go before next TC
      97              :          // - current remaining time
      98              :          // remainings increment sum is computed
      99              :          // a margin will be used
     100              :          // a correction factor is applied for UCI pondering in order to search longer ///@todo why only 3/2 ...
     101            0 :          const TimeType remainingIncr = moveToGo * msecIncLoc;
     102            0 :          const float ponderingCorrection = (ThreadPool::instance().main().getData().isPondering ? 3 : 2) / 2.f;
     103            0 :          msecMargin = getMargin(msecUntilNextTC);
     104            0 :          ms = static_cast<TimeType>((static_cast<float>(msecUntilNextTC + remainingIncr - msecMargin) / static_cast<float>(moveToGo) ) * ponderingCorrection);
     105              :       }
     106              :    }
     107              : 
     108              :    else { // sudden death style
     109           46 :       assert(msecUntilNextTC >= 0);
     110           46 :       Logging::LogIt(Logging::logInfo) << "Suddendeath style";
     111           46 :       if (!isDynamic) Logging::LogIt(Logging::logFatal) << "bad timing configuration ... (missing dynamic time info for sudden death style TC)";
     112              :       // we have only remaining time available
     113              :       // we will do "as if" nmoves still need to be played
     114              :       // we will take into account the time needed to parse input and get current position (overhead)
     115              :       // in situation where increment is smaller than minimum margin we will be a lot more conservative
     116              :       // a margin will be used
     117              :       // a correction factor is applied for UCI pondering in order to search longer ///@todo why only 3/2 ...
     118              :       // be carefull here !, using xboard (for instance under cutechess), we won't receive an increment
     119              :       // moreover, we won't assume that receiving an increment once is enough to be sure we will receive it next time
     120              :       // so we won't try to live on increment too soon. This will only happens when the game goes on and "nmoves" gets smaller...
     121              : 
     122              :       // if increment is smaller than the minimal lost by move, this is a "risky TC situation"
     123           46 :       const bool riskySituation = msecInc < msecMinimal;
     124              :       // in case of a non risky situation, we can make some correction
     125              :       // if game is long we will hope the end is near (between 0 and 15)
     126           92 :       const int gameLengthContrib = riskySituation ? static_cast<int>(std::min(15.f, p.halfmoves / 20.f))
     127            0 :                                                    : static_cast<int>(std::min(15.f, p.halfmoves / 10.f));
     128              :       // if material is low we will hope the end is near (between 0 and 15)
     129           46 :       ScoreType sw = 0;
     130           46 :       ScoreType sb = 0;
     131           46 :       const float gp = gamePhase(p.mat, sw, sb);
     132           46 :       const int gamePhaseContrib  = riskySituation ? 0
     133            0 :                                                    : static_cast<int>((1.f-gp)*15);
     134              :       // the applied correction will be the bigger one
     135           46 :       const int nmovesCorrection = std::max(gameLengthContrib, gamePhaseContrib);                                                    
     136           46 :       const int nmoves = riskySituation ? 28 : (16 - nmovesCorrection); 
     137              : 
     138           46 :       if (nmoves * msecMinimal > msecUntilNextTC){
     139           56 :          Logging::LogIt(Logging::logGUI) << Logging::_protocolComment[Logging::ct] << "Minic is in time trouble ...";
     140              :       }
     141              : 
     142           46 :       msecMargin = getMargin(msecUntilNextTC);
     143           46 :       const float frac = (static_cast<float>(msecUntilNextTC - msecMargin) / static_cast<float>(nmoves));
     144           46 :       const float incrBonus = riskySituation ? 0 : std::min(0.9f * msecIncLoc, msecUntilNextTC - msecMargin - frac);
     145           46 :       const float ponderingCorrection = (ThreadPool::instance().main().getData().isPondering ? 3 : 2) / 2.f;
     146           46 :       ms = static_cast<TimeType>( (frac + incrBonus) * ponderingCorrection);
     147              : 
     148           46 :       Logging::LogIt(Logging::logInfo) << "risky ?           " << riskySituation;
     149           46 :       Logging::LogIt(Logging::logInfo) << "msecIncLoc        " << msecIncLoc;
     150           46 :       Logging::LogIt(Logging::logInfo) << "msecUntilNextTC   " << msecUntilNextTC;
     151           46 :       Logging::LogIt(Logging::logInfo) << "gameLengthContrib " << gameLengthContrib;
     152           46 :       Logging::LogIt(Logging::logInfo) << "gamePhaseContrib  " << gamePhaseContrib;
     153           46 :       Logging::LogIt(Logging::logInfo) << "nmoves            " << nmoves;
     154           92 :       Logging::LogIt(Logging::logInfo) << "p.moves           " << static_cast<int>(p.moves);
     155           92 :       Logging::LogIt(Logging::logInfo) << "p.halfmoves       " << static_cast<int>(p.halfmoves);
     156           46 :       Logging::LogIt(Logging::logInfo) << "frac              " << frac;
     157           46 :       Logging::LogIt(Logging::logInfo) << "incrBonus         " << incrBonus;
     158           46 :       Logging::LogIt(Logging::logInfo) << "ms                " << ms;
     159              :    }
     160              : 
     161              :    // take overhead into account
     162              :    // and if not much time left, let's try to return msecMinimal and hope for a friendly GUI...
     163           47 :    targetTime = std::max(ms - overHead, msecMinimal);
     164              :    // maxTime will be our limit even in case of trouble
     165           47 :    maxTime = std::min(msecUntilNextTC - msecMargin, targetTime * 7);
     166           47 :    return targetTime;
     167              : }
     168              : 
     169            1 : void simulate(const TCType tcType, const TimeType initialTime, const TimeType increment, const int movesInTC, const TimeType guiLag){
     170              : 
     171            1 :    Logging::LogIt(Logging::logInfo) << "Start of TC simulation";
     172              : 
     173              :    // init TC parameter
     174            1 :    isDynamic       = tcType == TC_suddendeath;
     175            1 :    nbMoveInTC      = tcType == TC_repeating ? movesInTC : -1;
     176            1 :    msecPerMove     = -1;
     177            1 :    msecInTC        = tcType == TC_repeating ? initialTime : static_cast<TimeType>(-1);
     178            1 :    msecInc         = increment;
     179            1 :    msecUntilNextTC = tcType == TC_suddendeath ? initialTime : static_cast<TimeType>(-1);
     180            1 :    moveToGo        = -1;
     181              : 
     182              :    // and check configuration
     183            1 :    switch(tcType){
     184            1 :       case TC_suddendeath:
     185            1 :          assert(msecUntilNextTC >= 0);
     186              :          break;
     187            0 :       case TC_repeating:
     188            0 :          assert(msecInTC > 0);
     189            0 :          assert(nbMoveInTC > 0);
     190              :          break;
     191            0 :       case TC_fixed:
     192            0 :          assert(false); // not used here
     193              :          break;
     194              :    }
     195              : 
     196            2 :    RootPosition p{std::string(startPosition)};
     197            1 :    Position p2 = p;
     198              : #ifdef WITH_NNUE
     199            1 :    NNUEEvaluator evaluator;
     200              :    p2.associateEvaluator(evaluator);
     201            1 :    p2.resetNNUEEvaluator(p2.evaluator());
     202              : #endif
     203              : 
     204            1 :    TimeType remaining = msecUntilNextTC;
     205              :    TimeType currentMoveMs = 0;
     206              : 
     207           47 :    while( remaining > 0 && p2.moves < 300){
     208           46 :       currentMoveMs = getNextMSecPerMove(p2);
     209           46 :       Logging::LogIt(Logging::logInfo) << "currentMoveMs " << currentMoveMs;
     210           46 :       remaining -= currentMoveMs;
     211           46 :       remaining += msecInc;
     212           46 :       remaining -= guiLag;
     213           46 :       Logging::LogIt(Logging::logInfo) << "remaining " << remaining;
     214           46 :       switch(tcType){
     215           46 :          case TC_suddendeath:
     216           46 :             msecUntilNextTC = remaining;
     217           46 :          break;
     218              :          case TC_repeating:
     219              :          break;
     220              :          case TC_fixed:
     221              :          break;
     222              :       }
     223           46 :       const bool isInCheck = isPosInCheck(p2);
     224           46 :       ThreadData _data;
     225           46 :       _data.score = randomMover(p2, _data.pv, isInCheck);
     226           46 :       if ( _data.pv.empty() ) break;
     227           46 :       if (const MoveInfo moveInfo(p2,_data.pv[0]); ! applyMove(p2, moveInfo)) break;
     228           92 :       std::cout << GetFEN(p2) << std::endl;
     229              :    }
     230            2 :    Logging::LogIt(Logging::logInfo) << "End of TC simulation : " << (remaining > 0 ? "success" : "failed");
     231            1 : }
     232              : 
     233              : } // namespace TimeMan
        

Generated by: LCOV version 2.0-1