LCOV - code coverage report
Current view: top level - Source - uci.cpp (source / functions) Coverage Total Hit
Test: coverage Lines: 61.4 % 202 124
Test Date: 2026-03-02 16:42:41 Functions: 66.7 % 6 4

            Line data    Source code
       1              : #include "uci.hpp"
       2              : 
       3              : #include "cli.hpp"
       4              : #include "com.hpp"
       5              : #include "dynamicConfig.hpp"
       6              : #include "logging.hpp"
       7              : #include "opponent.hpp"
       8              : #include "option.hpp"
       9              : #include "position.hpp"
      10              : #include "searcher.hpp"
      11              : #include "timeMan.hpp"
      12              : #include "tools.hpp"
      13              : 
      14              : namespace UCI {
      15              : 
      16              : bool iterate;
      17              : 
      18            1 : void init() {
      19            1 :    Logging::ct = Logging::CT_uci;
      20            1 :    Logging::LogIt(Logging::logInfo) << "Init uci";
      21            1 :    COM::init(COM::p_uci);
      22            1 : }
      23              : 
      24            1 : void uci() {
      25            1 :    iterate = true;
      26           46 :    while (iterate) {
      27           45 :       COM::readLine();
      28           45 :       if(COM::command.empty()){
      29            0 :          Logging::LogIt(Logging::logWarn) << "Empty command";
      30              :          continue;
      31              :       }      
      32              :       std::lock_guard lock(COM::mutexGUI); // cannot treat GUI bestmove while receiving new position
      33           45 :       processCommand(COM::command);
      34              :    }
      35            1 :    Logging::LogIt(Logging::logInfo) << "Leaving UCI loop";
      36              :    
      37              :    // write last game
      38            1 :    if (DynamicConfig::pgnOut){
      39            0 :       std::ofstream os("games_" + std::to_string(GETPID()) + "_" + std::to_string(0) + ".pgn", std::ofstream::app);
      40            0 :       COM::GetGameInfo().write(os);
      41            0 :       os.close();
      42            0 :    }
      43            1 : }
      44              : 
      45           45 : void processCommand(const std::string & command) {
      46              :    ///@todo protect some stuff when search is running !
      47              :    std::string        uciCommand;
      48           45 :    std::istringstream iss(command);
      49           45 :    iss >> uciCommand;
      50           45 :    Logging::LogIt(Logging::logInfo) << "uci received command " << uciCommand;
      51              : 
      52           45 :    if (uciCommand == "uci") {
      53              :       std::string version = MinicVersion;
      54              :       if (Distributed::moreThanOneProcess()) version += "_" + std::to_string(Distributed::worldSize) + "proc";
      55            1 :       Logging::LogIt(Logging::logGUI) << "id name Minic " << version;
      56            1 :       Logging::LogIt(Logging::logGUI) << "id author Vivien Clauzon";
      57            1 :       Options::displayOptionsUCI();
      58            1 :       Logging::LogIt(Logging::logGUI) << "uciok";
      59              :    }
      60           44 :    else if (uciCommand == "setoption") {
      61              :       std::string tmpstr;
      62              :       std::string key;
      63              :       std::string val;
      64           10 :       iss >> tmpstr; //"name"
      65           10 :       iss >> key;
      66           10 :       iss >> tmpstr; //"value"
      67           10 :       getline(iss, val); // get all the remaining stuff on the line
      68              :       //iss >> val;
      69           10 :       val = trim(val);
      70           10 :       if (!Options::SetValue(key, val)) Logging::LogIt(Logging::logError) << "Unable to set value " << key << " = " << val;
      71              :    }
      72           34 :    else if (uciCommand == "isready") {
      73              :       // callback that are not triggered immediatly when the option is received.
      74            3 :       Opponent::init(); // only implemented for UCI
      75              : 
      76            3 :       Logging::LogIt(Logging::logGUI) << "readyok";
      77              :    }
      78           31 :    else if (uciCommand == "debug") {
      79              :       std::string str;
      80            1 :       iss >> str;
      81            1 :       if (str == "on") DynamicConfig::minOutputLevel = Logging::logInfo;
      82              :       else
      83            0 :          DynamicConfig::minOutputLevel = Logging::logGUI;
      84              :    }
      85           30 :    else if (uciCommand == "stop") {
      86            2 :       Logging::LogIt(Logging::logInfo) << "stop requested";
      87            2 :       COM::stop();
      88              :    }
      89           28 :    else if (uciCommand == "position") {
      90            6 :       const auto startTimePos = Clock::now();
      91            6 :       COM::position.h   = nullHash; // invalidate position
      92              :       std::string type;
      93              : 
      94           13 :       while (iss >> type) {
      95            7 :          if (type == "startpos") { 
      96            4 :             readFEN(std::string(startPosition), COM::position, false, true); 
      97              :             // let's start by resetting history
      98            4 :             COM::GetGameInfo().clear(COM::position);              
      99              :          }
     100            3 :          else if (type == "fen") {
     101              :             std::string fen;
     102           14 :             for (int i = 0; i < 6; i++) { // suppose always full fen ... ///@todo better?
     103              :                std::string component;
     104           12 :                iss >> component;
     105           12 :                fen += component + " ";
     106              :             }
     107            2 :             if (!readFEN(fen, COM::position, false, true)) Logging::LogIt(Logging::logFatal) << "Illegal FEN " << fen;
     108              :             // let's start by resetting history
     109            2 :             COM::GetGameInfo().clear(COM::position);              
     110              :          }
     111            1 :          else if (type == "moves") {
     112            1 :             if (COM::position.h != nullHash) {
     113              :                std::string mstr;
     114            5 :                while (iss >> mstr) {
     115            4 :                   const Move m = COM::moveFromCOM(mstr);
     116            8 :                   Logging::LogIt(Logging::logInfo) << "UCI applying move " << ToString(m);
     117            8 :                   if (!COM::makeMove(m, false, "")) { // make move
     118            0 :                      Logging::LogIt(Logging::logInfo) << "Bad move ! : " << mstr;
     119            0 :                      Logging::LogIt(Logging::logInfo) << ToString(COM::position);
     120              :                   }
     121              :                   else {
     122              :                      // in UCI mode, we expect current position to always being sent 
     123              :                      // using an initial position and a serie of moves
     124              :                      // Here, we append all game moves again
     125            8 :                      COM::GetGameInfo().append({COM::position, m});                     
     126              :                   }
     127              :                }
     128              :             }
     129              :             else
     130            0 :                Logging::LogIt(Logging::logGUI) << "info string no start position specified";
     131              :          }
     132              :       }
     133              :       // we take into account the time needed to parse input and get current position
     134            6 :       TimeMan::overHead = getTimeDiff(startTimePos);
     135              :    }
     136           22 :    else if (uciCommand == "go") {
     137            9 :       if (ThreadPool::instance().main().searching()) {
     138            4 :          Logging::LogIt(Logging::logGUI) << "info string go command received, but search already in progress";
     139              :       }
     140              :       else {
     141            5 :          if (COM::position.h != nullHash) {
     142              :             ///@todo MoveList root_moves;
     143              : 
     144            5 :             TimeMan::isDynamic       = false;
     145            5 :             TimeMan::nbMoveInTC      = -1;
     146            5 :             TimeMan::msecPerMove     = -1;
     147            5 :             TimeMan::msecInTC        = -1;
     148            5 :             TimeMan::msecInc         = -1;
     149            5 :             TimeMan::msecUntilNextTC = -1;
     150            5 :             TimeMan::moveToGo        = -1;
     151            5 :             COM::depth               = MAX_DEPTH; // infinity
     152              : 
     153            5 :             DynamicConfig::mateFinder = false;
     154              :             COM::State state          = COM::st_searching; //uci is/should be stateless but we use this anyhow
     155              : 
     156              :             std::string param;
     157              :             bool        noParam = true;
     158           10 :             while (iss >> param) {
     159            5 :                Logging::LogIt(Logging::logInfo) << "received parameter " << param;
     160            5 :                if (param == "infinite") {
     161              :                   noParam              = false;
     162            1 :                   TimeMan::msecPerMove = INFINITETIME;
     163              :                   state                = COM::st_analyzing;
     164              :                }
     165            4 :                else if (param == "depth") {
     166              :                   noParam = false;
     167            2 :                   int d   = 0;
     168            2 :                   iss >> d;
     169            2 :                   COM::depth           = clampDepth(d);
     170            2 :                   TimeMan::msecPerMove = INFINITETIME;
     171              :                }
     172            2 :                else if (param == "movetime") {
     173              :                   noParam = false;
     174              :                   iss >> TimeMan::msecPerMove;
     175              :                }
     176            1 :                else if (param == "nodes") {
     177              :                   noParam = false;
     178              :                   iss >> TimeMan::maxNodes;
     179              :                }
     180            1 :                else if (param == "searchmoves") { Logging::LogIt(Logging::logGUI) << "info string " << param << " not implemented yet"; }
     181            1 :                else if (param == "wtime") {
     182              :                   noParam = false;
     183              :                   int t;
     184            0 :                   iss >> t;
     185            0 :                   if (COM::position.c == Co_White) {
     186            0 :                      TimeMan::msecUntilNextTC = t;
     187            0 :                      TimeMan::isDynamic       = true;
     188              :                   }
     189              :                }
     190            1 :                else if (param == "btime") {
     191              :                   noParam = false;
     192              :                   int t;
     193            0 :                   iss >> t;
     194            0 :                   if (COM::position.c == Co_Black) {
     195            0 :                      TimeMan::msecUntilNextTC = t;
     196            0 :                      TimeMan::isDynamic       = true;
     197              :                   }
     198              :                }
     199            1 :                else if (param == "winc") {
     200              :                   int t;
     201            0 :                   iss >> t;
     202            0 :                   if (COM::position.c == Co_White) {
     203            0 :                      TimeMan::msecInc   = t;
     204            0 :                      TimeMan::isDynamic = true;
     205              :                   }
     206              :                }
     207            1 :                else if (param == "binc") {
     208              :                   int t;
     209            0 :                   iss >> t;
     210            0 :                   if (COM::position.c == Co_Black) {
     211            0 :                      TimeMan::msecInc   = t;
     212            0 :                      TimeMan::isDynamic = true;
     213              :                   }
     214              :                }
     215            1 :                else if (param == "movestogo") {
     216              :                   noParam = false;
     217              :                   int t;
     218            0 :                   iss >> t;
     219            0 :                   TimeMan::moveToGo  = t;
     220            0 :                   TimeMan::isDynamic = true;
     221              :                }
     222            1 :                else if (param == "ponder") { state = COM::st_pondering; }
     223            0 :                else if (param == "mate") {
     224            0 :                   int d = 0;
     225            0 :                   iss >> d;
     226            0 :                   COM::depth                = clampDepth(d);
     227            0 :                   DynamicConfig::mateFinder = true;
     228            0 :                   TimeMan::msecPerMove      = INFINITETIME;
     229              :                }
     230            0 :                else { Logging::LogIt(Logging::logGUI) << "info string " << param << " not implemented"; }
     231              :             }
     232            5 :             if (noParam) {
     233            1 :                Logging::LogIt(Logging::logWarn) << "no parameters given for go command, going for a depth 10 search ...";
     234            1 :                COM::depth           = 10;
     235            1 :                TimeMan::msecPerMove = INFINITETIME;
     236              :             }
     237            5 :             Logging::LogIt(Logging::logInfo) << "uci search launched";
     238            5 :             COM::thinkAsync(state);
     239            5 :             Logging::LogIt(Logging::logInfo) << "uci async started";
     240              :          }
     241            0 :          else { Logging::LogIt(Logging::logGUI) << "info string search command received, but no position specified"; }
     242              :       }
     243              :    }
     244           13 :    else if (uciCommand == "ponderhit") {
     245            1 :       Logging::LogIt(Logging::logInfo) << "received command ponderhit";
     246            1 :       COM::state                                          = COM::st_searching; // will allow move reporting to GUI
     247            1 :       ThreadPool::instance().main().getData().isPondering = false;             // will unlock busy loop
     248              :    }
     249           12 :    else if (uciCommand == "ucinewgame") {
     250            2 :       if (ThreadPool::instance().main().searching()) {
     251            1 :          Logging::LogIt(Logging::logGUI) << "info string " << uciCommand << " received but search in progress ...";
     252              :       }
     253              :       else {
     254            1 :          COM::init(COM::p_uci); 
     255              :       }
     256              :    }
     257           10 :    else if (uciCommand == "eval") { 
     258            0 :       Logging::LogIt(Logging::logGUI) << "info string " << uciCommand << " not implemented yet";
     259              :    }
     260           10 :    else if (uciCommand == "tbprobe") {
     261              :       std::string type;
     262            0 :       iss >> type;
     263            0 :       Logging::LogIt(Logging::logGUI) << "info string " << uciCommand << " not implemented yet";
     264              :    }
     265           10 :    else if (uciCommand == "print") { Logging::LogIt(Logging::logInfo) << ToString(COM::position); }
     266           10 :    else if (uciCommand == "d") { Logging::LogIt(Logging::logInfo) << GetFEN(COM::position); }
     267           10 :    else if (uciCommand == "quit") {
     268            1 :       COM::stop();
     269            1 :       iterate = false;
     270              :    }
     271            9 :    else if (uciCommand == "wait") { // only used for testing purpose
     272              :       using namespace std::chrono_literals;
     273            9 :       Logging::LogIt(Logging::logInfo) << "Start to wait";
     274           13 :       while (!ThreadPool::instance().main().stopFlag) { 
     275            4 :          std::this_thread::sleep_for(10ms);
     276              :       }
     277            9 :       std::this_thread::sleep_for(200ms);
     278            9 :       Logging::LogIt(Logging::logInfo) << "End of wait";
     279              :    }
     280            0 :    else if (uciCommand == "bench") {
     281            0 :       int bd = 10;
     282            0 :       iss >> bd;
     283            0 :       bench(clampDepth(bd));
     284              :    }
     285            0 :    else if (uciCommand == "benchBig") {
     286            0 :       int bd = 10;
     287            0 :       iss >> bd;
     288            0 :       benchBig(clampDepth(bd));
     289              :    }   
     290            0 :    else if (!uciCommand.empty()) { Logging::LogIt(Logging::logGUI) << "info string unrecognised command " << uciCommand; }
     291          106 : }
     292              : 
     293            0 : std::string wdlStat(const ScoreType score, const unsigned int ply) {
     294            0 :    std::stringstream ss;
     295            0 :    const int wdlW = static_cast<int>(toWDLModel(score, ply));
     296            0 :    const int wdlL = static_cast<int>(toWDLModel(-score, ply));
     297            0 :    const int wdlD = 1000 - wdlW - wdlL;
     298            0 :    ss << " wdl " << wdlW << " " << wdlD << " " << wdlL;
     299            0 :    return ss.str();
     300            0 : }
     301              : 
     302          274 : std::string uciScore(const ScoreType score, const unsigned int ply) {
     303          274 :    if (isMatedScore(score)) return "mate " + std::to_string((-MATE - score) / 2);
     304          274 :    if (isMateScore(score)) return "mate " + std::to_string((MATE - score + 1) / 2);
     305          822 :    return "cp " + std::to_string(score) + (DynamicConfig::withWDL ? wdlStat(score, ply) : "");
     306              : }
     307              : 
     308            0 : void handleVariant(){
     309            0 :     if (DynamicConfig::chessvariant == "chess"){
     310            0 :         DynamicConfig::FRC = false;
     311            0 :         DynamicConfig::armageddon = false;
     312            0 :         DynamicConfig::antichess = false;
     313              :     }
     314            0 :     else if (DynamicConfig::chessvariant == "antichess"){
     315            0 :         DynamicConfig::FRC = false;
     316            0 :         DynamicConfig::armageddon = false;
     317            0 :         DynamicConfig::antichess = true;
     318            0 :         DynamicConfig::useNNUE = false;
     319              :     }
     320            0 :     else if (DynamicConfig::chessvariant == "armageddon"){
     321            0 :         DynamicConfig::FRC = false;
     322            0 :         DynamicConfig::armageddon = true;
     323            0 :         DynamicConfig::antichess = false;
     324            0 :         DynamicConfig::useNNUE = false;
     325              :     }
     326            0 :     else if (DynamicConfig::chessvariant == "fischerandom"){
     327            0 :         DynamicConfig::FRC = true;
     328            0 :         DynamicConfig::armageddon = false;
     329            0 :         DynamicConfig::antichess = false;
     330            0 :         DynamicConfig::useNNUE = false;
     331              :     }
     332              :     else {
     333            0 :         Logging::LogIt(Logging::logGUI) << "info string unhandled variant : "  << DynamicConfig::chessvariant;
     334              :     }
     335            0 : }
     336              : 
     337              : } // namespace UCI
        

Generated by: LCOV version 2.0-1