Belofte version 2.1.9
A promising chess program using the UCI or Winboard interface
epd_testsuite.cpp
Go to the documentation of this file.
1/*---------------------------------------------------------------------+
2 * File: epd_testsuite.cpp
3 * Project: part of belofte - A Promising Chess Program
4 * Author: yves
5 * SPDX-License-Identifier: GPL-2.0-only
6+----------------------------------------------------------------------*/
7
8#include "belofte.h"
9
10bEpd::operator std::string() const
11{
12 std::stringstream ss;
13 ss << m_Fen << " => ";
14 for (const auto &m : m_Keys)
15 ss << "[" << m.first << "] = " << m.second << "; ";
16 ss << "\n";
17 return ss.str();
18}
19
20//-----------------------------------------------------------------------
21
22/**
23 * EPD pos is fen-board fen-tomove fen-castling fen-ep [50moves] [move#] cmdsection*
24 * cmdsection is bm SanMove! | am SanMove! | id=["]identification["]
25 * id ["]identification["] | D[1-99] n | n[,n[,n]...]
26 */
27void bEpd::setPos(std::string const& s)
28{
30 if (sFenSgmt.size() >= 4) {
31 // decompose perft elements, splitting -,20,400 into list["-","20","400"]
32 belofte::stringList sPerftCommaElem = belofte::stringSplit(sFenSgmt[3], ",");
33 if (sPerftCommaElem.size() > 1) {
34 sFenSgmt[3] = sPerftCommaElem[0];
35 }
36 // compose FEN part
37 m_Fen = sFenSgmt[0] + " " + sFenSgmt[1] + " "
38 + sFenSgmt[2] + " " + sFenSgmt[3];
39 sFenSgmt.erase(sFenSgmt.begin(), sFenSgmt.begin() + 4);
40
41 // 50 move counter
42 if (belofte::is_number(sFenSgmt[0])) {
43 m_Fen += " " + sFenSgmt[0];
44 sFenSgmt.erase(sFenSgmt.begin());
45 } else {
46 m_Fen += " 0";
47 }
48
49 // move number
50 if (belofte::is_number(sFenSgmt[0])) {
51 m_Fen += " " + sFenSgmt[0];
52 sFenSgmt.erase(sFenSgmt.begin());
53 } else {
54 m_Fen += " 1";
55 }
56
57 // compose command part based on remainder of line
58 if (sPerftCommaElem.size() > 1) {
59 // convert list["-","20","400"] to D1 20 ;D2 400 ;D3 8902 ;
60 std::stringstream sPerftSection;
61 for (unsigned long i = 1; (i < EPD_PERFTMAXDEPTH) && (sPerftCommaElem.size() > i); ++i) {
62 sPerftSection << "D" << belofte::to_string(static_cast<int32_t>(i)) << " "
63 << sPerftCommaElem[i] << ";";
64 }
65 sFenSgmt.push_back(sPerftSection.str());
66 }
67
68 // make sure FEN extension starts with ;
69 std::string sCmd = ";" + std::accumulate(sFenSgmt.begin(), sFenSgmt.end(), std::string(),
70 [](std::string result, std::string sElem) {
71 return std::move(result) + std::move(sElem) + " ";
72 });
73 belofte::stringList sCmdSgmt = belofte::stringSplit(sCmd, ";");
74
75 // remove leading - trailing space(s) on opcodes
76 for (auto it = sCmdSgmt.begin(); it != sCmdSgmt.end(); ++it) {
77 *it = belofte::alltrim(*it);
78 }
79
80 m_Keys.clear();
81 // split each command in between opcode and info
82 // remove quotes from info
83 for (const auto &sElem : sCmdSgmt) {
84 if (sElem.length() > 0) {
85 /// @todo do only consider opcode starting with letter
86 if (sElem.substr(0,3) == "id=") {
87 /// @note backwards compatibility with belofte 0.9.x, which has testfiles where
88 /// id is followed by = sign instead of space
89 std::pair<std::string, std::string> sCmdOpcode = belofte::decompose(sElem, "=");
90 m_Keys[sCmdOpcode.first] = belofte::alltrim(sCmdOpcode.second, "\"");
91 } else {
92 // any other command followed by space, e.g. bm | am | id | D[1-99] |
93 // c[0-9] | acs | dm
94 std::pair<std::string, std::string> sCmdOpcode = belofte::decompose(sElem, " ");
95 m_Keys[sCmdOpcode.first] = belofte::alltrim(sCmdOpcode.second, "\"");
96 }
97 }
98 }
99
100 m_valid = true;
101 }
102}
103
104std::string const bEpd::getTag(std::string const& tag)
105{
106 if (m_Keys.find(tag) != m_Keys.end()) {
107 return m_Keys[tag];
108 }
109 return "";
110}
111
112//-----------------------------------------------------------------------
113
114/**
115 * parse any epd position
116 */
118{
120 return parseSTSPosition(bFen(m_Fen));
121 }
122
124 return parsePerftPosition(bFen(m_Fen));
125 }
126
127 // check if any D[1-99] is in list
128 bool bPerft = false;
129 for (int i = 1; i <= EPD_PERFTMAXDEPTH; i++) {
130 std::string sOp = "D" + belofte::to_string(i);
131 if (m_Keys.find(sOp) != m_Keys.end()) { bPerft = true; break; }
132 }
133 if (bPerft) {
134 return parsePerftPosition(bFen(m_Fen));
135 }
136
137 // this is any other epd test
138 return parseEpdPosition(bFen(m_Fen));
139}
140
142{
143 bEpdMoveValues moveValues;
144 setScore(0);
145 if (m_Keys.find("c0") != m_Keys.end()) {
146 belofte::stringList c0Parts = belofte::stringSplit(m_Keys["c0"], ":");
147 // drop part before : character
148 if (c0Parts.size() > 1) c0Parts.erase(c0Parts.begin());
149 if (!c0Parts.empty()) {
150 // convert c0 tag into keypairs
151 // e.g. f5=10, Be5+=2, Bf2=3, Bg4=2
152 belofte::stringList sC0Segment = belofte::stringSplit(c0Parts.front(), ",");
153 for (auto it = sC0Segment.begin(); it != sC0Segment.end(); ++it) {
154 // remove leading - trailing space(s) on m_Keys
155 /// @todo protect against malformed STS containing no = sign
156 std::pair<std::string, std::string> sMoveVal = belofte::decompose(*it, "=");
157 moveValues[belofte::alltrim(sMoveVal.first)] = atoi(belofte::alltrim(sMoveVal.second).c_str());
158 }
159 }
160 } else if (m_Keys.find("bm") == m_Keys.end()) {
161 // neither bm or c0 is present
163 }
164
165 if (m_Keys.find("bm") != m_Keys.end()) {
166 // find all bm and add them to list if not yet present in list
167 belofte::stringList const sMoves = belofte::stringSplit(m_Keys["bm"], " ");
168 for (auto it = sMoves.begin(); it != sMoves.end(); ++it) {
169 // remove leading - trailing space(s) on moves
170 std::string sMove = belofte::alltrim(*it);
171 if (moveValues.find(sMove) == moveValues.end()) {
172 // not present yet
173 moveValues.insert(std::make_pair(sMove,10));
174 }
175 }
176 }
177
178 bMove m(Game()->getEpdMoveInPosition(fen));
179 bBoard const& b = Game()->getCurrentPosition();
180 bPgnMove pgnmove(b, m.getBMoveT());
181 int score = translateSTSResult(pgnmove, moveValues);
182 setScore(score);
184}
185
186/**
187 * do actual epd position test
188 */
190{
191 bMove m(Game()->getEpdMoveInPosition(fen));
192 bBoard const& currentPosition = Game()->getCurrentPosition();
193 bPgnMove pgnmove(currentPosition, m.getBMoveT());
194 return translateEpdResult(pgnmove);
195}
196
197/**
198 * check move found against epd line m_Keys, change level
199 * before starting search
200 * am avoid-moves
201 * bm best-moves
202 * acs # set search seconds
203 * acn # set search nodes
204 * dm # set direct mate depth
205 */
207{
208 if (m_Keys.find("bm") != m_Keys.end()) {
209 belofte::stringList const sMoves = belofte::stringSplit(m_Keys["bm"], " ");
210 if (std::any_of(sMoves.begin(), sMoves.end(),
211 [m](std::string const& sm)
212 { return m == sm; })) {
214 }
215 } else if (m_Keys.find("am") != m_Keys.end()) {
216 // am and bm are exclusive
217 belofte::stringList const sMoves = belofte::stringSplit(m_Keys["am"], " ");
218 if (std::none_of(sMoves.begin(), sMoves.end(),
219 [m](std::string const& sm)
220 { return m == sm; })) {
222 }
223 } /// @todo implement other opcodes such as mate search
224
225 App().bout << "found : " << m << " - ";
227}
228
230{
231 std::string ms = m;
232 if (moveValues.find(ms) != moveValues.end()) {
233 return moveValues[ms];
234 }
235 return 0;
236}
237
238/**
239 * check perft result for different depths
240 * D[1-99] # perft test - nodes
241 */
243{
245 Game()->newGame();
246 Game()->setFEN(fen);
247
248 // Parse all D1-D99 m_Keys as perft tests
249 for (int i = 1; i <= EPD_PERFTMAXDEPTH; i++) {
250 std::string sOp = "D" + belofte::to_string(i);
251 if (m_Keys.find(sOp) != m_Keys.end()) {
252 std::string sPerftResult = belofte::alltrim(m_Keys[sOp]);
253 int64_t nodes = Game()->DoPerftCommand(static_cast<depth_t>(i));
254 if (sPerftResult != belofte::to_string(nodes)) {
255 App().bout << "Not resolved, found nodes : " << belofte::to_string(nodes) << " - "
256 << std::string(*this);
258 break;
259 }
260 }
261 }
262
263 return result;
264}
265
266//-----------------------------------------------------------------------
267
268bEpdPos::bEpdPos(std::string const& s, epdTest_t const typeOfTest)
269 : bEpd(typeOfTest)
270{
271 setPos(s);
272}
273
274bEpdPos::bEpdPos(bEpdParamList const& sParams, epdTest_t const typeOfTest)
275 : bEpd(typeOfTest)
276{
277 std::string s;
278 for (const auto &sElem : sParams) s += sElem + " ";
279 setPos(s);
280}
281
282//-----------------------------------------------------------------------
283
284#if defined(__GNUC__)
285#pragma GCC diagnostic push
286#pragma GCC diagnostic ignored "-Weffc++"
287#endif
288
289bEpdFile::bEpdFile(std::string const& sFileName, epdTest_t const typeOfTest)
290 : bEpd(typeOfTest)
291{
292 handleFile(sFileName);
293}
294
295bEpdFile::bEpdFile(bEpdParamList const& sParams, epdTest_t const typeOfTest)
296 : bEpd(typeOfTest)
297{
298 if (!sParams.empty()) {
299 handleFile(sParams[0]);
300 }
301}
302
303#if defined(__GNUC__)
304#pragma GCC diagnostic pop
305#endif
306
308{
309 if (m_sEpdFile.is_open()) m_sEpdFile.close();
310}
311
312//-----------------------------------------------------------------------
313
314void bEpdFile::handleFile(std::string const& sFileName)
315{
316 if (sFileName.empty()) {
317 AppEI()->sendError("Filename missing", sFileName);
318 } else {
319 m_sEpdFile.open(sFileName.c_str(), std::ios::in );
320 if (!m_sEpdFile.is_open()) {
321 AppEI()->sendError("Could not open file", sFileName);
322 } else {
323 AppEI()->sendDebug(1, "Reading " + sFileName);
324 }
325 }
326}
327
328/**
329 * Main epd parser for multiple lines of epd file
330 * Will calculate a total score and some statistics
331 */
333{
334 int nGoods = 0;
335 int nTotal = 0;
336 if (!m_sEpdFile.is_open()) return bEpd::tReturn::EPD_ERROR;
337 std::string line;
338 while(getline(m_sEpdFile,line)) {
339 if ((line.length() < 6)
340 || (line.substr(0,1) == ";")
341 || (line.substr(0,1) == "#")) {
342 /// @todo consider other prefixes to be skipped (e.g. //, %)
343 // skip
344 } else {
345 setPos(line);
346 bEpdResult result = bEpd::parse();
347 std::string sID = getTag("id");
348 if (result == bEpd::tReturn::EPD_ERROR) {
349 App().bout << "Error : " << std::string(*this);
351 } else if (result >= bEpd::tReturn::PERFT_OK) {
352 App().bout << "Perft OK : " << std::string(*this);
353 nGoods += bEpd::tReturn::BM_OK;
354 } else if (result == bEpd::tReturn::EPD_OK) {
355 continue; // skip
356 } else if (result == bEpd::tReturn::STS_OK) {
357 // we get actual score instead of maximum score
358 int sc = getScore();
359 nGoods += sc;
360 /// @todo consider to use verbosity level to dictate output
361 App().bout << "score : " << std::setw(2) << sc
362 << " Total : " << std::setw(4) << nGoods;
363 if (!sID.empty()) App().bout << " (" << sID << ")";
364 App().bout.endl();
365 } else if (result >= bEpd::tReturn::BM_OK) {
366 // AM_OK, MATE_OK, ... are equal to maximum score
367 // so we just return maximum score
368 nGoods += bEpd::tReturn::BM_OK;
369 } else if (result == bEpd::tReturn::PERFT_MISS) {
370 // no logging, we already reported before
371 } else if (result == bEpd::tReturn::NOT_RESOLVED) {
372 /// @todo consider to use verbosity level to dictate output
373 App().bout << "Not resolved";
374 if (!sID.empty()) App().bout << " (" << sID << ")";
375 App().bout.endl();
376 // skip
377 } else if (result == bEpd::tReturn::NO_MOVE_FOUND) {
378 App().bout << "No move found : " << std::string(*this);
379 }
380 // total is a multiple of BM_OK (10)
381 nTotal += bEpd::tReturn::BM_OK;
382 }
383 }
384 m_sEpdFile.close();
385
387 // score of STS test is relative to total value
388 App().bout << "Result " << std::setprecision(3) << std::defaultfloat
389 << static_cast<float>(nGoods)/static_cast<float>(bEpd::tReturn::BM_OK)
390 << "/" << static_cast<float>(nTotal)/static_cast<float>(bEpd::tReturn::BM_OK);
391 } else {
392 App().bout << "Result " << nGoods / bEpd::tReturn::BM_OK
393 << "/" << nTotal / bEpd::tReturn::BM_OK;
394 }
395 if (nTotal > 0) {
396 App().bout << " = " << std::setprecision(4) << std::defaultfloat
397 << static_cast<float>(nGoods * 100.0) / static_cast<float>(nTotal) << "%\n";
398 }
399 App().bout.endl();
400
402}
403
404// eof
405
appInstance & App()
Definition belofte.cpp:225
engineInterface * AppEI()
Definition belofte.cpp:231
bGame * Game()
Definition belofte.cpp:236
This is the main include file, needs to be included before any other include.
int_fast8_t depth_t
Definition belofte.h:103
outputWriter bout
Definition belofte.h:173
constexpr bmove_t getBMoveT() const
Definition basicmove.h:72
board
Definition board.h:45
bEpdFile(std::string const &sFileName, epdTest_t const typeOfTest)
virtual ~bEpdFile() override
virtual bEpdResult parse() override
Main epd parser for multiple lines of epd file Will calculate a total score and some statistics.
int translateSTSResult(bPgnMove const &m, bEpdMoveValues &moveValues)
std::string const getTag(std::string const &tag)
virtual bEpdResult parse()
parse any epd position
bEpdResult parseEpdPosition(bFen const &fen)
do actual epd position test
bEpdResult parseSTSPosition(bFen const &fen)
bEpdResult translateEpdResult(bPgnMove const &m)
check move found against epd line m_Keys, change level before starting search am avoid-moves bm best-...
void setPos(std::string const &s)
EPD pos is fen-board fen-tomove fen-castling fen-ep [50moves] [move#] cmdsection* cmdsection is bm Sa...
bEpd(epdTest_t const typeOfTest)
bEpdResult parsePerftPosition(bFen const &fen)
check perft result for different depths D[1-99] # perft test - nodes
epdTest_t getTestType() const
int getScore() const
void setScore(int const s)
@ NO_MOVE_FOUND
@ NOT_RESOLVED
@ EPD_ERROR
@ PERFT_MISS
bEpdPos(std::string const &sEpd, epdTest_t const typeOfTest)
FEN string.
Definition fen.h:14
void newGame()
Definition game.cpp:24
void setFEN(std::string const &fenstr)
Definition game.cpp:50
int64_t DoPerftCommand(depth_t const d)
do perft search using SearchPerft algorithm at depth
Definition game.cpp:121
bBoard & getCurrentPosition()
Definition game.h:54
Definition move.h:13
PgnMove is for user-interface only.
Definition pgnmove.h:14
virtual void sendDebug(int const l, std::string const &info)
virtual void sendError(std::string const &error, std::string const &description)
outputWriter & endl()
Definition bel_debug.h:68
std::vector< std::string > bEpdParamList
enum tEpdTestType epdTest_t
@ EPD_STS
@ EPD_PERF
std::map< std::string, int > bEpdMoveValues
int bEpdResult
#define EPD_PERFTMAXDEPTH
std::string alltrim(std::string s, std::string const &delim=" ")
trim left and right spaces or delim from string
Definition util.cpp:195
std::pair< std::string, std::string > decompose(std::string const &src, std::string const &delim)
Split delimited long string into a pair based on delimiter e.g.
Definition util.cpp:152
std::string to_string(int16_t value)
std::to_string not compatible on Mac OS (Apple LLVM version 5.0) provide generic utility function
Definition util.cpp:171
stringList const stringSplit(std::string src, std::string const &delim)
Split delimited long string into a vector.
Definition util.cpp:133
std::vector< std::string > stringList
Definition util.h:46
bool is_number(std::string const &s)
Definition util.cpp:205