Support UCCI protocol
authorFabian Fichter <ianfab@users.noreply.github.com>
Sat, 9 Nov 2019 14:54:04 +0000 (15:54 +0100)
committerFabian Fichter <ianfab@users.noreply.github.com>
Sat, 9 Nov 2019 14:54:04 +0000 (15:54 +0100)
http://www.xqbase.com/protocol/cchess_ucci.htm

Closes #46.

Readme.md
src/search.h
src/thread.cpp
src/uci.cpp
src/ucioption.cpp

index 4e6d993..04011af 100644 (file)
--- a/Readme.md
+++ b/Readme.md
@@ -5,7 +5,7 @@
 [![Build Status](https://travis-ci.org/ianfab/Fairy-Stockfish.svg?branch=master)](https://travis-ci.org/ianfab/Fairy-Stockfish)
 [![Build Status](https://ci.appveyor.com/api/projects/status/github/ianfab/Fairy-Stockfish?branch=master&svg=true)](https://ci.appveyor.com/project/ianfab/Fairy-Stockfish/branch/master)
 
-Fairy-Stockfish is a UCI/USI/XBoard chess variant engine derived from [Stockfish](https://github.com/official-stockfish/Stockfish/) designed for the support of fairy chess variants and easy extensibility with more games. It can play various historical, regional, and modern chess variants as well [games with user-defined rules](https://github.com/ianfab/Fairy-Stockfish/wiki/Variant-configuration).
+Fairy-Stockfish is a UCI/UCCI/USI/XBoard chess variant engine derived from [Stockfish](https://github.com/official-stockfish/Stockfish/) designed for the support of fairy chess variants and easy extensibility with more games. It can play various historical, regional, and modern chess variants as well [games with user-defined rules](https://github.com/ianfab/Fairy-Stockfish/wiki/Variant-configuration).
 
 The goal of the project is to create an engine supporting a large variety of chess-like games, equipped with the powerful search of Stockfish. It is complementary to Stockfish forks more specialized for certain chess variants, such as [multi-variant Stockfish](https://github.com/ddugovic/Stockfish), [Seirawan-Stockfish](https://github.com/ianfab/Seirawan-Stockfish), [Makruk-Stockfish](https://github.com/ianfab/Makruk-Stockfish), etc., supporting many more variants with the tradeoff of slightly lower performance compared to a specialized implementation.
 
index c77ca3a..10c04e1 100644 (file)
@@ -93,7 +93,7 @@ struct LimitsType {
     return !(mate | movetime | depth | nodes | perft | infinite);
   }
 
-  std::vector<Move> searchmoves;
+  std::vector<Move> searchmoves, banmoves;
   TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime;
   int movestogo, depth, mate, perft, infinite;
   int64_t nodes;
index 5b4decd..a8c18a6 100644 (file)
@@ -184,8 +184,8 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
   Search::RootMoves rootMoves;
 
   for (const auto& m : MoveList<LEGAL>(pos))
-      if (   limits.searchmoves.empty()
-          || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m))
+      if (   (limits.searchmoves.empty() || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m))
+          && (limits.banmoves.empty() || !std::count(limits.banmoves.begin(), limits.banmoves.end(), m)))
           rootMoves.emplace_back(m);
 
   if (!rootMoves.empty())
index bad9884..d6177e9 100644 (file)
@@ -85,6 +85,9 @@ namespace {
 
     is >> token; // Consume "name" token
 
+    if (Options["Protocol"] == "ucci")
+        name = token;
+    else
     // Read option name (can contain spaces)
     while (is >> token && token != "value")
         name += (name.empty() ? "" : " ") + token;
@@ -95,6 +98,8 @@ namespace {
 
     if (Options.count(name))
         Options[name] = value;
+    else if (Options["Protocol"] == "ucci" && (std::replace(name.begin(), name.end(), '_', ' '), Options.count(name)))
+        Options[name] = value;
     else
         sync_cout << "No such option: " << name << sync_endl;
   }
@@ -104,7 +109,7 @@ namespace {
   // the thinking time and other parameters from the input string, then starts
   // the search.
 
-  void go(Position& pos, istringstream& is, StateListPtr& states) {
+  void go(Position& pos, istringstream& is, StateListPtr& states, const std::vector<Move>& banmoves = {}) {
 
     Search::LimitsType limits;
     string token;
@@ -112,6 +117,8 @@ namespace {
 
     limits.startTime = now(); // As early as possible!
 
+    limits.banmoves = banmoves;
+
     while (is >> token)
         if (token == "searchmoves")
             while (is >> token)
@@ -129,6 +136,11 @@ namespace {
         else if (token == "perft")     is >> limits.perft;
         else if (token == "infinite")  limits.infinite = 1;
         else if (token == "ponder")    ponderMode = true;
+        // UCCI commands
+        else if (token == "time")      is >> limits.time[pos.side_to_move()];
+        else if (token == "opptime")   is >> limits.time[~pos.side_to_move()];
+        else if (token == "increment") is >> limits.inc[pos.side_to_move()];
+        else if (token == "oppinc")    is >> limits.inc[~pos.side_to_move()];
 
     Threads.start_thinking(pos, states, limits, ponderMode);
   }
@@ -268,12 +280,14 @@ void UCI::loop(int argc, char* argv[]) {
       else if (token == "ponderhit")
           Threads.main()->ponder = false; // Switch to normal search
 
-      else if (token == "uci" || token == "usi" || token == "xboard")
+      else if (token == "uci" || token == "usi" || token == "ucci" || token == "xboard")
       {
           Options["Protocol"] = token;
+          if (token == "ucci")
+              Options["UCI_Variant"] = string("xiangqi");
           if (token != "xboard")
               sync_cout << "id name " << engine_info(true)
-                          << "\n"       << Options
+                          << "\n" << Options
                           << "\n" << token << "ok"  << sync_endl;
       }
 
@@ -292,7 +306,8 @@ void UCI::loop(int argc, char* argv[]) {
               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 << "\""
+              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;
           }
@@ -434,9 +449,13 @@ void UCI::loop(int argc, char* argv[]) {
       }
 
       else if (token == "setoption")  setoption(is);
-      else if (token == "go")         go(pos, is, states);
-      else if (token == "position")   position(pos, is, states);
-      else if (token == "ucinewgame" || token == "usinewgame") Search::clear();
+      // 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();
+      else if (token == "ucinewgame" || token == "usinewgame" || token == "uccinewgame") Search::clear();
       else if (token == "isready")    sync_cout << "readyok" << sync_endl;
 
       // Additional custom non-UCI commands, mainly for debugging.
@@ -495,7 +514,7 @@ std::string UCI::square(const Position& pos, Square s) {
                                   : std::string{ char('0' + (pos.max_file() - file_of(s) + 1) / 10),
                                                  char('0' + (pos.max_file() - file_of(s) + 1) % 10),
                                                  char('a' + pos.max_rank() - rank_of(s)) };
-  else if (Options["Protocol"] == "xboard" && pos.max_rank() == RANK_10)
+  else if ((Options["Protocol"] == "xboard" || Options["Protocol"] == "ucci") && pos.max_rank() == RANK_10)
       return std::string{ char('a' + file_of(s)), char('0' + rank_of(s)) };
   else
       return rank_of(s) < RANK_10 ? std::string{ char('a' + file_of(s)), char('1' + (rank_of(s) % 10)) }
index fbffb90..d4f653b 100644 (file)
@@ -95,7 +95,7 @@ void init(OptionsMap& o) {
   // at most 2^32 clusters.
   constexpr int MaxHashMB = Is64Bit ? 131072 : 2048;
 
-  o["Protocol"]              << Option("uci", {"uci", "usi", "xboard"});
+  o["Protocol"]              << Option("uci", {"uci", "usi", "ucci", "xboard"});
   o["Debug Log File"]        << Option("", on_logger);
   o["Contempt"]              << Option(24, -100, 100);
   o["Analysis Contempt"]     << Option("Both", {"Both", "Off", "White", "Black"});
@@ -161,7 +161,14 @@ std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
           if (it.second.idx == idx)
           {
               const Option& o = it.second;
-              os << "\noption name " << it.first << " type " << o.type;
+              if (Options["Protocol"] == "ucci")
+              {
+                  string name = it.first;
+                  std::replace(name.begin(), name.end(), ' ', '_');
+                  os << "\noption " <<  name << " type " << o.type;
+              }
+              else
+                  os << "\noption name " << it.first << " type " << o.type;
 
               if (o.type == "string" || o.type == "check" || o.type == "combo")
                   os << " default " << o.defaultValue;