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 : }
|