LCOV - code coverage report
Current view: top level - Source - cli_selfplay.cpp (source / functions) Coverage Total Hit
Test: coverage Lines: 75.0 % 48 36
Test Date: 2026-03-02 16:42:41 Functions: 100.0 % 1 1

            Line data    Source code
       1              : #include "definition.hpp"
       2              : #include "dynamicConfig.hpp"
       3              : #include "moveApply.hpp"
       4              : #include "position.hpp"
       5              : #include "positionTools.hpp"
       6              : #include "searcher.hpp"
       7              : #include "threading.hpp"
       8              : 
       9              : // see cli.cpp
      10              : void analyze(const Position& p, DepthType depth, bool openBenchOutput = false);
      11              : 
      12            1 : void selfPlay(DepthType depth, uint64_t & nbPos) {
      13              :    //DynamicConfig::genFen = true; // this can be forced here but shall be set by CLI option in fact.
      14              : 
      15            1 :    assert(!DynamicConfig::armageddon);
      16            1 :    assert(!DynamicConfig::antichess);
      17              : 
      18              :    const std::string startfen = 
      19              : #if !defined(WITH_SMALL_MEMORY)
      20            1 :                                   DynamicConfig::DFRC ? chess960::getDFRCXFEN() : 
      21            0 :                                   DynamicConfig::FRC ? chess960::positions[std::rand() % 960] : 
      22              : #endif
      23            1 :                                   std::string(startPosition);
      24            1 :    RootPosition      p(startfen);
      25              : #ifdef WITH_NNUE
      26            1 :    NNUEEvaluator evaluator;
      27              :    p.associateEvaluator(evaluator);
      28            1 :    p.resetNNUEEvaluator(p.evaluator()); // this is needed as the RootPosition CTOR with a fen hasn't fill the evaluator yet ///@todo a CTOR with evaluator given ...
      29              : #endif
      30              : 
      31            1 :    if (DynamicConfig::pgnOut && !ThreadPool::instance().main().pgnStream.is_open()) {
      32            0 :       ThreadPool::instance().main().pgnStream.open("selfplay_" + std::to_string(GETPID()) + "_" + std::to_string(0) + ".pgn", std::ofstream::app);
      33              :    }
      34              : 
      35              : #ifdef WITH_GENFILE
      36            1 :    if (DynamicConfig::genFen && !ThreadPool::instance().main().genStream.is_open()) {
      37            0 :       ThreadPool::instance().main().genStream.open("genfen_" + std::to_string(GETPID()) + "_" + std::to_string(0) + ".epd", std::ofstream::app);
      38              :    }
      39              : #endif
      40            1 :    Position  p2           = p;
      41              :    bool      ended        = false;
      42              :    bool      justBegin    = true;
      43              :    int       result       = 0;
      44              :    int       drawCount    = 0;
      45              :    int       winCount     = 0;
      46              :    const int minAdjMove   = 40;
      47              :    const int minAdjCount  = 10;
      48              :    const int minDrawScore = 8;
      49              :    const int minWinScore  = 800;
      50              : 
      51            1 :    nbPos = 0;
      52              : 
      53              :    while (true) {
      54          194 :       ThreadPool::instance().main().subSearch = true;
      55          194 :       analyze(p2, depth); // search using a specific depth
      56          194 :       ThreadPool::instance().main().subSearch = false;
      57          194 :       ThreadData d = ThreadPool::instance().main().getData();
      58              : 
      59          194 :       if (justBegin){
      60            1 :          if (DynamicConfig::pgnOut){
      61            0 :             ThreadPool::instance().main().pgnStream << "[Event \"Minic self play\"]\n";
      62            0 :             ThreadPool::instance().main().pgnStream << "[FEN \"" + GetFEN(p) + "\"]\n";
      63              :          }
      64              :          justBegin = false;
      65              :       }
      66              : 
      67          194 :       if (Abs(d.score) < minDrawScore) ++drawCount;
      68              :       else
      69              :          drawCount = 0;
      70              : 
      71          194 :       if (Abs(d.score) > minWinScore) ++winCount;
      72              :       else
      73              :          winCount = 0;
      74              : 
      75          194 :       if (p2.halfmoves > minAdjMove && winCount > minAdjCount) {
      76            0 :          Logging::LogIt(Logging::logInfoPrio) << "End of game (adjudication win) " << GetFEN(p2);
      77              :          ended  = true;
      78            0 :          result = (d.score * (p2.c == Co_White ? 1 : -1)) > 0 ? 1 : -1;
      79              :       }
      80          194 :       else if (p2.halfmoves > minAdjMove && drawCount > minAdjCount) {
      81            0 :          Logging::LogIt(Logging::logInfoPrio) << "End of game (adjudication draw) " << GetFEN(p2);
      82              :          ended  = true;
      83              :          result = 0;
      84              :       }
      85          194 :       else if (d.best == INVALIDMOVE) {
      86            2 :          Logging::LogIt(Logging::logInfoPrio) << "End of game " << GetFEN(p2);
      87              :          ended = true;
      88              :          ///@todo this is only working in classic chess (no armageddon or antichess)
      89            1 :          if (isPosInCheck(p2)) result = p2.c == Co_Black ? 1 : -1; // checkmated (cannot move and attaked)
      90              :          else
      91              :             result = 0; // pat (cannot move)
      92              :       }
      93          193 :       else if (p2.halfmoves > MAX_PLY / 2) {
      94            0 :          Logging::LogIt(Logging::logInfoPrio) << "Too long game " << GetFEN(p2);
      95              :          ended  = true;
      96              :          result = 0; // draw
      97              :       }
      98              : 
      99              :       if (ended){
     100            1 :          if (DynamicConfig::pgnOut) ThreadPool::instance().main().pgnStream << (result == 0 ? "1/2-1/2" : result > 0 ? "1-0" : "0-1") << "\n";
     101              :          justBegin = true;
     102              :       }
     103              :       else {
     104          193 :          if (DynamicConfig::pgnOut) ThreadPool::instance().main().pgnStream << (p2.halfmoves%2?(std::to_string(p2.moves)+". ") : "") << showAlgAbr(d.best,p2) << " ";
     105          193 :          ++nbPos;
     106              :       }
     107              : 
     108              : #ifdef WITH_GENFILE
     109          194 :       if (DynamicConfig::genFen) {
     110              :          // if false, will skip position if bestmove if capture, 
     111              :          // if true, will search for a quiet position from here and rescore (a lot slower of course)
     112              :          const bool getQuietPos = true;
     113              : 
     114              :          // writeToGenFile using genFenDepth from this root position
     115            0 :          if (!ended){
     116            0 :             ThreadPool::instance().main().writeToGenFile(p2, getQuietPos, d, {}); // bufferized
     117              :          }
     118              :          else {
     119            0 :             ThreadPool::instance().main().writeToGenFile(p2, getQuietPos, d, result); // write to file using result
     120              :          }
     121              :       }
     122              : #else
     123              :       DISCARD ended;
     124              :       DISCARD result;
     125              : #endif
     126              : 
     127          194 :       if (ended) break;
     128              : 
     129              :       // update position using best move
     130          193 :       Position p3 = p2;
     131          193 :       if (const MoveInfo moveInfo(p3, d.best); !applyMove(p3, moveInfo)) break;
     132          193 :       p2 = p3;
     133          194 :    }
     134            2 : }
        

Generated by: LCOV version 2.0-1