]> git.sesse.net Git - stockfish/commitdiff
Merge remote-tracking branch 'upstream/master'
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Fri, 12 Apr 2019 20:33:50 +0000 (22:33 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Fri, 12 Apr 2019 20:33:50 +0000 (22:33 +0200)
1  2 
src/main.cpp
src/position.cpp
src/syzygy/tbprobe.cpp
src/ucioption.cpp

diff --combined src/main.cpp
index 97ac7539a1ab80e7022dfb4525c1d132a2bb8b85,fc78b38c1cdedb630cfbae50353194b51822db80..b9d2dfb4ec5f0a2862a9d599185a454d32848bec
    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 "uci.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();
  }
@@@ -236,7 -42,6 +236,6 @@@ int main(int argc, char* argv[]) 
    Position::init();
    Bitbases::init();
    Search::init();
-   Pawns::init();
    Threads.set(Options["Threads"]);
    Search::clear(); // After threads are up
  
diff --combined src/position.cpp
index d3afddeb4fb1df982f15604d0f5c70eafa905e23,ada03fbcf4b1076b38d5aca3583d5bf5b60be2f7..ed5cc43f557a940c949d379be16e1e659012f86f
@@@ -18,7 -18,6 +18,6 @@@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
  
- #include <algorithm>
  #include <cassert>
  #include <cstddef> // For offsetof()
  #include <cstring> // For std::memset, std::memcmp
@@@ -183,7 -182,7 +182,7 @@@ void Position::init() 
                    {
                        std::swap(cuckoo[i], key);
                        std::swap(cuckooMove[i], move);
-                       if (move == 0)   // Arrived at empty slot ?
+                       if (move == MOVE_NONE) // Arrived at empty slot?
                            break;
                        i = (i == H1(key)) ? H2(key) : H1(key); // Push victim to alternative slot
                    }
@@@ -318,6 -317,8 +317,6 @@@ Position& Position::set(const string& f
    thisThread = th;
    set_state(st);
  
 -  assert(pos_is_ok());
 -
    return *this;
  }
  
@@@ -339,13 -340,8 +338,8 @@@ void Position::set_castling_right(Colo
    Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1);
    Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
  
-   for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); ++s)
-       if (s != kfrom && s != rfrom)
-           castlingPath[cr] |= s;
-   for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); ++s)
-       if (s != kfrom && s != rfrom)
-           castlingPath[cr] |= s;
+   castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
+                    & ~(square_bb(kfrom) | rfrom);
  }
  
  
@@@ -463,16 -459,16 +457,16 @@@ const string Position::fen() const 
    ss << (sideToMove == WHITE ? " w " : " b ");
  
    if (can_castle(WHITE_OO))
-       ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE |  KING_SIDE))) : 'K');
+       ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OO ))) : 'K');
  
    if (can_castle(WHITE_OOO))
-       ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | QUEEN_SIDE))) : 'Q');
+       ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OOO))) : 'Q');
  
    if (can_castle(BLACK_OO))
-       ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK |  KING_SIDE))) : 'k');
+       ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OO ))) : 'k');
  
    if (can_castle(BLACK_OOO))
-       ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | QUEEN_SIDE))) : 'q');
+       ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OOO))) : 'q');
  
    if (!can_castle(ANY_CASTLING))
        ss << '-';
@@@ -496,14 -492,15 +490,15 @@@ Bitboard Position::slider_blockers(Bitb
    Bitboard blockers = 0;
    pinners = 0;
  
-   // Snipers are sliders that attack 's' when a piece is removed
+   // Snipers are sliders that attack 's' when a piece and other snipers are removed
    Bitboard snipers = (  (PseudoAttacks[  ROOK][s] & pieces(QUEEN, ROOK))
                        | (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
+   Bitboard occupancy = pieces() & ~snipers;
  
    while (snipers)
    {
      Square sniperSq = pop_lsb(&snipers);
-     Bitboard b = between_bb(s, sniperSq) & pieces();
+     Bitboard b = between_bb(s, sniperSq) & occupancy;
  
      if (b && !more_than_one(b))
      {
@@@ -538,6 -535,7 +533,7 @@@ bool Position::legal(Move m) const 
  
    Color us = sideToMove;
    Square from = from_sq(m);
+   Square to = to_sq(m);
  
    assert(color_of(moved_piece(m)) == us);
    assert(piece_on(square<KING>(us)) == make_piece(us, KING));
    if (type_of(m) == ENPASSANT)
    {
        Square ksq = square<KING>(us);
-       Square to = to_sq(m);
        Square capsq = to - pawn_push(us);
        Bitboard occupied = (pieces() ^ from ^ capsq) | to;
  
              && !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP));
    }
  
-   // If the moving piece is a king, check whether the destination
-   // square is attacked by the opponent. Castling moves are checked
-   // for legality during move generation.
+   // Castling moves generation does not check if the castling path is clear of
+   // enemy attacks, it is delayed at a later time: now!
+   if (type_of(m) == CASTLING)
+   {
+       // After castling, the rook and king final positions are the same in
+       // Chess960 as they would be in standard chess.
+       to = relative_square(us, to > from ? SQ_G1 : SQ_C1);
+       Direction step = to > from ? WEST : EAST;
+       for (Square s = to; s != from; s += step)
+           if (attackers_to(s) & pieces(~us))
+               return false;
+       // In case of Chess960, verify that when moving the castling rook we do
+       // not discover some hidden checker.
+       // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
+       return   !chess960
+             || !(attacks_bb<ROOK>(to, pieces() ^ to_sq(m)) & pieces(~us, ROOK, QUEEN));
+   }
+   // If the moving piece is a king, check whether the destination square is
+   // attacked by the opponent.
    if (type_of(piece_on(from)) == KING)
-       return type_of(m) == CASTLING || !(attackers_to(to_sq(m)) & pieces(~us));
+       return !(attackers_to(to) & pieces(~us));
  
    // A non-king move is legal if and only if it is not pinned or it
    // is moving along the ray towards or away from the king.
    return   !(blockers_for_king(us) & from)
-         ||  aligned(from, to_sq(m), square<KING>(us));
+         ||  aligned(from, to, square<KING>(us));
  }
  
  
@@@ -607,7 -623,7 +621,7 @@@ bool Position::pseudo_legal(const Move 
    {
        // We have already handled promotion moves, so destination
        // cannot be on the 8th/1st rank.
-       if (rank_of(to) == relative_rank(us, RANK_8))
+       if ((Rank8BB | Rank1BB) & to)
            return false;
  
        if (   !(attacks_from<PAWN>(from, us) & pieces(~us) & to) // Not a capture
@@@ -1055,8 -1071,8 +1069,8 @@@ bool Position::see_ge(Move m, Value thr
        stmAttackers = attackers & pieces(stm);
  
        // Don't allow pinned pieces to attack (except the king) as long as
-       // all pinners are on their original square.
-       if (!(st->pinners[~stm] & ~occupied))
+       // any pinners are on their original square.
+       if (st->pinners[~stm] & occupied)
            stmAttackers &= ~st->blockersForKing[stm];
  
        // If stm has no more attackers then give up: stm loses
diff --combined src/syzygy/tbprobe.cpp
index 94d7371499cb8e3d96621e8275592c241e3cd046,7864486cb5fbab69a61eb1aec1cd12734adc12c5..3dbe18fb14ef4832b31d880ba61c382c996f04e3
@@@ -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-2018 Marco Costalba, Lucas Braesch
+   Copyright (C) 2016-2019 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
@@@ -32,7 -32,7 +32,7 @@@
  #include "../movegen.h"
  #include "../position.h"
  #include "../search.h"
- #include "../thread_win32.h"
+ #include "../thread_win32_osx.h"
  #include "../types.h"
  #include "../uci.h"
  
@@@ -74,7 -74,7 +74,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]
  
@@@ -214,14 -214,22 +214,22 @@@ public
              return *baseAddress = nullptr, nullptr;
  
          fstat(fd, &statbuf);
+         if (statbuf.st_size % 64 != 16)
+         {
+             std::cerr << "Corrupt tablebase file " << fname << std::endl;
+             exit(EXIT_FAILURE);
+         }
          *mapping = statbuf.st_size;
          *baseAddress = mmap(nullptr, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
          madvise(*baseAddress, statbuf.st_size, MADV_RANDOM);
          ::close(fd);
  
-         if (*baseAddress == MAP_FAILED) {
+         if (*baseAddress == MAP_FAILED)
+         {
              std::cerr << "Could not mmap() " << fname << std::endl;
-             exit(1);
+             exit(EXIT_FAILURE);
          }
  #else
          // Note FILE_FLAG_RANDOM_ACCESS is only a hint to Windows and as such may get ignored.
  
          DWORD size_high;
          DWORD size_low = GetFileSize(fd, &size_high);
+         if (size_low % 64 != 16)
+         {
+             std::cerr << "Corrupt tablebase file " << fname << std::endl;
+             exit(EXIT_FAILURE);
+         }
          HANDLE mmap = CreateFileMapping(fd, nullptr, PAGE_READONLY, size_high, size_low, nullptr);
          CloseHandle(fd);
  
-         if (!mmap) {
+         if (!mmap)
+         {
              std::cerr << "CreateFileMapping() failed" << std::endl;
-             exit(1);
+             exit(EXIT_FAILURE);
          }
  
          *mapping = (uint64_t)mmap;
          *baseAddress = MapViewOfFile(mmap, FILE_MAP_READ, 0, 0, 0);
  
-         if (!*baseAddress) {
+         if (!*baseAddress)
+         {
              std::cerr << "MapViewOfFile() failed, name = " << fname
                        << ", error = " << GetLastError() << std::endl;
-             exit(1);
+             exit(EXIT_FAILURE);
          }
  #endif
          uint8_t* data = (uint8_t*)*baseAddress;
          constexpr uint8_t Magics[][4] = { { 0xD7, 0x66, 0x0C, 0xA5 },
                                            { 0x71, 0xE8, 0x23, 0x5D } };
  
-         if (memcmp(data, Magics[type == WDL], 4)) {
+         if (memcmp(data, Magics[type == WDL], 4))
+         {
              std::cerr << "Corrupted table in file " << fname << std::endl;
              unmap(*baseAddress, *mapping);
              return *baseAddress = nullptr, nullptr;
@@@ -416,7 -434,7 +434,7 @@@ class TBTables 
              }
          }
          std::cerr << "TB hash table size too low!" << std::endl;
-         exit(1);
+         exit(EXIT_FAILURE);
      }
  
  public:
@@@ -1293,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 4aa0136e4e25fc08873446a6eb608b736a2172ee,54f33c9ad9d5656ef7419242937910dc47067dc1..8b75eea996f36717016d1b25f2d52d15e40aef26
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
  
- #include <algorithm>
  #include <cassert>
  #include <ostream>
+ #include <sstream>
  
  #include "misc.h"
  #include "search.h"
  #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 {
  
@@@ -43,13 -41,7 +43,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 {
@@@ -85,7 -77,6 +85,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);
  }
  
  
@@@ -145,8 -136,8 +145,8 @@@ Option::operator std::string() const 
  
  bool Option::operator==(const char* s) const {
    assert(type == "combo");
-   return    !CaseInsensitiveLess()(currentValue, s)
-          && !CaseInsensitiveLess()(s, currentValue);
+   return   !CaseInsensitiveLess()(currentValue, s)
+         && !CaseInsensitiveLess()(s, currentValue);
  }
  
  
@@@ -162,8 -153,8 +162,8 @@@ void Option::operator<<(const Option& o
  
  
  /// operator=() updates currentValue and triggers on_change() action. It's up to
- /// the GUI to check for option's limits, but we could receive the new value from
- /// the user by console window, so let's check the bounds anyway.
+ /// the GUI to check for option's limits, but we could receive the new value
+ /// from the user by console window, so let's check the bounds anyway.
  
  Option& Option::operator=(const string& v) {
  
        || (type == "spin" && (stof(v) < min || stof(v) > max)))
        return *this;
  
+   if (type == "combo")
+   {
+       OptionsMap comboMap; // To have case insensitive compare
+       string token;
+       std::istringstream ss(defaultValue);
+       while (ss >> token)
+           comboMap[token] << Option();
+       if (!comboMap.count(v) || v == "var")
+           return *this;
+   }
    if (type != "button")
        currentValue = v;