]> git.sesse.net Git - stockfish/commitdiff
Merge remote-tracking branch 'upstream/master'
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 18 Jan 2020 18:47:17 +0000 (19:47 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 18 Jan 2020 18:47:17 +0000 (19:47 +0100)
1  2 
src/main.cpp
src/misc.cpp
src/position.cpp
src/syzygy/tbprobe.cpp
src/ucioption.cpp

diff --combined src/main.cpp
index 4afb57ae6846300b881202f832a98015144b43b8,148bf248f5337aec842666bdeb42df8d163837b1..dc8487e994b73098308c7eae72512de066f22ef0
@@@ -2,7 -2,7 +2,7 @@@
    Stockfish, a UCI chess playing engine derived from Glaurung 2.1
    Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
    Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
-   Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+   Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
  
    Stockfish is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
  
 +#include <deque>
  #include <iostream>
 +#include <stack>
 +#include <thread>
  
  #include "bitboard.h"
  #include "position.h"
  #include "endgame.h"
  #include "syzygy/tbprobe.h"
  
 +#include <grpc/grpc.h>
 +#include <grpc++/server.h>
 +#include <grpc++/server_builder.h>
 +#include "hashprobe.h"
 +#include "hashprobe.grpc.pb.h"
 +#include "tt.h"
 +
 +using grpc::Server;
 +using grpc::ServerBuilder;
 +using grpc::ServerContext;
 +using grpc::Status;
 +using grpc::StatusCode;
 +using namespace hashprobe;
 +
 +Status HashProbeImpl::Probe(ServerContext* context,
 +                            const HashProbeRequest* request,
 +                          HashProbeResponse *response) {
 +      Position pos;
 +      StateInfo st;
 +      pos.set(request->fen(), /*isChess960=*/false, &st, Threads.main());
 +      if (!pos.pos_is_ok()) {
 +              return Status(StatusCode::INVALID_ARGUMENT, "Invalid FEN");
 +      }
 +
 +      bool invert = (pos.side_to_move() == BLACK);
 +      StateListPtr setup_states = StateListPtr(new std::deque<StateInfo>(1));
 +
 +      ProbeMove(&pos, setup_states.get(), invert, response->mutable_root());
 +
 +      MoveList<LEGAL> moves(pos);
 +      for (const ExtMove* em = moves.begin(); em != moves.end(); ++em) {
 +              HashProbeLine *line = response->add_line();
 +              FillMove(&pos, em->move, line->mutable_move());
 +              setup_states->push_back(StateInfo());
 +              pos.do_move(em->move, setup_states->back());
 +              ProbeMove(&pos, setup_states.get(), !invert, line);
 +              pos.undo_move(em->move);
 +      }
 +
 +      return Status::OK;
 +}
 +
 +void HashProbeImpl::FillMove(Position *pos, Move move, HashProbeMove* decoded) {
 +      if (!is_ok(move)) return;
 +
 +      Square from = from_sq(move);
 +      Square to = to_sq(move);
 +
 +      if (type_of(move) == CASTLING) {
 +              to = make_square(to > from ? FILE_G : FILE_C, rank_of(from));
 +      }
 +
 +      Piece moved_piece = pos->moved_piece(move);
 +      std::string pretty;
 +      if (type_of(move) == CASTLING) {
 +              if (to > from) {
 +                      pretty = "O-O";
 +              } else {
 +                      pretty = "O-O-O";
 +              }
 +      } else if (type_of(moved_piece) == PAWN) {
 +              if (type_of(move) == ENPASSANT || pos->piece_on(to) != NO_PIECE) {
 +                      // Capture.
 +                      pretty = char('a' + file_of(from));
 +                      pretty += "x";
 +              }
 +              pretty += UCI::square(to);
 +              if (type_of(move) == PROMOTION) {
 +                      pretty += "=";
 +                      pretty += " PNBRQK"[promotion_type(move)];
 +              }
 +      } else {
 +              pretty = " PNBRQK"[type_of(moved_piece)];
 +              Bitboard attackers = pos->attackers_to(to) & pos->pieces(color_of(moved_piece), type_of(moved_piece));
 +              if (more_than_one(attackers)) {
 +                      // Remove all illegal moves to disambiguate.
 +                      Bitboard att_copy = attackers;
 +                      while (att_copy) {
 +                              Square s = pop_lsb(&att_copy);
 +                              Move m = make_move(s, to);
 +                              if (!pos->pseudo_legal(m) || !pos->legal(m)) {
 +                                      attackers &= ~SquareBB[s];
 +                              }
 +                      }
 +              }
 +              if (more_than_one(attackers)) {
 +                      // Disambiguate by file if possible.
 +                      Bitboard attackers_this_file = attackers & file_bb(file_of(from));
 +                      if (attackers != attackers_this_file) {
 +                              pretty += char('a' + file_of(from));
 +                              attackers = attackers_this_file;
 +                      }
 +                      if (more_than_one(attackers)) {
 +                              // Still ambiguous, so need to disambiguate by rank.
 +                              pretty += char('1' + rank_of(from));
 +                      }
 +              }
 +
 +              if (type_of(move) == ENPASSANT || pos->piece_on(to) != NO_PIECE) {
 +                      pretty += "x";
 +              }
 +
 +              pretty += UCI::square(to);
 +      }
 +
 +      if (pos->gives_check(move)) {
 +              // Check if mate.
 +              StateInfo si;
 +              pos->do_move(move, si, true);
 +              if (MoveList<LEGAL>(*pos).size() > 0) {
 +                      pretty += "+";
 +              } else {
 +                      pretty += "#";
 +              }
 +              pos->undo_move(move);
 +      }
 +
 +      decoded->set_pretty(pretty);
 +}
 +
 +void HashProbeImpl::ProbeMove(Position* pos, std::deque<StateInfo>* setup_states, bool invert, HashProbeLine* response) {
 +      bool found;
 +      TTEntry *entry = TT.probe(pos->key(), found);
 +      response->set_found(found);
 +      if (found) {
 +              Value value = entry->value();
 +              Value eval = entry->eval();
 +              Bound bound = entry->bound();
 +
 +              if (invert) {
 +                      value = -value;
 +                      eval = -eval;
 +                      if (bound == BOUND_UPPER) {
 +                              bound = BOUND_LOWER;
 +                      } else if (bound == BOUND_LOWER) {
 +                              bound = BOUND_UPPER;
 +                      }
 +              }
 +
 +              response->set_depth(entry->depth());
 +              FillValue(eval, response->mutable_eval());
 +              if (entry->depth() > DEPTH_NONE) {
 +                      FillValue(value, response->mutable_value());
 +              }
 +              response->set_bound(HashProbeLine::ValueBound(bound));
 +
 +              // Follow the PV until we hit an illegal move.
 +              std::stack<Move> pv;
 +              std::set<Key> seen;
 +              while (found && is_ok(entry->move()) &&
 +                     pos->pseudo_legal(entry->move()) &&
 +                     pos->legal(entry->move())) {
 +                      FillMove(pos, entry->move(), response->add_pv());
 +                      if (seen.count(pos->key())) break;
 +                      pv.push(entry->move());
 +                      seen.insert(pos->key());
 +                      setup_states->push_back(StateInfo());
 +                      pos->do_move(entry->move(), setup_states->back());
 +                      entry = TT.probe(pos->key(), found);
 +              }
 +
 +              // Unroll the PV back again, so the Position object remains unchanged.
 +              while (!pv.empty()) {
 +                      pos->undo_move(pv.top());
 +                      pv.pop();
 +              }
 +      }
 +}
 +
 +void HashProbeImpl::FillValue(Value value, HashProbeScore* score) {
 +      if (abs(value) < VALUE_MATE - MAX_PLY) {
 +              score->set_score_type(HashProbeScore::SCORE_CP);
 +              score->set_score_cp(value * 100 / PawnValueEg);
 +      } else {
 +              score->set_score_type(HashProbeScore::SCORE_MATE);
 +              score->set_score_mate((value > 0 ? VALUE_MATE - value + 1 : -VALUE_MATE - value) / 2);
 +      }
 +}
 +
 +HashProbeThread::HashProbeThread(const std::string &server_address) {
 +      builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
 +      builder.RegisterService(&service);
 +      server = std::move(builder.BuildAndStart());
 +      std::cout << "Server listening on " << server_address << std::endl;
 +      std::thread([this]{ server->Wait(); }).detach();
 +}
 +
 +void HashProbeThread::Shutdown() {
 +      server->Shutdown();
 +}
 +
  namespace PSQT {
    void init();
  }
diff --combined src/misc.cpp
index 2a8832d573f91a0a6a3721117e3f1e7b87d03672,95862ebbd36cd0c329e421b95d4c6a6b91e123b0..6ed0bdd0d9108a5633fde87664559bac1f2c40e3
@@@ -2,7 -2,7 +2,7 @@@
    Stockfish, a UCI chess playing engine derived from Glaurung 2.1
    Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
    Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
-   Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+   Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
  
    Stockfish is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@@ -56,7 -56,7 +56,7 @@@ namespace 
  
  /// Version number. If Version is left empty, then compile date in the format
  /// DD-MM-YY and show in engine_info.
- const string Version = "";
+ const string Version = "11";
  
  /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
  /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
@@@ -140,7 -140,6 +140,7 @@@ const string engine_info(bool to_uci) 
    {
        date >> month >> day >> year;
        ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
 +      ss << "-asn";
    }
  
    ss << (Is64Bit ? " 64" : "")
  }
  
  
+ /// compiler_info() returns a string trying to describe the compiler we use
+ const std::string compiler_info() {
+   #define STRINGIFY2(x) #x
+   #define STRINGIFY(x) STRINGIFY2(x)
+   #define VER_STRING(major, minor, patch) STRINGIFY(major) "." STRINGIFY(minor) "." STRINGIFY(patch)
+ /// Predefined macros hell:
+ ///
+ /// __GNUC__           Compiler is gcc, Clang or Intel on Linux
+ /// __INTEL_COMPILER   Compiler is Intel
+ /// _MSC_VER           Compiler is MSVC or Intel on Windows
+ /// _WIN32             Building on Windows (any)
+ /// _WIN64             Building on Windows 64 bit
+   std::string compiler = "\nCompiled by ";
+   #ifdef __clang__
+      compiler += "clang++ ";
+      compiler += VER_STRING(__clang_major__, __clang_minor__, __clang_patchlevel__);
+   #elif __INTEL_COMPILER
+      compiler += "Intel compiler ";
+      compiler += "(version ";
+      compiler += STRINGIFY(__INTEL_COMPILER) " update " STRINGIFY(__INTEL_COMPILER_UPDATE);
+      compiler += ")";
+   #elif _MSC_VER
+      compiler += "MSVC ";
+      compiler += "(version ";
+      compiler += STRINGIFY(_MSC_FULL_VER) "." STRINGIFY(_MSC_BUILD);
+      compiler += ")";
+   #elif __GNUC__
+      compiler += "g++ (GNUC) ";
+      compiler += VER_STRING(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
+   #else
+      compiler += "Unknown compiler ";
+      compiler += "(unknown version)";
+   #endif
+   #if defined(__APPLE__) 
+      compiler += " on Apple";
+   #elif defined(__CYGWIN__)
+      compiler += " on Cygwin";
+   #elif defined(__MINGW64__)
+      compiler += " on MinGW64";
+   #elif defined(__MINGW32__)
+      compiler += " on MinGW32";
+   #elif defined(__ANDROID__)
+      compiler += " on Android";
+   #elif defined(__linux__)
+      compiler += " on Linux";
+   #elif defined(_WIN64)
+      compiler += " on Microsoft Windows 64-bit";
+   #elif defined(_WIN32)
+      compiler += " on Microsoft Windows 32-bit";
+   #else
+      compiler += " on unknown system";
+   #endif
+   compiler += "\n __VERSION__ macro expands to: ";
+   #ifdef __VERSION__
+      compiler += __VERSION__;
+   #else
+      compiler += "(undefined macro)";
+   #endif
+   compiler += "\n";
+   return compiler;
+ }
  /// Debug functions used mainly to collect run-time statistics
  static std::atomic<int64_t> hits[2], means[2];
  
diff --combined src/position.cpp
index 5faa9546c7f288dcee403879de40c5e185ce036a,53d9b64e9ff0be947b4525433d32f360807d09b5..09a574a5c23d901f4d10f73d9f04463235459422
@@@ -2,7 -2,7 +2,7 @@@
    Stockfish, a UCI chess playing engine derived from Glaurung 2.1
    Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
    Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
-   Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+   Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
  
    Stockfish is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@@ -50,41 -50,6 +50,6 @@@ const string PieceToChar(" PNBRQK  pnbr
  
  constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
                               B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING };
- // min_attacker() is a helper function used by see_ge() to locate the least
- // valuable attacker for the side to move, remove the attacker we just found
- // from the bitboards and scan for new X-ray attacks behind it.
- template<PieceType Pt>
- PieceType min_attacker(const Bitboard* byTypeBB, Square to, Bitboard stmAttackers,
-                        Bitboard& occupied, Bitboard& attackers) {
-   Bitboard b = stmAttackers & byTypeBB[Pt];
-   if (!b)
-       return min_attacker<PieceType(Pt + 1)>(byTypeBB, to, stmAttackers, occupied, attackers);
-   occupied ^= lsb(b); // Remove the attacker from occupied
-   // Add any X-ray attack behind the just removed piece. For instance with
-   // rooks in a8 and a7 attacking a1, after removing a7 we add rook in a8.
-   // Note that new added attackers can be of any color.
-   if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN)
-       attackers |= attacks_bb<BISHOP>(to, occupied) & (byTypeBB[BISHOP] | byTypeBB[QUEEN]);
-   if (Pt == ROOK || Pt == QUEEN)
-       attackers |= attacks_bb<ROOK>(to, occupied) & (byTypeBB[ROOK] | byTypeBB[QUEEN]);
-   // X-ray may add already processed pieces because byTypeBB[] is constant: in
-   // the rook example, now attackers contains _again_ rook in a7, so remove it.
-   attackers &= occupied;
-   return Pt;
- }
- template<>
- PieceType min_attacker<KING>(const Bitboard*, Square, Bitboard, Bitboard&, Bitboard&) {
-   return KING; // No need to update bitboards: it is the last cycle
- }
  } // namespace
  
  
@@@ -318,6 -283,8 +283,6 @@@ Position& Position::set(const string& f
    thisThread = th;
    set_state(st);
  
 -  assert(pos_is_ok());
 -
    return *this;
  }
  
@@@ -850,7 -817,7 +815,7 @@@ void Position::do_move(Move m, StateInf
            st->nonPawnMaterial[us] += PieceValue[MG][promotion];
        }
  
-       // Update pawn hash key and prefetch access to pawnsTable
+       // Update pawn hash key
        st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
  
        // Reset rule 50 draw counter
@@@ -977,7 -944,7 +942,7 @@@ void Position::do_castling(Color us, Sq
  }
  
  
- /// Position::do(undo)_null_move() is used to do(undo) a "null move": It flips
+ /// Position::do(undo)_null_move() is used to do(undo) a "null move": it flips
  /// the side to move without executing any move on the board.
  
  void Position::do_null_move(StateInfo& newSt) {
@@@ -1050,77 -1017,96 +1015,96 @@@ bool Position::see_ge(Move m, Value thr
    if (type_of(m) != NORMAL)
        return VALUE_ZERO >= threshold;
  
-   Bitboard stmAttackers;
    Square from = from_sq(m), to = to_sq(m);
-   PieceType nextVictim = type_of(piece_on(from));
-   Color us = color_of(piece_on(from));
-   Color stm = ~us; // First consider opponent's move
-   Value balance;   // Values of the pieces taken by us minus opponent's ones
-   // The opponent may be able to recapture so this is the best result
-   // we can hope for.
-   balance = PieceValue[MG][piece_on(to)] - threshold;
  
-   if (balance < VALUE_ZERO)
+   int swap = PieceValue[MG][piece_on(to)] - threshold;
+   if (swap < 0)
        return false;
  
-   // Now assume the worst possible result: that the opponent can
-   // capture our piece for free.
-   balance -= PieceValue[MG][nextVictim];
-   // If it is enough (like in PxQ) then return immediately. Note that
-   // in case nextVictim == KING we always return here, this is ok
-   // if the given move is legal.
-   if (balance >= VALUE_ZERO)
+   swap = PieceValue[MG][piece_on(from)] - swap;
+   if (swap <= 0)
        return true;
  
-   // Find all attackers to the destination square, with the moving piece
-   // removed, but possibly an X-ray attacker added behind it.
    Bitboard occupied = pieces() ^ from ^ to;
-   Bitboard attackers = attackers_to(to, occupied) & occupied;
+   Color stm = color_of(piece_on(from));
+   Bitboard attackers = attackers_to(to, occupied);
+   Bitboard stmAttackers, bb;
+   int res = 1;
  
    while (true)
    {
-       stmAttackers = attackers & pieces(stm);
+       stm = ~stm;
+       attackers &= occupied;
+       // If stm has no more attackers then give up: stm loses
+       if (!(stmAttackers = attackers & pieces(stm)))
+           break;
  
        // Don't allow pinned pieces to attack (except the king) as long as
-       // any pinners are on their original square.
+       // there are pinners on their original square.
        if (st->pinners[~stm] & occupied)
            stmAttackers &= ~st->blockersForKing[stm];
  
-       // If stm has no more attackers then give up: stm loses
        if (!stmAttackers)
            break;
  
+       res ^= 1;
        // Locate and remove the next least valuable attacker, and add to
-       // the bitboard 'attackers' the possibly X-ray attackers behind it.
-       nextVictim = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
+       // the bitboard 'attackers' any X-ray attackers behind it.
+       if ((bb = stmAttackers & pieces(PAWN)))
+       {
+           if ((swap = PawnValueMg - swap) < res)
+               break;
  
-       stm = ~stm; // Switch side to move
+           occupied ^= lsb(bb);
+           attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
+       }
  
-       // Negamax the balance with alpha = balance, beta = balance+1 and
-       // add nextVictim's value.
-       //
-       //      (balance, balance+1) -> (-balance-1, -balance)
-       //
-       assert(balance < VALUE_ZERO);
+       else if ((bb = stmAttackers & pieces(KNIGHT)))
+       {
+           if ((swap = KnightValueMg - swap) < res)
+               break;
  
-       balance = -balance - 1 - PieceValue[MG][nextVictim];
+           occupied ^= lsb(bb);
+       }
  
-       // If balance is still non-negative after giving away nextVictim then we
-       // win. The only thing to be careful about it is that we should revert
-       // stm if we captured with the king when the opponent still has attackers.
-       if (balance >= VALUE_ZERO)
+       else if ((bb = stmAttackers & pieces(BISHOP)))
        {
-           if (nextVictim == KING && (attackers & pieces(stm)))
-               stm = ~stm;
-           break;
+           if ((swap = BishopValueMg - swap) < res)
+               break;
+           occupied ^= lsb(bb);
+           attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
+       }
+       else if ((bb = stmAttackers & pieces(ROOK)))
+       {
+           if ((swap = RookValueMg - swap) < res)
+               break;
+           occupied ^= lsb(bb);
+           attackers |= attacks_bb<ROOK>(to, occupied) & pieces(ROOK, QUEEN);
+       }
+       else if ((bb = stmAttackers & pieces(QUEEN)))
+       {
+           if ((swap = QueenValueMg - swap) < res)
+               break;
+           occupied ^= lsb(bb);
+           attackers |=  (attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN))
+                       | (attacks_bb<ROOK  >(to, occupied) & pieces(ROOK  , QUEEN));
        }
-       assert(nextVictim != KING);
+       else // KING
+            // If we "capture" with the king but opponent still has attackers,
+            // reverse the result.
+           return (attackers & ~pieces(stm)) ? res ^ 1 : res;
    }
-   return us != stm; // We break the above loop when stm loses
- }
  
+   return bool(res);
+ }
  
  /// Position::is_draw() tests whether the position is drawn by 50-move rule
  /// or by repetition. It does not detect stalemates.
diff --combined src/syzygy/tbprobe.cpp
index 84346c278194501550c820412e3650317d2a35fe,721a0ef5ba62fc950af0877fc961f0f24a229bf1..b9a3359511ae53ecd1d988331d6ac7f317f3c879
@@@ -1,7 -1,7 +1,7 @@@
  /*
    Stockfish, a UCI chess playing engine derived from Glaurung 2.1
    Copyright (c) 2013 Ronald de Man
-   Copyright (C) 2016-2019 Marco Costalba, Lucas Braesch
+   Copyright (C) 2016-2020 Marco Costalba, Lucas Braesch
  
    Stockfish is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@@ -76,7 -76,7 +76,7 @@@ int MapB1H1H7[SQUARE_NB]
  int MapA1D1D4[SQUARE_NB];
  int MapKK[10][SQUARE_NB]; // [MapA1D1D4][SQUARE_NB]
  
 -int Binomial[6][SQUARE_NB];    // [k][n] k elements from a set of n elements
 +int Binomial[7][SQUARE_NB];    // [k][n] k elements from a set of n elements
  int LeadPawnIdx[6][SQUARE_NB]; // [leadPawnsCnt][SQUARE_NB]
  int LeadPawnsSize[6][4];       // [leadPawnsCnt][FILE_A..FILE_D]
  
@@@ -683,7 -683,7 +683,7 @@@ Ret do_probe_table(const Position& pos
      bool blackStronger = (pos.material_key() != entry->key);
  
      int flipColor   = (symmetricBlackToMove || blackStronger) * 8;
-     int flipSquares = (symmetricBlackToMove || blackStronger) * 070;
+     int flipSquares = (symmetricBlackToMove || blackStronger) * 56;
      int stm         = (symmetricBlackToMove || blackStronger) ^ pos.side_to_move();
  
      // For pawns, TB files store 4 separate tables according if leading pawn is on
  
      // Then we reorder the pieces to have the same sequence as the one stored
      // in pieces[i]: the sequence that ensures the best compression.
-     for (int i = leadPawnsCnt; i < size; ++i)
-         for (int j = i; j < size; ++j)
+     for (int i = leadPawnsCnt; i < size - 1; ++i)
+         for (int j = i + 1; j < size; ++j)
              if (d->pieces[i] == pieces[j])
              {
                  std::swap(pieces[i], pieces[j]);
      // piece is below RANK_5.
      if (rank_of(squares[0]) > RANK_4)
          for (int i = 0; i < size; ++i)
-             squares[i] ^= 070; // Vertical flip: SQ_A8 -> SQ_A1
+             squares[i] ^= SQ_A8; // Vertical flip: SQ_A8 -> SQ_A1
  
      // Look for the first piece of the leading group not on the A1-D4 diagonal
      // and ensure it is mapped below the diagonal.
@@@ -1311,7 -1311,7 +1311,7 @@@ void Tablebases::init(const std::string
      Binomial[0][0] = 1;
  
      for (int n = 1; n < 64; n++) // Squares
 -        for (int k = 0; k < 6 && k <= n; ++k) // Pieces
 +        for (int k = 0; k < 7 && k <= n; ++k) // Pieces
              Binomial[k][n] =  (k > 0 ? Binomial[k - 1][n - 1] : 0)
                              + (k < n ? Binomial[k    ][n - 1] : 0);
  
diff --combined src/ucioption.cpp
index ea370106fef273723e5f931c1ab1bf6cbeb37b0e,26fcf30227a0cf209bffe006230a49ea336ce569..78e3d46547c55435222e9f6fad6828fb0ed9a6f7
@@@ -2,7 -2,7 +2,7 @@@
    Stockfish, a UCI chess playing engine derived from Glaurung 2.1
    Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
    Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
-   Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+   Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
  
    Stockfish is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
  #include "thread.h"
  #include "tt.h"
  #include "uci.h"
 +#include "hashprobe.h"
  #include "syzygy/tbprobe.h"
  
  using std::string;
  
  UCI::OptionsMap Options; // Global object
 +std::unique_ptr<HashProbeThread> hash_probe_thread;
  
  namespace UCI {
  
@@@ -44,13 -42,7 +44,13 @@@ void on_hash_size(const Option& o) { TT
  void on_logger(const Option& o) { start_logger(o); }
  void on_threads(const Option& o) { Threads.set(o); }
  void on_tb_path(const Option& o) { Tablebases::init(o); }
 -
 +void on_rpc_server_address(const Option& o) {
 +      if (hash_probe_thread) {
 +              hash_probe_thread->Shutdown();
 +      }
 +      std::string addr = o;
 +      hash_probe_thread.reset(new HashProbeThread(addr));
 +}
  
  /// Our case insensitive less() function as required by UCI protocol
  bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
@@@ -88,7 -80,6 +88,7 @@@ void init(OptionsMap& o) 
    o["SyzygyProbeDepth"]      << Option(1, 1, 100);
    o["Syzygy50MoveRule"]      << Option(true);
    o["SyzygyProbeLimit"]      << Option(7, 0, 7);
 +  o["RPCServerAddress"]      << Option("<empty>", on_rpc_server_address);
  }