Line data Source code
1 : #include "extendedPosition.hpp"
2 :
3 : #if defined(WITH_TEST_SUITE) || defined(WITH_PGN_PARSER) || defined(WITH_EVAL_TUNING)
4 :
5 : #include <algorithm>
6 : #include <cctype>
7 : #include <functional>
8 : #include <locale>
9 : #include <numeric>
10 :
11 : #include "com.hpp"
12 : #include "dynamicConfig.hpp"
13 : #include "logging.hpp"
14 : #include "moveGen.hpp"
15 : #include "positionTools.hpp"
16 : #include "searcher.hpp"
17 : #include "tools.hpp"
18 :
19 0 : void split(std::vector<std::string> &v, const std::string &str, const std::string &sep) {
20 : size_t start = 0;
21 : size_t end = 0;
22 0 : while (end != std::string::npos) {
23 : end = str.find(sep, start);
24 0 : v.push_back(str.substr(start, (end == std::string::npos) ? std::string::npos : end - start));
25 0 : start = ((end > (std::string::npos - sep.size())) ? std::string::npos : end + sep.size());
26 : }
27 0 : }
28 :
29 0 : std::vector<std::string> split2(const std::string &line, char sep, char delim) {
30 0 : std::vector<std::string> v;
31 : const char * c = line.c_str(); // prepare to parse the line
32 : bool inSep {false};
33 0 : for (const char *p = c; *p; ++p) { // iterate through the string
34 0 : if (*p == delim) { // toggle flag if we're btw delim
35 0 : inSep = !inSep;
36 : }
37 0 : else if (*p == sep && !inSep) { // if sep OUTSIDE delim
38 0 : std::string str(c, p - c);
39 0 : str.erase(std::remove(str.begin(), str.end(), ':'), str.end());
40 0 : v.push_back(str); // keep the field
41 0 : c = p + 1; // and start parsing next one
42 : }
43 : }
44 0 : v.push_back(std::string(c)); // last field delimited by end of line instead of sep
45 0 : return v;
46 0 : }
47 :
48 0 : inline std::string ltrim(std::string &s) {
49 0 : s.erase(s.begin(), std::ranges::find_if(s, [](int ch) { return !std::isspace(ch); }));
50 0 : return s;
51 : }
52 :
53 0 : ExtendedPosition::ExtendedPosition(const std::string &extFEN, bool withMoveCount): RootPosition(extFEN, withMoveCount) {
54 0 : if (!withMoveCount) {
55 0 : halfmoves = 0;
56 0 : moves = 1;
57 0 : fifty = 0;
58 : }
59 : //Logging::LogIt(Logging::logInfo) << ToString(*this);
60 0 : std::vector<std::string> strList;
61 0 : std::stringstream iss(extFEN);
62 0 : std::copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(), back_inserter(strList));
63 0 : if (strList.size() < (withMoveCount ? 7u : 5u)) Logging::LogIt(Logging::logFatal) << "Not an extended position";
64 0 : std::vector<std::string>(strList.begin() + (withMoveCount ? 6 : 4), strList.end()).swap(strList);
65 : const std::string extendedParamsStr =
66 0 : std::accumulate(strList.begin(), strList.end(), std::string(""), [](const std::string &a, const std::string &b) { return a + ' ' + b; });
67 : //Logging::LogIt(Logging::logInfo) << "extended parameters : " << extendedParamsStr;
68 0 : std::vector<std::string> strParamList;
69 0 : split(strParamList, extendedParamsStr, ";");
70 0 : for (auto e : strParamList) {
71 : //Logging::LogIt(Logging::logInfo) << "extended parameters : " << k << " " << e;
72 0 : e = ltrim(e);
73 0 : if (e.empty()) continue;
74 0 : std::vector<std::string> pair;
75 0 : split(pair, e, " ");
76 0 : if (pair.size() < 2) Logging::LogIt(Logging::logFatal) << "Not un extended parameter pair";
77 0 : std::vector<std::string> values = pair;
78 : values.erase(values.begin());
79 0 : _extendedParams[pair[0]] = values;
80 : //Logging::LogIt(Logging::logInfo) << "extended parameters pair : " << pair[0] << " => " << values[0];
81 0 : }
82 0 : }
83 :
84 0 : bool ExtendedPosition::shallFindBest() { return _extendedParams.contains("bm"); }
85 :
86 0 : bool ExtendedPosition::shallAvoidBad() { return _extendedParams.contains("am"); }
87 :
88 0 : std::vector<std::string> ExtendedPosition::bestMoves() { return _extendedParams["bm"]; }
89 :
90 0 : std::vector<std::string> ExtendedPosition::badMoves() { return _extendedParams["am"]; }
91 :
92 0 : std::vector<std::string> ExtendedPosition::comment0() { return _extendedParams["c0"]; }
93 :
94 0 : std::string ExtendedPosition::id() {
95 0 : if (_extendedParams.contains("id"))
96 0 : return _extendedParams["id"][0];
97 : else
98 0 : return "";
99 : }
100 :
101 : template<typename T> std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
102 : for (auto it = v.begin(); it != v.end(); ++it) { os << *it << " "; }
103 : return os;
104 : }
105 :
106 0 : std::string ExtendedPosition::epdString() const {
107 0 : std::string epd = GetFENShort2(*this) + " ";
108 0 : for (const auto &p : _extendedParams) {
109 0 : epd += p.first + " ";
110 0 : for (const auto &cc : p.second) { epd += cc + " "; }
111 : epd += ";";
112 : }
113 0 : return epd;
114 : }
115 :
116 0 : void ExtendedPosition::test(const std::vector<std::string> &positions,
117 : const std::vector<int> & timeControls,
118 : bool breakAtFirstSuccess,
119 : bool multipleBest,
120 : const std::vector<int> & scores,
121 : std::function<int(int)> eloF,
122 : bool withMoveCount) {
123 0 : struct Results {
124 : int k = 0;
125 : int t = 0;
126 : std::string name;
127 : std::vector<std::string> bm;
128 : std::vector<std::string> am;
129 : std::vector<std::pair<std::string, int>> mea;
130 : std::string computerMove;
131 : int score = 0;
132 : };
133 :
134 0 : if (scores.size() != timeControls.size()) { Logging::LogIt(Logging::logFatal) << "Wrong timeControl versus score vector size"; }
135 :
136 0 : Results **results = new Results *[positions.size()];
137 :
138 0 : unsigned int threads = DynamicConfig::threads; //copy
139 :
140 0 : auto worker = [&](size_t begin, size_t end) {
141 : // run the test and fill results table
142 0 : for (size_t k = begin; k < end; ++k) {
143 0 : Logging::LogIt(Logging::logInfo) << "####################################################";
144 0 : Logging::LogIt(Logging::logInfo) << "Test #" << k << " " << positions[k];
145 0 : results[k] = new Results[timeControls.size()];
146 0 : for (size_t t = 0; t < timeControls.size(); ++t) {
147 0 : ExtendedPosition extP(positions[k], withMoveCount);
148 : #ifdef WITH_NNUE
149 0 : NNUEEvaluator evaluator;
150 : extP.associateEvaluator(evaluator);
151 0 : extP.resetNNUEEvaluator(extP.evaluator());
152 : #endif
153 0 : Logging::LogIt(Logging::logInfo) << "Current test time control " << timeControls[t];
154 :
155 0 : TimeMan::isDynamic = false;
156 0 : TimeMan::nbMoveInTC = -1;
157 0 : TimeMan::msecPerMove = timeControls[t];
158 0 : TimeMan::msecInTC = -1;
159 0 : TimeMan::msecInc = -1;
160 0 : TimeMan::msecUntilNextTC = -1;
161 0 : ThreadPool::instance().currentMoveMs = TimeMan::getNextMSecPerMove(extP);
162 :
163 : ///@todo support threading here by using ThinkAsync ?
164 :
165 0 : ThreadData d;
166 0 : d.p = extP; // assumed slicing
167 0 : ThreadPool::instance().distributeData(d);
168 0 : ThreadPool::instance().main().stopFlag = false;
169 : //COM::position = extP; // only need for display purpose
170 0 : ThreadPool::instance().main().searchDriver(false);
171 0 : d = ThreadPool::instance().main().getData();
172 0 : const Move bestMove = d.best;
173 :
174 0 : results[k][t].name = extP.id();
175 0 : results[k][t].k = static_cast<int>(k);
176 0 : results[k][t].t = timeControls[t];
177 :
178 0 : results[k][t].computerMove = showAlgAbr(bestMove, extP);
179 0 : Logging::LogIt(Logging::logInfo) << "Best move found is " << results[k][t].computerMove;
180 :
181 0 : if (!multipleBest && extP.shallFindBest()) {
182 0 : Logging::LogIt(Logging::logInfo) << "Best move should be " << extP.bestMoves()[0];
183 0 : results[k][t].bm = extP.bestMoves();
184 0 : results[k][t].score = 0;
185 : bool success = false;
186 0 : for (size_t i = 0; i < results[k][t].bm.size(); ++i) {
187 0 : if (results[k][t].computerMove == results[k][t].bm[i]) {
188 0 : results[k][t].score = scores[t];
189 : success = true;
190 0 : break;
191 : }
192 : }
193 0 : if (breakAtFirstSuccess && success) break;
194 : }
195 0 : else if (!multipleBest && extP.shallAvoidBad()) {
196 0 : Logging::LogIt(Logging::logInfo) << "Bad move was " << extP.badMoves()[0];
197 0 : results[k][t].am = extP.badMoves();
198 0 : results[k][t].score = scores[t];
199 : bool success = true;
200 0 : for (size_t i = 0; i < results[k][t].am.size(); ++i) {
201 0 : if (results[k][t].computerMove == results[k][t].am[i]) {
202 0 : results[k][t].score = 0;
203 : success = false;
204 0 : break;
205 : }
206 : }
207 0 : if (breakAtFirstSuccess && success) break;
208 : }
209 : else { // look into c0 section ...
210 0 : Logging::LogIt(Logging::logInfo) << "Mea style " << extP.comment0()[0];
211 0 : std::vector<std::string> tokens = extP.comment0();
212 0 : for (size_t ms = 0; ms < tokens.size(); ++ms) {
213 : std::string tmp = tokens[ms];
214 0 : tmp.erase(std::remove(tmp.begin(), tmp.end(), '"'), tmp.end());
215 0 : tmp.erase(std::remove(tmp.begin(), tmp.end(), ','), tmp.end());
216 : //std::cout << tmp << std::endl;
217 0 : std::vector<std::string> keyval;
218 0 : tokenize(tmp, keyval, "=");
219 0 : if (keyval.size() > 2) { // "=" prom sign inside ...
220 0 : std::stringstream strTmp;
221 : copy(keyval.begin(), keyval.begin() + 1, std::ostream_iterator<std::string>(strTmp, "="));
222 0 : strTmp >> keyval[0];
223 : keyval[1] = keyval.back();
224 0 : }
225 : std::cout << keyval[0] << " " << keyval[1] << std::endl;
226 0 : results[k][t].mea.push_back(std::make_pair(keyval[0], std::stoi(keyval[1])));
227 0 : }
228 0 : results[k][t].score = 0;
229 : bool success = false;
230 0 : constexpr array1d<int, 23> ms = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150,
231 : 175, 200, 250, 300, 400, 500, 600, 700, 800, 900, 10000};
232 0 : constexpr array1d<double, 23> bonus = {3.0, 2.9, 2.8, 2.7, 2.6, 2.5, 2.4, 2.3, 2.2, 2.1, 2.0, 1.9,
233 : 1.8, 1.7, 1.6, 1.5, 1.4, 1.3, 1.2, 1.1, 1.0, 1.0, 0.0};
234 0 : for (size_t i = 0; i < results[k][t].mea.size(); ++i) {
235 0 : if (results[k][t].computerMove == results[k][t].mea[i].first) {
236 0 : results[k][t].score = results[k][t].mea[i].second;
237 : const SearchData &datas = d.datas;
238 : TimeType msec = 1000;
239 0 : DepthType dd = d.depth;
240 0 : for (; dd >= 0; --dd) {
241 0 : if (sameMove(datas.moves[dd], bestMove)) { msec = datas.times[dd]; }
242 : else
243 : break;
244 : }
245 : size_t id = 0;
246 0 : for (; id < 23 && ms[id] < msec; ++id) { ; }
247 :
248 0 : Logging::LogIt(Logging::logInfo) << "Good " << i + 1 << " best move : " << results[k][t].mea[i].first;
249 0 : Logging::LogIt(Logging::logInfo) << "Found at depth " << static_cast<int>(dd) << " in " << datas.times[dd] << " id " << id;
250 0 : Logging::LogIt(Logging::logInfo) << "Bonus " << bonus[id] << " score " << results[k][t].mea[i].second;
251 :
252 0 : results[k][t].score = static_cast<int>(results[k][t].score * bonus[id]);
253 : success = true;
254 : break;
255 : }
256 : }
257 0 : if (breakAtFirstSuccess && success) break;
258 0 : }
259 : }
260 : }
261 0 : };
262 :
263 0 : threadedWork(worker, threads, positions.size());
264 :
265 : // display results
266 : int totalScore = 0;
267 0 : std::cout << std::setw(25) << "Test" << std::setw(14) << "Move";
268 0 : for (size_t j = 0; j < timeControls.size(); ++j) { std::cout << std::setw(8) << timeControls[j]; }
269 : std::cout << std::setw(6) << "score" << std::endl;
270 0 : for (size_t k = 0; k < positions.size(); ++k) {
271 : int score = 0;
272 0 : for (size_t t = 0; t < timeControls.size(); ++t) { score += results[k][t].score; }
273 0 : totalScore += score;
274 0 : std::stringstream str;
275 0 : const std::vector<std::string> v = results[k][0].bm.empty() ? results[k][0].am : results[k][0].bm;
276 : std::ostream_iterator<std::string> strIt(str, " ");
277 0 : std::ranges::copy(v, strIt);
278 0 : std::cout << std::setw(25) << results[k][0].name << std::setw(14) << (results[k][0].bm.empty() ? std::string("!") + str.str() : str.str());
279 0 : for (size_t j = 0; j < timeControls.size(); ++j) { std::cout << std::setw(8) << results[k][j].computerMove; }
280 0 : std::cout << std::setw(6) << score << std::endl;
281 0 : }
282 :
283 0 : Logging::LogIt(Logging::logInfo) << "Total score " << totalScore << " => ELO " << eloF(totalScore);
284 :
285 : // clear results table
286 0 : for (size_t k = 0; k < positions.size(); ++k) { delete[] results[k]; }
287 0 : delete[] results;
288 0 : }
289 :
290 0 : void ExtendedPosition::testStatic(const std::vector<std::string> &positions, bool withMoveCount) {
291 0 : struct Results {
292 : int k = 0;
293 : int t = 0;
294 : ScoreType score = 0;
295 : std::string name;
296 : };
297 :
298 0 : Results *results = new Results[positions.size()];
299 :
300 : // run the test and fill results table
301 0 : for (size_t k = 0; k < positions.size(); ++k) {
302 : std::cout << "Test #" << k << " " << positions[k] << std::endl;
303 0 : ExtendedPosition extP(positions[k], withMoveCount);
304 : //std::cout << " " << t << std::endl;
305 0 : EvalData data;
306 0 : ScoreType ret = eval(extP, data, ThreadPool::instance().main(), true, true);
307 :
308 0 : results[k].name = extP.id();
309 0 : results[k].k = static_cast<int>(k);
310 0 : results[k].score = ret;
311 :
312 0 : std::cout << "score is " << ret << std::endl;
313 : }
314 :
315 : // display results
316 : std::cout << std::setw(25) << "Test" << std::setw(14) << "score" << std::endl;
317 0 : for (size_t k = 0; k < positions.size() - 2; k += 4) {
318 0 : std::cout << std::setw(25) << results[k].name << std::setw(14) << results[k].score << std::endl;
319 : // only compare unsigned score ...
320 0 : if (Abs(Abs(results[k].score) - Abs(results[k + 2].score)) > 0) { Logging::LogIt(Logging::logWarn) << "Score differ !"; }
321 0 : std::cout << std::setw(25) << results[k + 1].name << std::setw(14) << results[k + 1].score << std::endl;
322 : // only compare unsigned score ...
323 0 : if (Abs(Abs(results[k + 1].score) - Abs(results[k + 3].score)) > 0) { Logging::LogIt(Logging::logWarn) << "Score differ !"; }
324 : }
325 :
326 : // clear results table
327 0 : delete[] results;
328 0 : }
329 :
330 : #endif
|