UCILoader 1.1.2
Small C++ library that allows user to connect to a chess engines via UCI protocol.
Loading...
Searching...
No Matches
UCI.h
1#pragma once
2
3#include <string>
4#include <vector>
5#include <cstdint>
6
7namespace UCILoader {
8
9 enum ProcedureStatus {
10 Checking,
11 Ok,
12 Error
13 };
14
15 enum OptionType {
16 Check,
17 Spin,
18 Combo,
19 Button,
20 String
21 };
22
23 enum InfoType {
24 Depth,
25 Seldepth,
26 Time,
27 Nodes,
28 Pv,
29 MultiPv,
30 Score,
31 CurrentMove,
32 CurrentMoveNumber,
33 Hashfull,
34 NodesPerSecond,
35 Tbhiths,
36 Sbhits,
37 CPUload,
38 InfoString,
39 Refutation,
40 CurrentLine,
41 };
42
43
44
45 struct combo_option {
46 std::string value;
47 std::vector<std::string> supported_values;
48 };
49
50 struct spin_option {
51 int32_t value;
52 int32_t min;
53 int32_t max;
54 };
55
56 class Option {
57 std::string id_;
58 OptionType type_;
59 void* content;
60
61 template <class T>
62 void dispose_content_internal() {
63 T* view = (T*)content;
64 delete view;
65 }
66
67 void dispose_content();
68
69 void* deep_copy_content() const;
70 public:
71 Option() : id_("<empty>"), type_(Button), content(nullptr) {};
72 Option(std::string id) : id_(id), type_(Button), content(nullptr) {}; // creates a 'button' type option
73 Option(std::string id, bool value) : id_(id), type_(Check), content(new bool(value)) {}; // creates a 'check' type option with the provided default value
74 Option(std::string id, std::string value) : id_(id), type_(String), content(new std::string(value)) {}; // creates a 'check' type option with the provided default value
75 Option(std::string id, const char* value) : id_(id), type_(String), content(new std::string(value)) {}; // c-string option constructor.
76 Option(std::string id, const spin_option& content) : id_(id), type_(Spin), content(new spin_option(content)) {};
77 Option(std::string id, const combo_option& content) : id_(id), type_(Combo), content(new combo_option(content)) {};
78 Option(const Option& other) : id_(other.id_), type_(other.type_), content(other.deep_copy_content()) {};
79 Option(Option&& other) noexcept : id_(other.id_), type_(other.type_), content(other.content) { other.content = nullptr; other.type_ = Button; };
80 ~Option() { dispose_content(); };
81
82 const OptionType& type() const { return type_; };
83 const std::string& id() const { return id_; };
84
85 std::string& str_content() { return *(std::string*)content; };
86 const std::string& str_content() const { return *(std::string*)content; };
87
88 bool& check_content() { return *(bool*)content; };
89 const bool& check_content() const { return *(bool*)content; };
90
91 spin_option& spin_content() { return *(spin_option*)content; };
92 const spin_option& spin_content() const { return *(spin_option*)content; };
93
94 combo_option& combo_content() { return *(combo_option*)content; };
95 const combo_option& combo_content() const { return *(combo_option*)content; };
96
97 Option& operator=(const Option& other) {
98 dispose_content();
99 id_ = other.id_;
100 type_ = other.type_;
101 content = other.deep_copy_content();
102 return *this;
103 }
104 };
105
106 class UciScore {
107 public:
108 enum BoundType {
109 Exact,
110 Lowerbound,
111 Upperbound
112 };
113
114 enum Unit {
115 Centipawn,
116 Mate
117 };
118
119 private:
120 BoundType _type;
121 Unit _unit;
122 int32_t _value;
123 public:
124
125 UciScore(BoundType _boundType, Unit unit, int32_t value) : _type(_boundType), _unit(unit), _value(value) {};
126 UciScore(const UciScore& other) : _type(other._type), _unit(other._unit), _value(other._value) {};
127
128 static UciScore fromCentipawns(int32_t v, BoundType _type = Exact) {
129 return UciScore(_type, Centipawn, v);
130 }
131
132 static UciScore fromMateDistance(int32_t v, BoundType _type = Exact) {
133 return UciScore(_type, Mate, v);
134 }
135
136 const BoundType& getBoundType() const { return _type; };
137 const Unit& getUnit() const { return _unit; };
138 const int32_t getValue() const { return _value; };
139 };
140
141 template<class Move>
142 class RefutationInfo {
143 Move refutedMove;
144 std::vector<Move> refutationLine;
145 public:
146 template<class MoveIterator>
147 RefutationInfo(const Move& m, MoveIterator ref_begin, MoveIterator ref_end) {
148 refutedMove = m;
149 MoveIterator it = ref_begin;
150 while (it != ref_end) {
151 refutationLine.push_back(*it);
152 it++;
153 };
154 };
155 RefutationInfo(const RefutationInfo& other) : refutedMove(other.refutedMove), refutationLine(other.refutationLine) {};
156 const Move& getRefutedMove() const { return refutedMove; };
157 const std::vector<Move>& getRefutationLine() const { return refutationLine; };
158 RefutationInfo& operator= (const RefutationInfo& other) {
159 this->refutedMove = other.refutedMove;
160 this->refutationLine.clear();
161 this->refutationLine.insert(this->refutationLine.begin(), other.refutationLine.begin(), other.refutationLine.end());
162 };
163 };
164 template <class Move>
165 class CurrentLineInfo {
166 int32_t cpunr;
167 std::vector<Move> currentLine;
168 public:
169 template <class MoveIterator>
170 CurrentLineInfo(const int32_t& cpunr, MoveIterator line_begin, MoveIterator line_end) : cpunr(cpunr) {
171 MoveIterator it = line_begin;
172 while (it != line_end) {
173 currentLine.push_back(*it);
174 ++it;
175 };
176 }
177 CurrentLineInfo(const CurrentLineInfo& other) : cpunr(other.cpunr), currentLine(other.currentLine) {};
178
179 const std::vector<Move>& getCurrentLine() const { return currentLine; };
180 const int32_t& getCPUnr() const { return cpunr; };
181
182 CurrentLineInfo& operator= (const CurrentLineInfo& other) {
183 this->cpunr = other.cpunr;
184 this->currentLine.clear();
185 this->currentLine.insert(this->currentLine.begin(), other.currentLine.begin(), other.currentLine.end());
186 }
187 };
188
189 template <class Move>
190 class Info {
191 public:
192
193 private:
194 union _ContentType {
195 int32_t integer;
196 UciScore score;
197
198 _ContentType(const int32_t & v) {
199 integer = v;
200 }
201
202 _ContentType(const UciScore & s) {
203 score = s;
204 }
205 };
206
207 std::string stringContent;
208 std::vector<Move> moveArray;
209
210 InfoType _type;
211 _ContentType content;
212
213 public:
214 Info(InfoType tp, const int32_t & type, const std::string& value, const std::vector<Move>& moveArray) : _type(tp), content(type),
215 stringContent(value), moveArray(moveArray) {};
216 Info(InfoType tp, const UciScore& type, const std::string& value, const std::vector<Move>& moveArray) : _type(tp), content(type),
217 stringContent(value), moveArray(moveArray) {
218 };
219 Info(const RefutationInfo<Move>& refInfo) : _type(Refutation), content(0), stringContent("") {
220 moveArray.push_back(refInfo.getRefutedMove());
221 auto it = refInfo.getRefutationLine().cbegin();
222 while (it != refInfo.getRefutationLine().cend()) {
223 moveArray.push_back(*it);
224 ++it;
225 }
226 }
227 Info(const CurrentLineInfo<Move>& currLineInfo) : _type(CurrentLine), content(currLineInfo.getCPUnr()), stringContent(""), moveArray(currLineInfo.getCurrentLine()) {};
228 Info(const Info& other) : _type(other._type), content(int32_t(0)), stringContent(other.stringContent), moveArray(other.moveArray) {
229 if (_type == InfoType::Score)
230 content.score = other.content.score;
231 else
232 content.integer = other.content.integer;
233 }
234
235 const InfoType getType() const { return _type; };
236 const int32_t& getIntegerValue() const { return content.integer; };
237 const UciScore getAsScore() const { return content.score; };
238 const std::string& getStringValue() const { return stringContent; };
239 const std::vector<Move> getMoveArray() const { return moveArray; };
240 const Move& getAsCurrentMoveInfo() const { return moveArray[0]; };
241 RefutationInfo<Move> getAsRefutationInfo() const {
242 auto it = moveArray.cbegin();
243 ++it;
244 return RefutationInfo<Move>(moveArray[0], it, moveArray.cend()); }
245 CurrentLineInfo<Move> getAsCurrentLineInfo() const { return CurrentLineInfo<Move>(getIntegerValue(), moveArray.cbegin(), moveArray.cend()); }
246
247 };
248
249
250 #define INTEGER_INFO_FACTORY_METHOD(tp) static Info<Move> make##tp##Info(int32_t value) {return makeIntegerInfo(value, tp);}
251 template <class Move>
252 class InfoFactory {
253 static Info<Move> makeIntegerInfo(int32_t value, InfoType type) {
254 return Info<Move>(type, { value }, "", {});
255 };
256 public:
257 INTEGER_INFO_FACTORY_METHOD(Depth);
258 INTEGER_INFO_FACTORY_METHOD(Seldepth);
259 INTEGER_INFO_FACTORY_METHOD(Time);
260 INTEGER_INFO_FACTORY_METHOD(Nodes);
261 INTEGER_INFO_FACTORY_METHOD(MultiPv);
262 INTEGER_INFO_FACTORY_METHOD(CurrentMoveNumber);
263 INTEGER_INFO_FACTORY_METHOD(Hashfull);
264 INTEGER_INFO_FACTORY_METHOD(NodesPerSecond);
265 INTEGER_INFO_FACTORY_METHOD(Tbhiths);
266 INTEGER_INFO_FACTORY_METHOD(Sbhits);
267 INTEGER_INFO_FACTORY_METHOD(CPUload);
268
269 static Info<Move> makeStringInfo(const std::string& value) {
270 return Info<Move>(InfoString, { 0 }, value, {});
271 }
272
273 static Info<Move> makePvInfo(const std::vector<Move>& line) {
274 return Info<Move>(Pv, { 0 }, "", line);
275 }
276
277 static Info<Move> makeRefutationInfo(const Move& m, const std::vector<Move>& refutation) {
278 return Info<Move>(RefutationInfo<Move>(m, refutation.begin(), refutation.end()));
279 }
280
281 static Info<Move> makeCurrLineInfo(const std::vector<Move>& m, int32_t cpunr = 1) {
282 CurrentLineInfo<Move> currL(cpunr, m.begin(), m.end());
283 return Info<Move>(CurrentLineInfo<Move>(currL));
284 }
285
286 static Info<Move> makeCurrentMoveInfo(const Move& m) {
287 std::vector<Move> vec = { m };
288 return Info<Move>(CurrentMove, { 0 }, "", vec);
289 }
290
291 static Info<Move> makeScoreInfo(const UciScore& score) {
292 return Info<Move>(Score, score , "", {});
293 }
294
295 };
296 #undef INTEGER_INFO_FACTORY_METHOD
297 template <class Move>
298 class AbstractEngineHandler {
299 public:
300 ~AbstractEngineHandler() {};
301
302 virtual void onEngineName(const std::string& name) = 0;
303 virtual void onEngineAuthor(const std::string& author) = 0;
304 virtual void onUCIOK() = 0;
305 virtual void onReadyOK() = 0;
306 virtual void onBestMove(const Move& bestMove) = 0;
307 virtual void onBestMove(const Move& bestMove, const Move& ponderMove) = 0;
308 virtual void onInfo(const std::vector<Info<Move>>& infos) = 0;
309 virtual void onCopyProtection(ProcedureStatus status) = 0;
310 virtual void onRegistration(ProcedureStatus status) = 0;
311 virtual void onOption(const Option& option) = 0;
312 virtual void onError(const std::string& errorMsg) = 0;
313 };
314
315 /*
316 Yes, this struct breaks camelCase conventions by using CupCase naming convention for its members.
317 This exception was made because GoParamsBuilder methods are generated by preprocessor macros and that naming convention allows
318 generated method to follow camelCase instead of snake_case.
319 */
320 template<class Move>
321 struct GoParams {
322 std::vector<Move> SearchMoves;
323 bool Ponder;
324 bool Infinite;
325
326 unsigned int BlackTime;
327 unsigned int BlackIncrement;
328 unsigned int WhiteTime;
329 unsigned int WhiteIncrement;
330 unsigned int MovesToGo;
331 unsigned int Depth;
332 unsigned int Nodes;
333 unsigned int Mate;
334 unsigned int MoveTime;
335 };
336
337 #define SELF GoParamsBuilder<Move>
338 #define WITH_INTEGER_VALUE(member_name) SELF& with##member_name (unsigned int v) {this->result.member_name = v; return *this;}
339 template <class Move>
340 class GoParamsBuilder {
341 GoParams<Move> result;
342 public:
343 GoParamsBuilder();
344
345 SELF& withSearchMoves(const std::vector<Move>& moves) { this->result.SearchMoves = moves; return *this; };
346 WITH_INTEGER_VALUE(MoveTime);
347 WITH_INTEGER_VALUE(MovesToGo);
348 WITH_INTEGER_VALUE(Depth);
349 WITH_INTEGER_VALUE(Nodes);
350 WITH_INTEGER_VALUE(Mate);
351 SELF& withWhiteTime(unsigned int time, unsigned int inc) { this->result.WhiteTime = time; this->result.WhiteIncrement = inc; return *this; }
352 SELF& withBlackTime(unsigned int time, unsigned int inc) { this->result.BlackTime = time; this->result.BlackIncrement = inc; return *this; }
353 SELF& withPondering(bool v) { this->result.Ponder = v; return *this; }
354 SELF& withInfiniteMode(bool v) { this->result.Infinite = v; return *this; }
355 GoParams<Move> build() const { return result; };
356 };
357 #undef WITH_INTEGER_VALUE
358 #undef SELF
359
364 public:
365 virtual std::string toFen() const = 0;
366 virtual std::string toPositionString() const = 0;
367 };
368
369 std::string formatSetOptionCommand(const Option & option);
370
371
372 #define SingleTokenCommand(command) static std::string command() {return std::string(#command) + '\n' ;};
373
374 template<class Move>
375 class UciFormatter {
376 // todo: add set option formatting
377 public:
378 static std::string(*MoveFormatter)(const Move& m);
379
380 SingleTokenCommand(uci);
381 SingleTokenCommand(isready);
382 SingleTokenCommand(ponderhit);
383 SingleTokenCommand(stop);
384 SingleTokenCommand(quit);
385 static std::string debug(bool b) { return b ? "debug on\n" : "debug off\n"; }
386 static std::string registerLater() { return "register later\n"; };
387 static std::string registerEngine(std::string name, std::string code) { return "register name " + name + " code " + code + "\n"; }
388 static std::string position(const PositionFormatter & pos , std::vector<Move> moves = {});
389 static std::string go(const GoParams<Move>& params);
390 static std::string setOpion(const Option& option);
391 };
392
393 #undef SingleTokenCommand
394
395 template<class Move>
396 inline std::string UciFormatter<Move>::position(const PositionFormatter& pos, std::vector<Move> moves)
397 {
398 std::string result = "position " + pos.toPositionString();
399
400 if (!moves.empty()) {
401 result += " moves ";
402
403 for (const Move& m : moves)
404 result += MoveFormatter(m) + " ";
405
406 result.pop_back(); // remove trailing space
407 }
408
409 result += "\n";
410 return result;
411 }
412
413 #define UCI_ADD_IF_NO_ZERO(member_name, uci_name) if(params.member_name) result += std::string(#uci_name) + " " + std::to_string(params.member_name) + " ";
414 template<class Move>
415 inline std::string UciFormatter<Move>::go(const GoParams<Move>& params)
416 {
417 std::string result = "go ";
418
419 UCI_ADD_IF_NO_ZERO(WhiteTime, wtime);
420 UCI_ADD_IF_NO_ZERO(WhiteIncrement, winc);
421 UCI_ADD_IF_NO_ZERO(BlackTime, btime);
422 UCI_ADD_IF_NO_ZERO(BlackIncrement, binc);
423 UCI_ADD_IF_NO_ZERO(MovesToGo, movestogo);
424 UCI_ADD_IF_NO_ZERO(Depth, depth);
425 UCI_ADD_IF_NO_ZERO(Nodes, nodes);
426 UCI_ADD_IF_NO_ZERO(Mate, mate);
427 UCI_ADD_IF_NO_ZERO(MoveTime, movetime);
428
429 if (params.Ponder) result += "ponder ";
430 if (params.Infinite) result += "infinite ";
431
432 if (params.SearchMoves.size()) {
433 result += "searchmoves ";
434 for (const Move& m : params.SearchMoves) {
435 result += MoveFormatter(m);
436 result.push_back(' ');
437 }
438
439 }
440 result.pop_back(); // remove trailing space
441 result.push_back('\n');
442 return result;
443 }
444 #undef UCI_ADD_IF_NO_ZERO
445
446 template<class Move>
447 inline std::string UciFormatter<Move>::setOpion(const Option& option)
448 {
449 return formatSetOptionCommand(option);
450 }
451
452 template<class Move>
453 inline GoParamsBuilder<Move>::GoParamsBuilder()
454 {
455 result.BlackIncrement = result.WhiteIncrement = 0u;
456 result.BlackTime = result.WhiteTime = 0u;
457 result.Depth = result.Mate = result.Nodes = result.MoveTime = result.MovesToGo = 0u;
458 result.Infinite = result.Ponder = false;
459 }
460}
Definition: UCI.h:363