Split UCI and XBoard protocol code for better maintainability.
No functional change.
### Object files
OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \
material.o misc.o movegen.o movepick.o parser.o pawns.o piece.o position.o psqt.o \
- search.o thread.o timeman.o tt.o uci.o ucioption.o variant.o syzygy/tbprobe.o
+ search.o thread.o timeman.o tt.o uci.o ucioption.o variant.o xboard.o syzygy/tbprobe.o
### Establish the operating system name
KERNEL = $(shell uname -s)
#include "timeman.h"
#include "tt.h"
#include "uci.h"
+#include "xboard.h"
#include "syzygy/tbprobe.h"
using namespace std;
Threads.start_thinking(pos, states, limits, ponderMode);
}
- void xboard_go(Position& pos, Search::LimitsType limits, StateListPtr& states) {
-
- limits.startTime = now(); // As early as possible!
-
- Threads.start_thinking(pos, states, limits, false);
- }
-
// bench() is called when engine receives the "bench" command. Firstly
// a list of UCI commands is setup according to bench parameters, then
// it is run one by one printing a summary at the end.
Options["VariantPath"] = token;
}
- // do_move() is called when engine needs to change position state in XBoard mode.
-
- void do_move(Position& pos, std::deque<Move>& moveList, StateListPtr& states, Move m) {
-
- // transfer states back
- if (Threads.setupStates.get())
- states = std::move(Threads.setupStates);
-
- if (m == MOVE_NONE)
- return;
- moveList.push_back(m);
- states->emplace_back();
- pos.do_move(m, states->back());
- }
-
- // undo_move() is called when engine needs to change position state in XBoard mode.
-
- void undo_move(Position& pos, std::deque<Move>& moveList, StateListPtr& states) {
-
- // transfer states back
- if (Threads.setupStates.get())
- states = std::move(Threads.setupStates);
-
- pos.undo_move(moveList.back());
- states->pop_back();
- moveList.pop_back();
- }
-
} // namespace
for (int i = 1; i < argc; ++i)
cmd += std::string(argv[i]) + " ";
- // XBoard states
- Color playColor = COLOR_NB;
- bool move_after_search = false;
- Search::LimitsType limits;
- Search::LimitsType analysis_limits;
- analysis_limits.infinite = 1;
- std::deque<Move> moveList = std::deque<Move>();
+ // XBoard state machine
+ XBoard::StateMachine xboardStateMachine;
+ // UCCI banmoves state
+ std::vector<Move> banmoves = {};
do {
if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF
}
else if (Options["Protocol"] == "xboard")
- {
- if (move_after_search)
- {
- Threads.stop = true;
- Threads.main()->wait_for_search_finished();
- do_move(pos, moveList, states, Threads.main()->bestThread->rootMoves[0].pv[0]);
- move_after_search = false;
- }
- if (token == "protover")
- {
- string vars = "chess";
- for (string v : variants.get_keys())
- if (v != "chess")
- vars += "," + v;
- sync_cout << "feature setboard=1 usermove=1 time=1 memory=1 smp=1 colors=0 draw=0 name=0 sigint=0 myname=Fairy-Stockfish variants=\""
- << vars << "\""
- << Options << sync_endl
- << "feature done=1" << sync_endl;
- }
- else if (token == "accepted" || token == "rejected" || token == "result" || token == "?") {}
- else if (token == "new")
- {
- is = istringstream("startpos");
- position(pos, is, states);
- // play second by default
- playColor = ~pos.side_to_move();
- }
- else if (token == "variant")
- {
- if (is >> token)
- Options["UCI_Variant"] = token;
- is = istringstream("startpos");
- position(pos, is, states);
- }
- else if (token == "force")
- playColor = COLOR_NB;
- else if (token == "go")
- {
- playColor = pos.side_to_move();
- xboard_go(pos, limits, states);
- move_after_search = true;
- }
- else if (token == "level" || token == "st" || token == "sd" || token == "time" || token == "otim")
- {
- int num;
- if (token == "level")
- {
- // moves to go
- is >> limits.movestogo;
- // base time
- is >> token;
- size_t idx = token.find(":");
- if (idx != string::npos)
- num = std::stoi(token.substr(0, idx)) * 60 + std::stoi(token.substr(idx + 1));
- else
- num = std::stoi(token) * 60;
- limits.time[WHITE] = num * 1000;
- limits.time[BLACK] = num * 1000;
- // increment
- is >> num;
- limits.inc[WHITE] = num * 1000;
- limits.inc[BLACK] = num * 1000;
- }
- else if (token == "sd")
- is >> limits.depth;
- else if (token == "st")
- is >> limits.movetime;
- // Note: time/otim are in centi-, not milliseconds
- else if (token == "time")
- {
- is >> num;
- limits.time[playColor != COLOR_NB ? playColor : pos.side_to_move()] = num * 10;
- }
- else if (token == "otim")
- {
- is >> num;
- limits.time[playColor != COLOR_NB ? ~playColor : ~pos.side_to_move()] = num * 10;
- }
- }
- else if (token == "setboard")
- {
- std::getline(is, token);
- is = istringstream("fen" + token);
- position(pos, is, states);
- }
- else if (token == "cores")
- {
- if (is >> token)
- Options["Threads"] = token;
- }
- else if (token == "memory")
- {
- if (is >> token)
- Options["Hash"] = token;
- }
- else if (token == "hard" || token == "easy")
- Options["Ponder"] = token == "hard";
- else if (token == "option")
- {
- string name, value;
- is.get();
- std::getline(is, name, '=');
- std::getline(is, value);
- if (Options.count(name))
- Options[name] = value;
- }
- else if (token == "analyze")
- {
- Options["UCI_AnalyseMode"] = string("true");
- xboard_go(pos, analysis_limits, states);
- }
- else if (token == "exit")
- {
- Threads.stop = true;
- Threads.main()->wait_for_search_finished();
- Options["UCI_AnalyseMode"] = string("false");
- }
- else if (token == "undo")
- {
- if (moveList.size())
- {
- if (Options["UCI_AnalyseMode"])
- {
- Threads.stop = true;
- Threads.main()->wait_for_search_finished();
- }
- undo_move(pos, moveList, states);
- if (Options["UCI_AnalyseMode"])
- xboard_go(pos, analysis_limits, states);
- }
- }
- // Additional custom non-UCI commands, mainly for debugging.
- else if (token == "perft")
- {
- Search::LimitsType perft_limits;
- is >> perft_limits.perft;
- xboard_go(pos, perft_limits, states);
- }
- // Move strings and unknown commands
- else
- {
- // process move string
- if (token == "usermove")
- is >> token;
- if (Options["UCI_AnalyseMode"])
- {
- Threads.stop = true;
- Threads.main()->wait_for_search_finished();
- }
- Move m;
- if ((m = UCI::to_move(pos, token)) != MOVE_NONE)
- do_move(pos, moveList, states, m);
- else
- sync_cout << "Error (unkown command): " << token << sync_endl;
- if (Options["UCI_AnalyseMode"])
- xboard_go(pos, analysis_limits, states);
- else if (pos.side_to_move() == playColor)
- {
- xboard_go(pos, limits, states);
- move_after_search = true;
- }
- }
- }
+ xboardStateMachine.process_command(pos, token, is, states);
else if (token == "setoption") setoption(is);
// UCCI-specific banmoves command
else if (token == "banmoves")
while (is >> token)
- limits.banmoves.push_back(UCI::to_move(pos, token));
- else if (token == "go") go(pos, is, states, limits.banmoves);
- else if (token == "position") position(pos, is, states), limits.banmoves.clear();
+ banmoves.push_back(UCI::to_move(pos, token));
+ else if (token == "go") go(pos, is, states, banmoves);
+ else if (token == "position") position(pos, is, states), banmoves.clear();
else if (token == "ucinewgame" || token == "usinewgame" || token == "uccinewgame") Search::clear();
else if (token == "isready") sync_cout << "readyok" << sync_endl;
--- /dev/null
+/*
+ Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
+ Copyright (C) 2018-2019 Fabian Fichter
+
+ Fairy-Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fairy-Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <iostream>
+#include <string>
+
+#include "search.h"
+#include "thread.h"
+#include "types.h"
+#include "uci.h"
+#include "xboard.h"
+
+namespace {
+
+ const Search::LimitsType analysisLimits = []{
+ Search::LimitsType limits;
+ limits.infinite = 1;
+ return limits;
+ }();
+
+ // go() starts the search for game play, analysis, or perft.
+
+ void go(Position& pos, Search::LimitsType limits, StateListPtr& states) {
+
+ limits.startTime = now(); // As early as possible!
+
+ Threads.start_thinking(pos, states, limits, false);
+ }
+
+ // setboard() is called when engine receives the "setboard" XBoard command.
+
+ void setboard(Position& pos, StateListPtr& states, std::string fen = "") {
+
+ if (fen.empty())
+ fen = variants.find(Options["UCI_Variant"])->second->startFen;
+
+ states = StateListPtr(new std::deque<StateInfo>(1)); // Drop old and create a new one
+ pos.set(variants.find(Options["UCI_Variant"])->second, fen, Options["UCI_Chess960"], &states->back(), Threads.main());
+ }
+
+ // do_move() is called when engine needs to apply a move when using XBoard protocol.
+
+ void do_move(Position& pos, std::deque<Move>& moveList, StateListPtr& states, Move m) {
+
+ // transfer states back
+ if (Threads.setupStates.get())
+ states = std::move(Threads.setupStates);
+
+ if (m == MOVE_NONE)
+ return;
+ moveList.push_back(m);
+ states->emplace_back();
+ pos.do_move(m, states->back());
+ }
+
+ // undo_move() is called when the engine receives the undo command in XBoard protocol.
+
+ void undo_move(Position& pos, std::deque<Move>& moveList, StateListPtr& states) {
+
+ // transfer states back
+ if (Threads.setupStates.get())
+ states = std::move(Threads.setupStates);
+
+ pos.undo_move(moveList.back());
+ states->pop_back();
+ moveList.pop_back();
+ }
+
+} // namespace
+
+namespace XBoard {
+
+/// StateMachine::process_command() processes commands of the XBoard protocol.
+
+void StateMachine::process_command(Position& pos, std::string token, std::istringstream& is, StateListPtr& states) {
+ if (moveAfterSearch)
+ {
+ Threads.stop = true;
+ Threads.main()->wait_for_search_finished();
+ do_move(pos, moveList, states, Threads.main()->bestThread->rootMoves[0].pv[0]);
+ moveAfterSearch = false;
+ }
+ if (token == "protover")
+ {
+ std::string vars = "chess";
+ for (std::string v : variants.get_keys())
+ if (v != "chess")
+ vars += "," + v;
+ sync_cout << "feature setboard=1 usermove=1 time=1 memory=1 smp=1 colors=0 draw=0 name=0 sigint=0 myname=Fairy-Stockfish variants=\""
+ << vars << "\""
+ << Options << sync_endl
+ << "feature done=1" << sync_endl;
+ }
+ else if (token == "accepted" || token == "rejected" || token == "result" || token == "?") {}
+ else if (token == "new")
+ {
+ setboard(pos, states);
+ // play second by default
+ playColor = ~pos.side_to_move();
+ }
+ else if (token == "variant")
+ {
+ if (is >> token)
+ Options["UCI_Variant"] = token;
+ setboard(pos, states);
+ }
+ else if (token == "force")
+ playColor = COLOR_NB;
+ else if (token == "go")
+ {
+ playColor = pos.side_to_move();
+ go(pos, limits, states);
+ moveAfterSearch = true;
+ }
+ else if (token == "level" || token == "st" || token == "sd" || token == "time" || token == "otim")
+ {
+ int num;
+ if (token == "level")
+ {
+ // moves to go
+ is >> limits.movestogo;
+ // base time
+ is >> token;
+ size_t idx = token.find(":");
+ if (idx != std::string::npos)
+ num = std::stoi(token.substr(0, idx)) * 60 + std::stoi(token.substr(idx + 1));
+ else
+ num = std::stoi(token) * 60;
+ limits.time[WHITE] = num * 1000;
+ limits.time[BLACK] = num * 1000;
+ // increment
+ is >> num;
+ limits.inc[WHITE] = num * 1000;
+ limits.inc[BLACK] = num * 1000;
+ }
+ else if (token == "sd")
+ is >> limits.depth;
+ else if (token == "st")
+ is >> limits.movetime;
+ // Note: time/otim are in centi-, not milliseconds
+ else if (token == "time")
+ {
+ is >> num;
+ limits.time[playColor != COLOR_NB ? playColor : pos.side_to_move()] = num * 10;
+ }
+ else if (token == "otim")
+ {
+ is >> num;
+ limits.time[playColor != COLOR_NB ? ~playColor : ~pos.side_to_move()] = num * 10;
+ }
+ }
+ else if (token == "setboard")
+ {
+ std::string fen;
+ std::getline(is >> std::ws, fen);
+ setboard(pos, states, fen);
+ }
+ else if (token == "cores")
+ {
+ if (is >> token)
+ Options["Threads"] = token;
+ }
+ else if (token == "memory")
+ {
+ if (is >> token)
+ Options["Hash"] = token;
+ }
+ else if (token == "hard" || token == "easy")
+ Options["Ponder"] = token == "hard";
+ else if (token == "option")
+ {
+ std::string name, value;
+ is.get();
+ std::getline(is, name, '=');
+ std::getline(is, value);
+ if (Options.count(name))
+ Options[name] = value;
+ }
+ else if (token == "analyze")
+ {
+ Options["UCI_AnalyseMode"] = std::string("true");
+ go(pos, analysisLimits, states);
+ }
+ else if (token == "exit")
+ {
+ Threads.stop = true;
+ Threads.main()->wait_for_search_finished();
+ Options["UCI_AnalyseMode"] = std::string("false");
+ }
+ else if (token == "undo")
+ {
+ if (moveList.size())
+ {
+ if (Options["UCI_AnalyseMode"])
+ {
+ Threads.stop = true;
+ Threads.main()->wait_for_search_finished();
+ }
+ undo_move(pos, moveList, states);
+ if (Options["UCI_AnalyseMode"])
+ go(pos, analysisLimits, states);
+ }
+ }
+ // Additional custom non-XBoard commands
+ else if (token == "perft")
+ {
+ Search::LimitsType perft_limits;
+ is >> perft_limits.perft;
+ go(pos, perft_limits, states);
+ }
+ // Move strings and unknown commands
+ else
+ {
+ // process move string
+ if (token == "usermove")
+ is >> token;
+ if (Options["UCI_AnalyseMode"])
+ {
+ Threads.stop = true;
+ Threads.main()->wait_for_search_finished();
+ }
+ Move m;
+ if ((m = UCI::to_move(pos, token)) != MOVE_NONE)
+ do_move(pos, moveList, states, m);
+ else
+ sync_cout << "Error (unkown command): " << token << sync_endl;
+ if (Options["UCI_AnalyseMode"])
+ go(pos, analysisLimits, states);
+ else if (pos.side_to_move() == playColor)
+ {
+ go(pos, limits, states);
+ moveAfterSearch = true;
+ }
+ }
+}
+
+} // namespace XBoard
--- /dev/null
+/*
+ Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
+ Copyright (C) 2018-2019 Fabian Fichter
+
+ Fairy-Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fairy-Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef XBOARD_H_INCLUDED
+#define XBOARD_H_INCLUDED
+
+#include <sstream>
+#include <string>
+
+#include "types.h"
+
+class Position;
+
+namespace XBoard {
+
+/// StateMachine class maintains the states required by XBoard protocol
+
+class StateMachine {
+public:
+ StateMachine() {
+ moveList = std::deque<Move>();
+ moveAfterSearch = false;
+ playColor = COLOR_NB;
+ }
+ void process_command(Position& pos, std::string token, std::istringstream& is, StateListPtr& states);
+
+private:
+ std::deque<Move> moveList;
+ Search::LimitsType limits;
+ bool moveAfterSearch;
+ Color playColor;
+};
+
+} // namespace XBoard
+
+#endif // #ifndef XBOARD_H_INCLUDED