Belofte version 2.1.8
A promising chess program using the UCI or Winboard interface
game.cpp
Go to the documentation of this file.
1/*---------------------------------------------------------------------+
2 * File: game.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
10#if defined(__GNUC__)
11#pragma GCC diagnostic push
12#pragma GCC diagnostic ignored "-Weffc++"
13#endif
14
16{
17 newGame();
18}
19
20#if defined(__GNUC__)
21#pragma GCC diagnostic pop
22#endif
23
25{
26}
27
29{
30 m_result = GR_UNKNOWN;
31 m_pgnTags.clear();
32 m_pgnTags["Event"] = "Running on " MYOS " " MYPLATFORM;
33 m_pgnTags["Site"] = "";
34 m_pgnTags["Date"] = belofte::currentDate();
35 m_pgnTags["Round"] = "";
36 m_pgnTags["White"] = MYNAME " " MYVERSION " build " MYRELEASEDATE;
37 m_pgnTags["Black"] = MYNAME " " MYVERSION " build " MYRELEASEDATE;
38 m_pgnTags["Result"] = getResult(m_result);
39 m_optTags.clear();
40 m_optTags["eloWhite"] = "";
41 m_optTags["eloBlack"] = "";
42 m_positions.clear();
44}
45
47{
48 m_positions.clear();
49 m_positions.emplace_back(bFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"));
50 m_optTags.erase("SetUp");
51 m_optTags.erase("FEN");
52}
53
54void bGame::setFEN(std::string const& fenstr)
55{
56 m_positions.clear();
57 m_positions.emplace_back(bFen(fenstr));
58 m_optTags["SetUp"] = "1";
59 m_optTags["FEN"] = fenstr;
60}
61
62void bGame::setPlayerName(std::string const& n)
63{
64 if (getCurrentPosition().whiteToMove()) {
65 m_pgnTags["White"] = n;
66 } else {
67 m_pgnTags["Black"] = n;
68 }
69}
70
71// TODO: check why copy constructor is called, check if we should return
72// anything else but positions.back()
74{
75 return m_positions.back();
76}
77
78/** Start search thread and exit
79 * in case of batch mode, will wait until end of search
80 */
82{
83 if (App().m_reader.isBatchMode()) {
84 /// in case of batch mode, do not start separate thread for searching
85 /// @todo allow for cancel search with ? / stop command
87 } else {
88 if (getCurrentPosition().whiteToMove()) {
89 m_pgnTags["White"] = App().getConfig("enginename", "");
90 m_pgnTags["Black"] = App().getConfig("opponent", "");
91 } else {
92 m_pgnTags["White"] = App().getConfig("opponent", "");
93 m_pgnTags["Black"] = App().getConfig("enginename", "");
94 }
95 std::thread th(&bGame::WaitForSearchEnd, this);
96 th.detach();
97 }
98}
99
104
105/**
106 * Called in separate thread, sure to terminate normally
107 */
109{
111 try {
113 bMove m(b.getMove(result.moveid));
114 AppEI()->sendMove(b, m);
116 AppEI()->sendResult(b, getResult());
117 } catch (const noMoveFoundException&) {
118 // ignore error
119 AppEI()->sendError("no move found", "Wait for search");
120 } catch (...) { throw; // push other exceptions down
121 }
122}
123
124/** do perft search at depth
125 */
126int64_t bGame::DoPerft(bSearchAlgorithm& search, int const d)
127{
128 bLevel oldLevel = m_level;
130 m_level.setDepthCommand(static_cast<depth_t>(d));
131
132 int iSearchVerbose = App().sout.getLevel();
133 App().sout.setLevel(-2);
134 int iOutputVerbose = App().bout.getLevel();
135 App().bout.setLevel(-1);
136
137 try {
138 search.SearchBestMove(b);
139 } catch (const noMoveFoundException&) {
140 // ignore error
141 } catch (...) { throw; // push other exceptions down
142 }
143
144 App().bout.setLevel(iOutputVerbose);
145 App().sout.setLevel(iSearchVerbose);
146
147 m_level = oldLevel;
148 return search.getNodes();
149}
150
152{
153 return getEval()->getEvaluation(b) * b.minimizing();
154}
155
157{
158 return getEval()->isGameEnded(b);
159}
160
161/**
162 * Following command should return value as expected
163 */
164void bGame::setExpecting(std::string const& s)
165{
166 m_expecting = s;
167}
168
169// TODO: improve by redirecting App().bout
170bool bGame::isExpecting(std::string const& s) const
171{
172 if (m_expecting == "" || m_expecting == s) {
173 return true;
174 }
175 return false;
176}
177
178std::string const& bGame::getExpecting() const
179{
180 return m_expecting;
181}
182
183std::string bGame::getResult(gameResult_t rs) const
184{
185 if (rs == GR_UNKNOWN) return "*";
186 if ((rs >= GR_DRAW_STALEMATE) && (rs <= GR_DRAW_OTHER)) return "1/2-1/2";
187 if ((rs >= GR_WHITEMATES) && (rs <= GR_WHITEWINS_FLAG)) return "1-0";
188 return "0-1";
189}
190
192{
193 m_result = rs;
194 m_pgnTags["Result"] = getResult(m_result);
195}
196
197/** all moves in a single string
198 * @return false if one move cannot be played
199 */
200bool bGame::playGameMoveSeries(std::string const& coordmoves)
201{
202 std::vector<std::string> vstrings = belofte::stringSplit(coordmoves, " ");
203
204 return std::all_of(vstrings.begin(), vstrings.end(),
205 [this](std::string const& s){ return this->playGameMove(bCoordMove(s)); });
206}
207
208bool bGame::playGameMove(bCoordMove const& coordmove)
209{
211 bMoveList ml(b);
212 movenum_t n_moves = ml.getNumberOfMoves();
213 for (movenum_t moveid = 1; moveid <= n_moves; moveid++) {
214 bMove m(ml[moveid]);
215 bCoordMove cm(m);
216 if (cm == coordmove) {
217 m_positions.emplace_back(b, const_cast<bMove&> (m));
219 if ((sc & SCORE_POSITIVE) == SCORE_THEORETIC_DRAW) {
221 } else if ((sc & SCORE_POSITIVE) == SCORE_PUNDEFINED) {
222 // skip
223 } else if (sc <= (-SCORE_MATE + SCORE_CONVERGE_BYDEPTH)) {
225 } else if (sc >= (SCORE_MATE - SCORE_CONVERGE_BYDEPTH)) {
227 }
228 return true;
229 }
230 }
231 return false;
232}
233
234/**
235 * required for Winboard protocol, not supported in UCI (except debug)
236 */
238{
239 m_positions.pop_back();
242}
243
244/** do actual epd position test
245 */
247 bEpdOpCodes& opcodes)
248{
249 bEpdResult iReturn;
250 newGame();
251 setFEN(fen);
252 int iOldSoutLevel = App().sout.getLevel();
253 App().sout.setLevel(-2);
254
255 bBoard& currentPosition = getCurrentPosition();
256 try {
257 bBestMoveInfo result = getAlgorithm()->SearchBestMove(currentPosition);
258 bMove m(currentPosition.getMove(result.moveid));
259 bPgnMove pgnmove(currentPosition, m);
260 iReturn = evalEpdResult(opcodes, pgnmove);
261 } catch (const noMoveFoundException&) {
263 } catch (...) { throw; // push other exceptions down
264 }
265
266 App().sout.setLevel(iOldSoutLevel);
267 return iReturn;
268}
269
270/** check move found against epd line opcodes, change level
271 * before starting search
272 * am avoid-moves
273 * bm best-moves
274 * acs # set search seconds
275 * acn # set search nodes
276 * dm # set direct mate depth
277 */
278bEpdResult bGame::evalEpdResult(bEpdOpCodes& opcodes, bPgnMove const& m)
279{
280 if (opcodes.find("bm") != opcodes.end()) {
281 belofte::stringList const sMoves = belofte::stringSplit(opcodes["bm"], " ");
282 if (std::any_of(sMoves.begin(), sMoves.end(),
283 [m](std::string const& sm)
284 { return m == sm; })) {
286 }
287 } else if (opcodes.find("am") != opcodes.end()) {
288 // am and bm are exclusive
289 belofte::stringList const sMoves = belofte::stringSplit(opcodes["am"], " ");
290 if (std::none_of(sMoves.begin(), sMoves.end(),
291 [m](std::string const& sm)
292 { return m == sm; })) {
294 }
295 } // TODO: implement other opcodes such as mate search
296
297 App().bout << "found : " << m << " - ";
299}
300
301/** check perft result for different depths
302 * D[1-99] # perft test - nodes
303 */
305 bEpdOpCodes& opcodes)
306{
308 newGame();
309 setFEN(fen);
310
311 // Parse all D1-D99 opcodes as perft tests
312 for (int i = 1; i <= EPD_PERFTMAXDEPTH; i++) {
313 std::string sOp = "D" + belofte::to_string(i);
314 if (opcodes.find(sOp) != opcodes.end()) {
315 std::string sPerftResult = belofte::alltrim(opcodes[sOp]);
316 SearchPerft search;
317 int64_t nodes = DoPerft(search, i);
318 if (sPerftResult != belofte::to_string(nodes)) {
319 App().bout << "found nodes : " << belofte::to_string(nodes) << " - ";
321 break;
322 }
323 }
324 }
325
326 return result;
327}
328
329// TODO: Look at using stream and << operator instead of string
330std::string bGame::movesinpgnformat() const
331{
332 std::stringstream sMoves;
333 std::stringstream sMovePrefix;
334
335 if (m_positions.size() > 1) {
336 if (!m_positions[0].whiteToMove()) {
337 sMovePrefix << m_positions[0].getMoveNumber() << "... ";
338 }
339
340 for (unsigned int i = 1; i < m_positions.size(); i++) {
341 if (m_positions[i].getMovePlayed()) {
342 if (m_positions[i - 1].whiteToMove()) {
343 sMovePrefix << m_positions[i].getMoveNumber() << ". ";
344 } else if (i > 1) {
345 sMovePrefix.str("");
346 }
347 bBoard b(m_positions[i - 1]);
348 bPgnMove pm(b, m_positions[i].getMovePlayed());
349 sMoves << sMovePrefix.str() << pm << " ";
350 }
351 }
352 sMoves << getResult(getResult());
353 }
354
355 return sMoves.str();
356}
357
358// TODO: Look at using stream and << operator instead of std::string
359bGame::operator std::string() const
360{
361 std::string sGameHeader = "";
362
363 // Export tags (7-tag roster)
364 for (auto const& x : m_pgnTags) {
365 sGameHeader += "[" + x.first + " \"" + x.second + "\"]" + "\n";
366 }
367
368 // only in extended PGN format, for values that are present
369 for (auto const& x : m_optTags) {
370 if (x.second.size()) {
371 sGameHeader += "[" + x.first + " \"" + x.second + "\"]" + "\n";
372 }
373 }
374 return sGameHeader + "\n" + movesinpgnformat();
375}
376
377// eof
appInstance & App()
Definition belofte.cpp:480
engineInterface * AppEI()
Definition belofte.cpp:486
bGame * Game()
Definition belofte.cpp:491
This is the main include file, needs to be included before any other include.
#define MYRELEASEDATE
Definition belofte.h:44
uint_fast8_t movenum_t
Definition belofte.h:109
#define MYVERSION
Definition belofte.h:33
#define MYNAME
Definition belofte.h:31
int_fast8_t depth_t
Definition belofte.h:112
int16_t bScore
outputWriter sout
normal output
Definition belofte.h:331
outputWriter bout
Definition belofte.h:330
int64_t getConfig(std::string const &s, int64_t v)
Definition belofte.cpp:438
bool whiteToMove() const
Definition board.h:80
movenum_t moveid
board
Definition board.h:147
bMove const & getMove(movenum_t const moveid) const
Definition board.cpp:967
bMoveList & getMoveListRef()
return reference to movelist
Definition board.cpp:954
bScore minimizing() const
Definition board.h:180
bSearchAlgorithm * getAlgorithm() const
bPositionEvaluation * getEval() const
simple coordmove, with 4 characters, or 5 characters in case of promotion mostly used for interface
Definition coordmove.h:14
@ NO_MOVE_FOUND
@ NOT_RESOLVED
FEN string.
Definition fen.h:14
std::string const & getExpecting() const
Definition game.cpp:178
bScore EvalForPlayer(bBoard const &b)
Definition game.cpp:151
bool playGameMove(bCoordMove const &coordmove)
Definition game.cpp:208
void revertGameMove()
required for Winboard protocol, not supported in UCI (except debug)
Definition game.cpp:237
void setPlayerName(std::string const &n)
Definition game.cpp:62
bEpdResult evalPerftResult(bFen const &fen, bEpdOpCodes &opcodes)
check perft result for different depths D[1-99] # perft test - nodes
Definition game.cpp:304
void WaitForSearchEnd()
Called in separate thread, sure to terminate normally.
Definition game.cpp:108
void setExpecting(std::string const &s)
Following command should return value as expected.
Definition game.cpp:164
void AbortSearch()
Definition game.cpp:100
void newGame()
Definition game.cpp:28
bLevel & getLevel()
Definition game.h:53
void DoSearch()
Start search thread and exit in case of batch mode, will wait until end of search.
Definition game.cpp:81
gameResult_t EvalFinalScore(bBoard const &b)
Definition game.cpp:156
bGame()
Definition game.cpp:15
bool playGameMoveSeries(std::string const &coordmoves)
all moves in a single string
Definition game.cpp:200
void setResult(gameResult_t rs)
Definition game.cpp:191
void setFEN(std::string const &fenstr)
Definition game.cpp:54
gameResult_t getResult() const
Definition game.h:42
bEpdResult evalEpdPosition(bFen const &fen, bEpdOpCodes &opcodes)
do actual epd position test
Definition game.cpp:246
~bGame() override
Definition game.cpp:24
int64_t DoPerft(bSearchAlgorithm &sa, int const d)
do perft search at depth
Definition game.cpp:126
bBoard & getCurrentPosition()
Definition game.cpp:73
bool isExpecting(std::string const &s) const
Definition game.cpp:170
void setFENInitialPos()
Definition game.cpp:46
Definition level.h:49
void flagLevelChanged()
Definition level.cpp:27
void setDepthCommand(depth_t const d)
Definition level.cpp:250
Definition move.h:69
void emptyMoveList()
Definition movelist.cpp:284
movenum_t getNumberOfMoves() const
Definition movelist.cpp:332
PgnMove is for user-interface only.
Definition pgnmove.h:12
virtual bScore getEvaluation(bBoard const &b) const =0
gameResult_t isGameEnded(bBoard const &b) const
See if board is in finite state, meaning game is ended.
Definition eval.cpp:38
void InterruptSearch()
Definition search.cpp:273
int64_t getNodes() const
Definition search.h:92
bBestMoveInfo SearchBestMove(bBoard &b)
Generic search, will call (non-)recursive method per algorithm only when there are moves to be played...
Definition search.cpp:152
virtual void sendResult(bBoard const &b, gameResult_t rs) const
Definition usercmd.cpp:351
virtual void sendMove(bBoard &b, bMove const &m)
Definition usercmd.cpp:345
virtual void sendError(std::string const &error, std::string const &description)
Definition usercmd.cpp:156
int getLevel() const
Definition bel_debug.h:47
void setLevel(int const l)
Definition bel_debug.h:46
int bEpdResult
#define EPD_PERFTMAXDEPTH
std::map< std::string, std::string > bEpdOpCodes
@ GR_DRAW_OTHER
Definition eval.h:36
@ GR_DRAW_STALEMATE
Definition eval.h:35
@ GR_WHITEWINS_FLAG
Definition eval.h:37
@ GR_UNKNOWN
Definition eval.h:34
@ GR_WHITEMATES
Definition eval.h:37
@ GR_BLACKMATES
Definition eval.h:38
constexpr bScore SCORE_MATE
Definition eval.h:23
enum gameResult gameResult_t
Definition eval.h:41
constexpr bScore SCORE_PUNDEFINED
Definition eval.h:19
constexpr bScore SCORE_CONVERGE_BYDEPTH
Definition eval.h:29
constexpr bScore SCORE_POSITIVE
Definition eval.h:16
constexpr bScore SCORE_THEORETIC_DRAW
Definition eval.h:17
#define MYPLATFORM
Definition myplatform.h:73
#define MYOS
Definition myplatform.h:117