### Object files
OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \
material.o misc.o movegen.o movepick.o pawns.o position.o psqt.o \
- search.o thread.o timeman.o tt.o uci.o ucioption.o syzygy/tbprobe.o
+ search.o thread.o timeman.o tt.o uci.o ucioption.o syzygy/tbprobe.o \
+ hashprobe.grpc.pb.o hashprobe.pb.o
+CLIOBJS = client.o hashprobe.grpc.pb.o hashprobe.pb.o uci.o
### Establish the operating system name
KERNEL = $(shell uname -s)
ifeq ($(COMP),gcc)
comp=gcc
CXX=g++
- CXXFLAGS += -pedantic -Wextra -Wshadow
+ CXXFLAGS += -pedantic -Wextra
ifeq ($(ARCH),armv7)
ifeq ($(OS),Android)
### 3.3 Optimization
ifeq ($(optimize),yes)
- CXXFLAGS += -O3
+ CXXFLAGS += -O3 -g
ifeq ($(comp),gcc)
ifeq ($(OS), Android)
CXXFLAGS += -fno-gcse -mthumb -march=armv7-a -mfloat-abi=softfp
endif
endif
-
+
ifeq ($(comp),$(filter $(comp),gcc clang icc))
ifeq ($(KERNEL),Darwin)
CXXFLAGS += -mdynamic-no-pic
ifeq ($(pext),yes)
CXXFLAGS += -DUSE_PEXT
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
- CXXFLAGS += -mbmi2
+ CXXFLAGS += -msse4 -mbmi2
endif
endif
@echo ""
@echo "Supported archs:"
@echo ""
- @echo "x86-64 > x86 64-bit"
- @echo "x86-64-modern > x86 64-bit with popcnt support"
- @echo "x86-64-bmi2 > x86 64-bit with pext support"
- @echo "x86-32 > x86 32-bit with SSE support"
+ @echo "x86-64-bmi2 > x86 64-bit with pext support (also enables SSE4)"
+ @echo "x86-64-modern > x86 64-bit with popcnt support (also enables SSE3)"
+ @echo "x86-64 > x86 64-bit generic"
+ @echo "x86-32 > x86 32-bit (also enables SSE)"
@echo "x86-32-old > x86 32-bit fall back for old hardware"
@echo "ppc-64 > PPC 64-bit"
@echo "ppc-32 > PPC 32-bit"
@echo "Advanced examples, for experienced users: "
@echo ""
@echo "make build ARCH=x86-64 COMP=clang"
- @echo "make profile-build ARCH=x86-64-modern COMP=gcc COMPCXX=g++-4.8"
+ @echo "make profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-4.8"
@echo ""
### Section 5. Private targets
### ==========================================================================
-all: $(EXE) .depend
+all: $(EXE) client .depend
config-sanity:
@echo ""
EXTRACXXFLAGS='-prof_use -prof_dir ./profdir' \
all
+### GRPC
+
+PROTOS_PATH = .
+PROTOC = protoc
+GRPC_CPP_PLUGIN = grpc_cpp_plugin
+GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)`
+
+%.grpc.pb.h %.grpc.pb.cc: %.proto
+ $(PROTOC) -I $(PROTOS_PATH) --grpc_out=. --plugin=protoc-gen-grpc=$(GRPC_CPP_PLUGIN_PATH) $<
+
+# oh my
+%.cpp: %.cc
+ cp $< $@
+
+%.pb.h %.pb.cc: %.proto
+ $(PROTOC) -I $(PROTOS_PATH) --cpp_out=. $<
+
+#LDFLAGS += -Wl,-Bstatic -Wl,-\( -lprotobuf -lgrpc++_unsecure -lgrpc_unsecure -lgrpc -lz -Wl,-\) -Wl,-Bdynamic -ldl
+LDFLAGS += /usr/lib/x86_64-linux-gnu/libprotobuf.a /usr/lib/x86_64-linux-gnu/libgrpc++_unsecure.a /usr/lib/x86_64-linux-gnu/libgrpc_unsecure.a /usr/lib/x86_64-linux-gnu/libgrpc.a /usr/lib/x86_64-linux-gnu/libcares.a -ldl -lz
+#LDFLAGS += /usr/lib/x86_64-linux-gnu/libprotobuf.a /usr/lib/libgrpc++_unsecure.a /usr/lib/libgrpc_unsecure.a /usr/lib/libgrpc.a /usr/lib/x86_64-linux-gnu/libcares.a -ldl -lz
+
+client: $(CLIOBJS)
+ $(CXX) -o $@ $(CLIOBJS) $(LDFLAGS)
+
+# Other stuff
+
.depend:
- -@$(CXX) $(DEPENDFLAGS) -MM $(OBJS:.o=.cpp) > $@ 2> /dev/null
+ -@$(CXX) $(DEPENDFLAGS) -MM $(OBJS:.o=.cpp) $(OBJS:.o=.cc) > $@ 2> /dev/null
-include .depend
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();
}
Position::init();
Bitbases::init();
Endgames::init();
- Search::init();
Threads.set(Options["Threads"]);
Search::clear(); // After threads are up
if (!fname.empty() && !l.file.is_open())
{
l.file.open(fname, ifstream::out);
+
+ if (!l.file.is_open())
+ {
+ cerr << "Unable to open debug log file " << fname << endl;
+ exit(EXIT_FAILURE);
+ }
+
cin.rdbuf(&l.in);
cout.rdbuf(&l.out);
}
{
date >> month >> day >> year;
ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
+ ss << "-asn";
}
ss << (Is64Bit ? " 64" : "")
std::ostream& operator<<(std::ostream& os, SyncCout sc) {
- static Mutex m;
+ static std::mutex m;
if (sc == IO_LOCK)
m.lock();
thisThread = th;
set_state(st);
- assert(pos_is_ok());
-
return *this;
}
void Position::set_castling_right(Color c, Square rfrom) {
Square kfrom = square<KING>(c);
- CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE;
- CastlingRight cr = (c | cs);
+ CastlingRights cr = c & (kfrom < rfrom ? KING_SIDE: QUEEN_SIDE);
st->castlingRights |= cr;
castlingRightsMask[kfrom] |= cr;
castlingRightsMask[rfrom] |= cr;
castlingRookSquare[cr] = rfrom;
- Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1);
- Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
+ Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1);
+ Square rto = relative_square(c, cr & KING_SIDE ? SQ_F1 : SQ_D1);
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
& ~(square_bb(kfrom) | rfrom);
if (end >= 4)
{
StateInfo* stp = st->previous->previous;
- for (int i=4; i <= end; i += 2)
+ for (int i = 4; i <= end; i += 2)
{
stp = stp->previous->previous;
if (stp->key == st->key)
}
for (Color c : { WHITE, BLACK })
- for (CastlingSide s : {KING_SIDE, QUEEN_SIDE})
+ for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE})
{
- if (!can_castle(c | s))
+ if (!can_castle(cr))
continue;
- if ( piece_on(castlingRookSquare[c | s]) != make_piece(c, ROOK)
- || castlingRightsMask[castlingRookSquare[c | s]] != (c | s)
- || (castlingRightsMask[square<KING>(c)] & (c | s)) != (c | s))
+ if ( piece_on(castlingRookSquare[cr]) != make_piece(c, ROOK)
+ || castlingRightsMask[castlingRookSquare[cr]] != cr
+ || (castlingRightsMask[square<KING>(c)] & cr) != cr)
assert(0 && "pos_is_ok: Castling");
}
#include <list>
#include <sstream>
#include <type_traits>
+ #include <mutex>
#include "../bitboard.h"
#include "../movegen.h"
#include "../position.h"
#include "../search.h"
- #include "../thread_win32_osx.h"
#include "../types.h"
#include "../uci.h"
#include <sys/stat.h>
#else
#define WIN32_LEAN_AND_MEAN
- #define NOMINMAX
+ #ifndef NOMINMAX
+ # define NOMINMAX // Disable macros min() and max()
+ #endif
#include <windows.h>
#endif
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]
hasPawns = pos.pieces(PAWN);
hasUniquePieces = false;
- for (Color c : {WHITE, BLACK})
+ for (Color c : { WHITE, BLACK })
for (PieceType pt = PAWN; pt < KING; ++pt)
if (popcount(pos.pieces(c, pt)) == 1)
hasUniquePieces = true;
std::swap(squares[0], *std::max_element(squares, squares + leadPawnsCnt, pawns_comp));
- tbFile = file_of(squares[0]);
- if (tbFile > FILE_D)
- tbFile = file_of(squares[0] ^ 7); // Horizontal flip: SQ_H1 -> SQ_A1
+ tbFile = map_to_queenside(file_of(squares[0]));
}
// DTZ tables are one-sided, i.e. they store positions only for white to
enum { Split = 1, HasPawns = 2 };
- assert(e.hasPawns == !!(*data & HasPawns));
- assert((e.key != e.key2) == !!(*data & Split));
+ assert(e.hasPawns == bool(*data & HasPawns));
+ assert((e.key != e.key2) == bool(*data & Split));
data++; // First byte stores flags
template<TBType Type>
void* mapped(TBTable<Type>& e, const Position& pos) {
- static Mutex mutex;
+ static std::mutex mutex;
// Use 'acquire' to avoid a thread reading 'ready' == true while
// another is still working. (compiler reordering may cause this).
if (e.ready.load(std::memory_order_acquire))
return e.baseAddress; // Could be nullptr if file does not exist
- std::unique_lock<Mutex> lk(mutex);
+ std::unique_lock<std::mutex> lk(mutex);
if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock
return e.baseAddress;
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);