Line data Source code
1 : #include "cli.hpp"
2 :
3 : #include "com.hpp"
4 : #include "dynamicConfig.hpp"
5 : #include "evalDef.hpp"
6 : #include "logging.hpp"
7 : #include "material.hpp"
8 : #include "moveApply.hpp"
9 : #include "moveGen.hpp"
10 : #include "option.hpp"
11 : #include "position.hpp"
12 : #include "searcher.hpp"
13 : #include "tables.hpp"
14 : #include "timeMan.hpp"
15 : #include "tools.hpp"
16 : #include "transposition.hpp"
17 : #include "uci.hpp"
18 : #include "xboard.hpp"
19 :
20 : // see cli_perft.cpp
21 : [[nodiscard]] bool perft_test(const std::string& fen, DepthType d, uint64_t expected);
22 : Counter perft(const Position& p, DepthType depth, PerftAccumulator& acc);
23 :
24 : // see cli_selfplay.cpp
25 : void selfPlay(DepthType depth, uint64_t & nbPos);
26 :
27 : // see cli_SEETest.cpp
28 : bool TestSEE();
29 :
30 201 : void analyze(const Position& p, DepthType depth, bool openBenchOutput = false) {
31 : static double benchms = 0;
32 : static Counter benchNodes = 0;
33 :
34 201 : const int oldOutLvl = DynamicConfig::minOutputLevel;
35 201 : if (openBenchOutput) { DynamicConfig::minOutputLevel = Logging::logOff; }
36 :
37 201 : TimeMan::isDynamic = false;
38 201 : TimeMan::nbMoveInTC = -1;
39 201 : TimeMan::msecPerMove = INFINITETIME;
40 201 : TimeMan::msecInTC = -1;
41 201 : TimeMan::msecInc = -1;
42 201 : TimeMan::msecUntilNextTC = -1;
43 201 : ThreadPool::instance().currentMoveMs = TimeMan::getNextMSecPerMove(p);
44 :
45 201 : ThreadData d;
46 : #ifdef WITH_ASYNC_ANALYZE
47 201 : COM::depth = depth;
48 201 : const std::string fen = GetFEN(p);
49 201 : const bool b = readFEN(fen, COM::position, true, true);
50 201 : if (!b) Logging::LogIt(Logging::logFatal) << "Illegal FEN " << fen;
51 201 : COM::thinkAsync(COM::st_searching);
52 4046 : while(ThreadPool::instance().main().searching()){
53 : using namespace std::chrono_literals;
54 3845 : std::this_thread::sleep_for(10ms);
55 : }
56 : #else
57 : d.p = p;
58 : d.depth = depth;
59 : ThreadPool::instance().distributeData(d);
60 : ThreadPool::instance().main().stopFlag = false;
61 : ThreadPool::instance().main().searchDriver(false);
62 : #endif
63 201 : d = ThreadPool::instance().main().getData();
64 804 : Logging::LogIt(Logging::logInfo) << "Best move is " << ToString(d.best) << " " << static_cast<int>(d.depth) << " " << d.score << " pv : " << ToString(d.pv);
65 :
66 201 : if (openBenchOutput) {
67 5 : Logging::LogIt(Logging::logInfo) << "Next two lines are for OpenBench";
68 5 : const TimeType ms = getTimeDiff(ThreadPool::instance().main().startTime);
69 : const Counter nodeCount =
70 5 : ThreadPool::instance().main().stats.counters[Stats::sid_nodes] + ThreadPool::instance().main().stats.counters[Stats::sid_qnodes];
71 5 : benchNodes += nodeCount;
72 5 : benchms += static_cast<decltype(benchms)>(ms) / 1000.;
73 5 : DynamicConfig::minOutputLevel = oldOutLvl;
74 5 : std::cerr << "NODES " << benchNodes << std::endl;
75 5 : std::cerr << "NPS " << static_cast<int>(static_cast<decltype(benchms)>(benchNodes) / benchms) << std::endl;
76 : }
77 201 : }
78 :
79 : // This struct is dedicated to display beta cut statistics in benchmark
80 : struct BetaCutStat{
81 : Counter betaCuts = 0ull;
82 : Counter betaCutsTT = 0ull;
83 : Counter betaCutsGC = 0ull;
84 : Counter betaCutsP = 0ull;
85 : Counter betaCutsK1 = 0ull;
86 : Counter betaCutsK2 = 0ull;
87 : Counter betaCutsK3 = 0ull;
88 : Counter betaCutsK4 = 0ull;
89 : Counter betaCutsC = 0ull;
90 : Counter betaCutsQ = 0ull;
91 : Counter betaCutsBC = 0ull;
92 :
93 5 : void update(const Stats& stats){
94 5 : betaCuts += stats.counters[Stats::sid_beta] + stats.counters[Stats::sid_ttbeta];
95 5 : betaCutsTT += stats.counters[Stats::sid_ttbeta];
96 5 : betaCutsGC += stats.counters[Stats::sid_beta_gc];
97 5 : betaCutsP += stats.counters[Stats::sid_beta_p];
98 5 : betaCutsK1 += stats.counters[Stats::sid_beta_k1];
99 5 : betaCutsK2 += stats.counters[Stats::sid_beta_k2];
100 5 : betaCutsK3 += stats.counters[Stats::sid_beta_k3];
101 5 : betaCutsK4 += stats.counters[Stats::sid_beta_k4];
102 5 : betaCutsC += stats.counters[Stats::sid_beta_c];
103 5 : betaCutsQ += stats.counters[Stats::sid_beta_q];
104 5 : betaCutsBC += stats.counters[Stats::sid_beta_bc];
105 5 : }
106 :
107 : float percent(const Counter c) const { return c*100.f/betaCuts; }
108 :
109 : void show() const {
110 : #ifdef WITH_BETACUTSTATS
111 : Logging::LogIt(Logging::logInfo) << "betaCuts " << betaCuts;
112 : Logging::LogIt(Logging::logInfo) << "betaCutsTT " << betaCutsTT << " (" << percent(betaCutsTT) << "%)";
113 : Logging::LogIt(Logging::logInfo) << "betaCutsGC " << betaCutsGC << " (" << percent(betaCutsGC) << "%)";
114 : Logging::LogIt(Logging::logInfo) << "betaCutsP " << betaCutsP << " (" << percent(betaCutsP) << "%)";
115 : Logging::LogIt(Logging::logInfo) << "betaCutsK1 " << betaCutsK1 << " (" << percent(betaCutsK1) << "%)";
116 : Logging::LogIt(Logging::logInfo) << "betaCutsK2 " << betaCutsK2 << " (" << percent(betaCutsK2) << "%)";
117 : Logging::LogIt(Logging::logInfo) << "betaCutsK3 " << betaCutsK3 << " (" << percent(betaCutsK3) << "%)";
118 : Logging::LogIt(Logging::logInfo) << "betaCutsK4 " << betaCutsK4 << " (" << percent(betaCutsK4) << "%)";
119 : Logging::LogIt(Logging::logInfo) << "betaCutsC " << betaCutsC << " (" << percent(betaCutsC) << "%)";
120 : Logging::LogIt(Logging::logInfo) << "betaCutsQ " << betaCutsQ << " (" << percent(betaCutsQ) << "%)";
121 : Logging::LogIt(Logging::logInfo) << "betaCutsBC " << betaCutsBC << " (" << percent(betaCutsBC) << "%)";
122 : #endif
123 : }
124 : };
125 :
126 1 : bool bench(DepthType depth) {
127 1 : RootPosition p;
128 : #ifdef WITH_NNUE
129 1 : NNUEEvaluator evaluator;
130 : p.associateEvaluator(evaluator);
131 : #endif
132 :
133 1 : BetaCutStat betaStats;
134 :
135 1 : auto pos = {startPosition, fine70, shirov, shirov2, mate4};
136 :
137 6 : for(const auto & fen : pos){
138 5 : readFEN(std::string{fen}, p);
139 5 : analyze(p, depth, true);
140 5 : betaStats.update(ThreadPool::instance().main().stats);
141 : }
142 :
143 : betaStats.show();
144 :
145 1 : return true;
146 1 : }
147 :
148 0 : bool spsaInputs(){
149 1 : Options::displayOptionsSPSA();
150 0 : return true;
151 : }
152 :
153 0 : bool benchBig(DepthType depth){
154 : // use here same postitions set as Ethereal or Berserk
155 : static const std::vector<std::string> positions = {
156 : "r3k2r/2pb1ppp/2pp1q2/p7/1nP1B3/1P2P3/P2N1PPP/R2QK2R w KQkq a6 0 14",
157 : "4rrk1/2p1b1p1/p1p3q1/4p3/2P2n1p/1P1NR2P/PB3PP1/3R1QK1 b - - 2 24",
158 : "r3qbrk/6p1/2b2pPp/p3pP1Q/PpPpP2P/3P1B2/2PB3K/R5R1 w - - 16 42",
159 : "6k1/1R3p2/6p1/2Bp3p/3P2q1/P7/1P2rQ1K/5R2 b - - 4 44",
160 : "8/8/1p2k1p1/3p3p/1p1P1P1P/1P2PK2/8/8 w - - 3 54",
161 : "7r/2p3k1/1p1p1qp1/1P1Bp3/p1P2r1P/P7/4R3/Q4RK1 w - - 0 36",
162 : "r1bq1rk1/pp2b1pp/n1pp1n2/3P1p2/2P1p3/2N1P2N/PP2BPPP/R1BQ1RK1 b - - 2 10",
163 : "3r3k/2r4p/1p1b3q/p4P2/P2Pp3/1B2P3/3BQ1RP/6K1 w - - 3 87",
164 : "2r4r/1p4k1/1Pnp4/3Qb1pq/8/4BpPp/5P2/2RR1BK1 w - - 0 42",
165 : "4q1bk/6b1/7p/p1p4p/PNPpP2P/KN4P1/3Q4/4R3 b - - 0 37",
166 : "2q3r1/1r2pk2/pp3pp1/2pP3p/P1Pb1BbP/1P4Q1/R3NPP1/4R1K1 w - - 2 34",
167 : "1r2r2k/1b4q1/pp5p/2pPp1p1/P3Pn2/1P1B1Q1P/2R3P1/4BR1K b - - 1 37",
168 : "r3kbbr/pp1n1p1P/3ppnp1/q5N1/1P1pP3/P1N1B3/2P1QP2/R3KB1R b KQkq b3 0 17",
169 : "8/6pk/2b1Rp2/3r4/1R1B2PP/P5K1/8/2r5 b - - 16 42",
170 : "1r4k1/4ppb1/2n1b1qp/pB4p1/1n1BP1P1/7P/2PNQPK1/3RN3 w - - 8 29",
171 : "8/p2B4/PkP5/4p1pK/4Pb1p/5P2/8/8 w - - 29 68",
172 : "3r4/ppq1ppkp/4bnp1/2pN4/2P1P3/1P4P1/PQ3PBP/R4K2 b - - 2 20",
173 : "5rr1/4n2k/4q2P/P1P2n2/3B1p2/4pP2/2N1P3/1RR1K2Q w - - 1 49",
174 : "1r5k/2pq2p1/3p3p/p1pP4/4QP2/PP1R3P/6PK/8 w - - 1 51",
175 : "q5k1/5ppp/1r3bn1/1B6/P1N2P2/BQ2P1P1/5K1P/8 b - - 2 34",
176 : "r1b2k1r/5n2/p4q2/1ppn1Pp1/3pp1p1/NP2P3/P1PPBK2/1RQN2R1 w - - 0 22",
177 : "r1bqk2r/pppp1ppp/5n2/4b3/4P3/P1N5/1PP2PPP/R1BQKB1R w KQkq - 0 5",
178 : "r1bqr1k1/pp1p1ppp/2p5/8/3N1Q2/P2BB3/1PP2PPP/R3K2n b Q - 1 12",
179 : "r1bq2k1/p4r1p/1pp2pp1/3p4/1P1B3Q/P2B1N2/2P3PP/4R1K1 b - - 2 19",
180 : "r4qk1/6r1/1p4p1/2ppBbN1/1p5Q/P7/2P3PP/5RK1 w - - 2 25",
181 : "r7/6k1/1p6/2pp1p2/7Q/8/p1P2K1P/8 w - - 0 32",
182 : "r3k2r/ppp1pp1p/2nqb1pn/3p4/4P3/2PP4/PP1NBPPP/R2QK1NR w KQkq - 1 5",
183 : "3r1rk1/1pp1pn1p/p1n1q1p1/3p4/Q3P3/2P5/PP1NBPPP/4RRK1 w - - 0 12",
184 : "5rk1/1pp1pn1p/p3Brp1/8/1n6/5N2/PP3PPP/2R2RK1 w - - 2 20",
185 : "8/1p2pk1p/p1p1r1p1/3n4/8/5R2/PP3PPP/4R1K1 b - - 3 27",
186 : "8/4pk2/1p1r2p1/p1p4p/Pn5P/3R4/1P3PP1/4RK2 w - - 1 33",
187 : "8/5k2/1pnrp1p1/p1p4p/P6P/4R1PK/1P3P2/4R3 b - - 1 38",
188 : "8/8/1p1kp1p1/p1pr1n1p/P6P/1R4P1/1P3PK1/1R6 b - - 15 45",
189 : "8/8/1p1k2p1/p1prp2p/P2n3P/6P1/1P1R1PK1/4R3 b - - 5 49",
190 : "8/8/1p4p1/p1p2k1p/P2npP1P/4K1P1/1P6/3R4 w - - 6 54",
191 : "8/8/1p4p1/p1p2k1p/P2n1P1P/4K1P1/1P6/6R1 b - - 6 59",
192 : "8/5k2/1p4p1/p1pK3p/P2n1P1P/6P1/1P6/4R3 b - - 14 63",
193 : "8/1R6/1p1K1kp1/p6p/P1p2P1P/6P1/1Pn5/8 w - - 0 67",
194 : "1rb1rn1k/p3q1bp/2p3p1/2p1p3/2P1P2N/PP1RQNP1/1B3P2/4R1K1 b - - 4 23",
195 : "4rrk1/pp1n1pp1/q5p1/P1pP4/2n3P1/7P/1P3PB1/R1BQ1RK1 w - - 3 22",
196 : "r2qr1k1/pb1nbppp/1pn1p3/2ppP3/3P4/2PB1NN1/PP3PPP/R1BQR1K1 w - - 4 12",
197 : "2r2k2/8/4P1R1/1p6/8/P4K1N/7b/2B5 b - - 0 55",
198 : "6k1/5pp1/8/2bKP2P/2P5/p4PNb/B7/8 b - - 1 44",
199 : "2rqr1k1/1p3p1p/p2p2p1/P1nPb3/2B1P3/5P2/1PQ2NPP/R1R4K w - - 3 25",
200 : "r1b2rk1/p1q1ppbp/6p1/2Q5/8/4BP2/PPP3PP/2KR1B1R b - - 2 14",
201 : "6r1/5k2/p1b1r2p/1pB1p1p1/1Pp3PP/2P1R1K1/2P2P2/3R4 w - - 1 36",
202 : "rnbqkb1r/pppppppp/5n2/8/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2",
203 : "2rr2k1/1p4bp/p1q1p1p1/4Pp1n/2PB4/1PN3P1/P3Q2P/2RR2K1 w - f6 0 20",
204 : "3br1k1/p1pn3p/1p3n2/5pNq/2P1p3/1PN3PP/P2Q1PB1/4R1K1 w - - 0 23",
205 : "2r2b2/5p2/5k2/p1r1pP2/P2pB3/1P3P2/K1P3R1/7R w - - 23 93"
206 0 : };
207 :
208 0 : RootPosition p;
209 : #ifdef WITH_NNUE
210 0 : NNUEEvaluator evaluator;
211 : p.associateEvaluator(evaluator);
212 : #endif
213 :
214 0 : BetaCutStat betaStats;
215 :
216 0 : for( const auto & fen : positions){
217 0 : readFEN(fen, p);
218 0 : analyze(p, depth, true);
219 0 : betaStats.update(ThreadPool::instance().main().stats);
220 : }
221 :
222 : betaStats.show();
223 :
224 0 : return true;
225 0 : }
226 :
227 21 : bool cliManagement(const std::string & firstArg, int argc, char** argv) {
228 :
229 : // first we will look for options that do not need extra parameters
230 :
231 : #ifdef WITH_UCI
232 21 : if (firstArg == "-uci") {
233 1 : UCI::init();
234 1 : TimeMan::init();
235 1 : UCI::uci();
236 1 : return true;
237 : }
238 20 : Logging::LogIt(Logging::logInfo) << "You can use -uci command line option to enter uci mode";
239 : #endif
240 : #ifdef WITH_XBOARD
241 20 : if (firstArg == "-xboard") {
242 1 : XBoard::init();
243 1 : TimeMan::init();
244 1 : XBoard::xboard();
245 1 : return true;
246 : }
247 19 : Logging::LogIt(Logging::logInfo) << "You can use -xboard command line option to enter xboard mode";
248 : #endif
249 :
250 : #ifdef WITH_FMTLIB
251 : // let's switch to "pretty mode"
252 : Pretty::init();
253 : #endif
254 :
255 : const auto args {std::span(argv, size_t(argc))};
256 :
257 19 : if (firstArg == "-selfplay") {
258 : DepthType d = 15; // this is "search depth", not genFenDepth !
259 1 : if (argc > 2) d = clampDepth(atoi(args[2]));
260 : int64_t n = 1;
261 1 : if (argc > 3) n = atoll(args[3]);
262 1 : Logging::LogIt(Logging::logInfo) << "Let's go for " << n << " selfplay games ...";
263 1 : const auto startTime = Clock::now();
264 : uint64_t nbGames = 0;
265 : uint64_t nbPos = 0;
266 2 : while (n-- > 0) {
267 2 : if (n % 1000 == 0) Logging::LogIt(Logging::logInfo) << "Remaining games " << n;
268 1 : uint64_t nbPosLoc = 0;
269 1 : selfPlay(d, nbPosLoc);
270 1 : nbPos += nbPosLoc;
271 : const auto ms = getTimeDiff(startTime);
272 1 : ++nbGames;
273 1 : Logging::LogIt(Logging::logError) << "Nb games : " << nbGames << ", Nb Pos : " << nbPos << ", elapsed s : " << ms/1000;
274 2 : Logging::LogIt(Logging::logError) << "Game speed : " << static_cast<double>(nbGames) / (static_cast<double>(ms) / 1000 / 60) << " games/min";
275 2 : Logging::LogIt(Logging::logError) << "Pos speed : " << static_cast<double>(nbPos) / (static_cast<double>(ms) / 1000) << " pos/s";
276 : }
277 : return true;
278 : }
279 :
280 18 : if (firstArg == "-perft_test") {
281 : bool ok = true;
282 1 : ok |= perft_test(std::string(startPosition), 5, 4865609);
283 1 : ok |= perft_test("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - ", 4, 4085603);
284 1 : ok |= perft_test("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - ", 6, 11030083);
285 1 : ok |= perft_test("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 5, 15833292);
286 1 : return ok;
287 : }
288 :
289 17 : if (firstArg == "-perft_test_long_fischer") {
290 : bool ok = true;
291 0 : DynamicConfig::FRC = true;
292 0 : std::ifstream infile("Book_and_Test/TestSuite/fischer.txt");
293 : std::string line;
294 0 : while (std::getline(infile, line)) {
295 : std::size_t found = line.find_first_of(",");
296 0 : const std::string fen = line.substr(0, found);
297 0 : while (found != std::string::npos) {
298 0 : const std::size_t start = found + 1;
299 : found = line.find_first_of(',', found + 1);
300 0 : const uint64_t ull = std::stoull(line.substr(start, found - start));
301 0 : ok |= perft_test(fen, 6, ull);
302 : }
303 : }
304 : return ok;
305 0 : }
306 :
307 17 : if (firstArg == "-perft_test_long") {
308 : bool ok = true;
309 0 : std::ifstream infile("Book_and_Test/TestSuite/perft.txt");
310 : std::string line;
311 0 : while (std::getline(infile, line)) {
312 : std::size_t found = line.find_first_of(",");
313 0 : const std::string fen = line.substr(0, found);
314 : DepthType i = 0;
315 0 : while (found != std::string::npos) {
316 0 : const std::size_t start = found + 1;
317 : found = line.find_first_of(',', found + 1);
318 0 : const uint64_t ull = std::stoull(line.substr(start, found - start));
319 0 : ok |= perft_test(fen, ++i, ull);
320 : }
321 : }
322 : return ok;
323 0 : }
324 :
325 17 : if (firstArg == "-see_test") {
326 1 : return TestSEE();
327 : }
328 :
329 16 : if (firstArg == "bench" || firstArg == "-bench") {
330 : DepthType d = 16;
331 2 : if (argc > 2) d = clampDepth(atoi(args[2]));
332 1 : return bench(d);
333 : }
334 :
335 15 : if (firstArg == "spsa" || firstArg == "-spsa") {
336 : return spsaInputs();
337 : }
338 :
339 14 : if (firstArg == "benchBig" || firstArg == "-benchBig") {
340 : DepthType d = 16;
341 0 : if (argc > 2) d = clampDepth(atoi(args[2]));
342 0 : return benchBig(d);
343 : }
344 :
345 14 : if (firstArg == "-evalSpeed") {
346 0 : DynamicConfig::disableTT = true;
347 0 : std::string filename = "Book_and_Test/TestSuite/evalSpeed.epd";
348 0 : if (argc > 2) filename = args[2];
349 0 : std::vector<RootPosition> data;
350 0 : Logging::LogIt(Logging::logInfo) << "Running eval speed with file (including NNUE evaluator reset)" << filename;
351 0 : std::vector<std::string> positions;
352 0 : DISCARD readEPDFile(filename, positions);
353 0 : for (size_t k = 0; k < positions.size(); ++k) {
354 0 : data.emplace_back(positions[k], false);
355 0 : if (k % 50000 == 0) Logging::LogIt(Logging::logInfo) << k << " position read";
356 : }
357 0 : Logging::LogIt(Logging::logInfo) << "Data size : " << data.size();
358 :
359 0 : std::chrono::time_point<Clock> startTime = Clock::now();
360 : const int loops = 10;
361 0 : for (int k = 0; k < loops; ++k)
362 0 : for (auto& p : data) {
363 : #ifdef WITH_NNUE
364 0 : NNUEEvaluator evaluator;
365 : p.associateEvaluator(evaluator);
366 0 : p.resetNNUEEvaluator(evaluator);
367 : #endif
368 0 : EvalData d;
369 0 : DISCARD eval(p, d, ThreadPool::instance().main(), true);
370 : }
371 : auto ms = getTimeDiff(startTime);
372 0 : Logging::LogIt(Logging::logInfo) << "Eval speed (with EG material hash): " << static_cast<double>(data.size()) * loops / (static_cast<double>(ms) * 1000) << " Meval/s";
373 :
374 0 : startTime = Clock::now();
375 0 : for (int k = 0; k < loops; ++k)
376 0 : for (auto& p : data) {
377 : #ifdef WITH_NNUE
378 0 : NNUEEvaluator evaluator;
379 : p.associateEvaluator(evaluator);
380 0 : p.resetNNUEEvaluator(evaluator);
381 : #endif
382 0 : EvalData d;
383 0 : DISCARD eval(p, d, ThreadPool::instance().main(), false);
384 : }
385 : ms = getTimeDiff(startTime);
386 0 : Logging::LogIt(Logging::logInfo) << "Eval speed : " << static_cast<double>(data.size()) * loops / (static_cast<double>(ms) * 1000) << " Meval/s";
387 :
388 0 : ThreadPool::instance().displayStats();
389 : return true;
390 0 : }
391 :
392 14 : if (firstArg == "-timeTest") {
393 : TimeMan::TCType tcType = TimeMan::TC_suddendeath;
394 : TimeType initialTime = 50000;
395 : TimeType increment = 0;
396 : int movesInTC = -1;
397 : TimeType guiLag = 0;
398 1 : if (argc > 2) initialTime = atoi(args[2]);
399 1 : if (argc > 3) increment = atoi(args[3]);
400 1 : if (argc > 4) movesInTC = atoi(args[4]);
401 1 : if (argc > 5) guiLag = atoi(args[5]);
402 1 : TimeMan::simulate(tcType, initialTime, increment, movesInTC, guiLag);
403 1 : return true;
404 : }
405 :
406 : // in all other cases, args[2] is always the fen string
407 13 : if(argc < 3){
408 0 : Logging::LogIt(Logging::logError) << "your command is either wrong or needs a fen string";
409 0 : return true;
410 : }
411 13 : std::string fen = args[2];
412 :
413 : // some "short cuts" !
414 13 : if (fen == "start") fen = startPosition;
415 13 : if (fen == "fine70") fen = fine70;
416 13 : if (fen == "shirov") fen = shirov;
417 13 : if (fen == "shirov2") fen = shirov2;
418 13 : if (fen == "mate4") fen = mate4;
419 :
420 : // validate the supposed fen string ...
421 : const std::regex lookLikeFen(
422 : "^"
423 : "([rnbqkpRNBQKP1-8]+\\/){7}([rnbqkpRNBQKP1-8]+)\\s"
424 : "[bw]\\s"
425 : "((-|(K|[A-H])?(Q|[A-H])?(k|[a-h])?(q|[a-h])?)\\s)"
426 : "((-|[a-h][36])\\s)"
427 : "((\\d+)\\s(\\d+)\\s?|(\\d+)\\s?)?"
428 : "$"
429 13 : );
430 13 : if( ! std::regex_match(fen, lookLikeFen)){
431 1 : Logging::LogIt(Logging::logError) << "fen does not look good... : " << fen;
432 1 : return true;
433 : }
434 :
435 : // instantiate the position
436 12 : RootPosition p;
437 : #ifdef WITH_NNUE
438 12 : NNUEEvaluator evaluator;
439 : p.associateEvaluator(evaluator);
440 : #endif
441 12 : if (!readFEN(fen, p, false, true)) {
442 0 : Logging::LogIt(Logging::logError) << "when reading fen";
443 0 : return true;
444 : }
445 :
446 24 : Logging::LogIt(Logging::logInfo) << ToString(p);
447 :
448 12 : if (firstArg == "-qsearch") {
449 1 : DepthType seldepth = 0;
450 1 : ScoreType s = ThreadPool::instance().main().qsearchNoPruning(-10000, 10000, p, 1, seldepth);
451 1 : Logging::LogIt(Logging::logInfo) << "QScore " << s;
452 : return true;
453 : }
454 :
455 : #ifdef WITH_SYZYGY
456 11 : if (firstArg == "-probe"){
457 0 : Logging::LogIt(Logging::logInfo) << "Probing TB";
458 0 : if ((BB::countBit(p.allPieces[Co_White] | p.allPieces[Co_Black])) <= SyzygyTb::MAX_TB_MEN) {
459 0 : ScoreType tbScore = 0;
460 0 : Logging::LogIt(Logging::logInfo) << "Probing root";
461 0 : if (MoveList movesTB; SyzygyTb::probe_root(ThreadPool::instance().main(), p, tbScore, movesTB) >= 0) { // only good moves if TB success
462 0 : for (auto m : movesTB ){
463 0 : Logging::LogIt(Logging::logInfo) << "TB move " << ToString(m);
464 : }
465 0 : Logging::LogIt(Logging::logInfo) << "Score " << tbScore;
466 : }
467 : else {
468 0 : Logging::LogIt(Logging::logInfo) << "TB failed";
469 : }
470 0 : Logging::LogIt(Logging::logInfo) << "Probing wdl";
471 0 : if (SyzygyTb::probe_wdl(p, tbScore, false) > 0) {
472 0 : Logging::LogIt(Logging::logInfo) << "Score " << tbScore;
473 : }
474 : else {
475 0 : Logging::LogIt(Logging::logInfo) << "TB failed";
476 : }
477 : }
478 : else {
479 0 : Logging::LogIt(Logging::logInfo) << "No TB hit";
480 : }
481 0 : return true;
482 : }
483 : #endif
484 :
485 11 : if ( firstArg == "-kpk"){
486 1 : Logging::LogIt(Logging::logInfo) << "Probing KPK";
487 1 : const Color winningSide = p.pieces_const<P_wp>(Co_White) == emptyBitBoard ? Co_Black : Co_White;
488 1 : std::cout << "KPK score : " << MaterialHash::helperKPK(p, winningSide, 0, 1) << std::endl;
489 : return true;
490 : }
491 :
492 10 : if (firstArg == "-see") {
493 1 : Square from = INVALIDSQUARE;
494 1 : Square to = INVALIDSQUARE;
495 1 : MType mtype = T_std;
496 1 : const std::string move = argc > 3 ? args[3] : "e2e4";
497 1 : readMove(p, move, from, to, mtype);
498 1 : Move m = ToMove(from, to, mtype);
499 1 : ScoreType t = clampInt<ScoreType>(atoi(args[4]));
500 1 : bool b = Searcher::SEE_GE(p, m, t);
501 2 : Logging::LogIt(Logging::logInfo) << "SEE ? " << (b ? "ok" : "not");
502 : return true;
503 : }
504 :
505 9 : if (firstArg == "-attacked") {
506 : Square k = Sq_e4;
507 2 : if (argc > 3) k = clampIntU<Square>(atoi(args[3]));
508 1 : Logging::LogIt(Logging::logInfo) << SquareNames[k];
509 2 : Logging::LogIt(Logging::logInfo) << ToString(BBTools::allAttackedBB(p, k, p.c));
510 1 : return true;
511 : }
512 :
513 8 : if (firstArg == "-cov") {
514 : Square k = Sq_e4;
515 2 : if (argc > 3) k = clampIntU<Square>(atoi(args[3]));
516 1 : switch (p.board_const(k)) {
517 2 : case P_wp: Logging::LogIt(Logging::logInfo) << ToString((BBTools::coverage<P_wp>(k, p.occupancy(), p.c) + BBTools::mask[k].push[p.c]) &~p.allPieces[Co_White]); break;
518 0 : case P_wn: Logging::LogIt(Logging::logInfo) << ToString(BBTools::coverage<P_wn>(k, p.occupancy(), p.c) & ~p.allPieces[Co_White]); break;
519 0 : case P_wb: Logging::LogIt(Logging::logInfo) << ToString(BBTools::coverage<P_wb>(k, p.occupancy(), p.c) & ~p.allPieces[Co_White]); break;
520 0 : case P_wr: Logging::LogIt(Logging::logInfo) << ToString(BBTools::coverage<P_wr>(k, p.occupancy(), p.c) & ~p.allPieces[Co_White]); break;
521 0 : case P_wq: Logging::LogIt(Logging::logInfo) << ToString(BBTools::coverage<P_wq>(k, p.occupancy(), p.c) & ~p.allPieces[Co_White]); break;
522 0 : case P_wk: Logging::LogIt(Logging::logInfo) << ToString(BBTools::coverage<P_wk>(k, p.occupancy(), p.c) & ~p.allPieces[Co_White]); break;
523 0 : case P_bk: Logging::LogIt(Logging::logInfo) << ToString(BBTools::coverage<P_wk>(k, p.occupancy(), p.c) & ~p.allPieces[Co_Black]); break;
524 0 : case P_bq: Logging::LogIt(Logging::logInfo) << ToString(BBTools::coverage<P_wq>(k, p.occupancy(), p.c) & ~p.allPieces[Co_Black]); break;
525 0 : case P_br: Logging::LogIt(Logging::logInfo) << ToString(BBTools::coverage<P_wr>(k, p.occupancy(), p.c) & ~p.allPieces[Co_Black]); break;
526 0 : case P_bb: Logging::LogIt(Logging::logInfo) << ToString(BBTools::coverage<P_wb>(k, p.occupancy(), p.c) & ~p.allPieces[Co_Black]); break;
527 0 : case P_bn: Logging::LogIt(Logging::logInfo) << ToString(BBTools::coverage<P_wn>(k, p.occupancy(), p.c) & ~p.allPieces[Co_Black]); break;
528 : case P_bp:
529 0 : Logging::LogIt(Logging::logInfo) << ToString((BBTools::coverage<P_wp>(k, p.occupancy(), p.c) + BBTools::mask[k].push[p.c]) &
530 0 : ~p.allPieces[Co_Black]);
531 0 : break;
532 0 : default: Logging::LogIt(Logging::logInfo) << ToString(emptyBitBoard);
533 : }
534 1 : return true;
535 : }
536 :
537 7 : if (firstArg == "-eval") {
538 1 : EvalData data;
539 1 : if (DynamicConfig::useNNUE) DynamicConfig::forceNNUE = true;
540 1 : const ScoreType score = eval(p, data, ThreadPool::instance().main(), true, true);
541 1 : Logging::LogIt(Logging::logInfo) << "eval " << score << " phase " << data.gp;
542 : return true;
543 : }
544 :
545 6 : if (firstArg == "-evalHCE") {
546 1 : EvalData data;
547 1 : if (DynamicConfig::useNNUE) DynamicConfig::useNNUE = false;
548 1 : const ScoreType score = eval(p, data, ThreadPool::instance().main(), true, true);
549 1 : Logging::LogIt(Logging::logInfo) << "eval " << score << " phase " << data.gp;
550 : return true;
551 : }
552 :
553 5 : if (firstArg == "-gen") {
554 1 : MoveList moves;
555 1 : MoveGen::generate<MoveGen::GP_all>(p, moves);
556 1 : CMHPtrArray cmhPtr = {nullptr};
557 1 : MoveSorter::scoreAndSort(ThreadPool::instance().main(), moves, p, 0.f, 0, cmhPtr);
558 1 : Logging::LogIt(Logging::logInfo) << "nb moves : " << moves.size();
559 41 : for (const auto & it : moves) { Logging::LogIt(Logging::logInfo) << ToString(it, true); }
560 : return true;
561 : }
562 :
563 4 : if (firstArg == "-testmove") {
564 : ///@todo
565 : constexpr Move m = ToMove(8, 16, T_std);
566 1 : Position p2 = p;
567 : #if defined(WITH_NNUE) && defined(DEBUG_NNUE_UPDATE)
568 : p2.associateEvaluator(evaluator);
569 : p2.resetNNUEEvaluator(p2.evaluator());
570 : #endif
571 1 : if (const MoveInfo moveInfo(p2, m); !applyMove(p2, moveInfo)) {
572 0 : Logging::LogIt(Logging::logError) << "Cannot apply this move";
573 : }
574 2 : Logging::LogIt(Logging::logInfo) << ToString(p2);
575 : return true;
576 1 : }
577 :
578 3 : if (firstArg == "-perft") {
579 : DepthType d = 5;
580 2 : if (argc > 3) d = clampIntU<DepthType>(atoi(args[3]));
581 1 : PerftAccumulator acc;
582 1 : auto start = Clock::now();
583 1 : perft(p, d, acc);
584 : auto elapsed = getTimeDiff(start);
585 1 : Logging::LogIt(Logging::logInfo) << "Perft done in " << elapsed << "ms";
586 1 : acc.Display();
587 2 : Logging::LogIt(Logging::logInfo) << "Speed " << static_cast<int>(acc.validNodes / elapsed) << "KNPS";
588 : return true;
589 : }
590 :
591 2 : if (firstArg == "-analyze") {
592 : DepthType depth = 15;
593 4 : if (argc > 3) depth = clampIntU<DepthType>(atoi(args[3]));
594 2 : auto start = Clock::now();
595 2 : analyze(p, depth);
596 : auto elapsed = getTimeDiff(start);
597 2 : Logging::LogIt(Logging::logInfo) << "Analysis done in " << elapsed << "ms";
598 : return true;
599 : }
600 :
601 0 : if (firstArg == "-mateFinder") {
602 0 : DynamicConfig::mateFinder = true;
603 : DepthType depth = 10;
604 0 : if (argc > 3) depth = clampIntU<DepthType>(atoi(args[3]));
605 0 : auto start = Clock::now();
606 0 : analyze(p, depth);
607 : auto elapsed = getTimeDiff(start);
608 0 : Logging::LogIt(Logging::logInfo) << "Analysis done in " << elapsed << "ms";
609 : return true;
610 : }
611 :
612 0 : Logging::LogIt(Logging::logError) << "unknown command line : " << firstArg;
613 0 : return false;
614 13 : }
|