--- /dev/null
+#include <iostream>
+#include <memory>
+#include <string>
+
+#include <grpc++/grpc++.h>
+
+#include "hashprobe.grpc.pb.h"
+#include "types.h"
+#include "uci.h"
+
+using grpc::Channel;
+using grpc::ClientContext;
+using grpc::Status;
+using namespace hashprobe;
+
+std::string FormatMove(const HashProbeMove &move) {
+ if (move.pretty().empty()) return "MOVE_NONE";
+ return move.pretty();
+}
+
+int main(int argc, char** argv) {
+ std::shared_ptr<Channel> channel(grpc::CreateChannel(
+ "localhost:50051", grpc::InsecureChannelCredentials()));
+ std::unique_ptr<HashProbe::Stub> stub(HashProbe::NewStub(channel));
+
+ for ( ;; ) {
+ char buf[256];
+ if (fgets(buf, sizeof(buf), stdin) == nullptr || buf[0] == '\n') {
+ exit(0);
+ }
+
+ char *ptr = strchr(buf, '\n');
+ if (ptr != nullptr) *ptr = 0;
+
+ HashProbeRequest request;
+ request.set_fen(buf);
+
+ HashProbeResponse response;
+ ClientContext context;
+ Status status = stub->Probe(&context, request, &response);
+
+ if (status.ok()) {
+ for (const HashProbeLine &line : response.line()) {
+ std::cout << FormatMove(line.move()) << " ";
+ std::cout << line.found() << " ";
+ for (const HashProbeMove &move : line.pv()) {
+ std::cout << FormatMove(move) << ",";
+ }
+ std::cout << " ";
+ switch (line.bound()) {
+ case HashProbeLine::BOUND_NONE:
+ std::cout << "?";
+ break;
+ case HashProbeLine::BOUND_EXACT:
+ std::cout << "==";
+ break;
+ case HashProbeLine::BOUND_UPPER:
+ std::cout << "<=";
+ break;
+ case HashProbeLine::BOUND_LOWER:
+ std::cout << ">=";
+ break;
+ }
+ switch (line.value().score_type()) {
+ case HashProbeScore::SCORE_CP:
+ std::cout << " cp " << line.value().score_cp() << " ";
+ break;
+ case HashProbeScore::SCORE_MATE:
+ std::cout << " mate " << line.value().score_mate() << " ";
+ break;
+ }
+ std::cout << line.depth() << std::endl;
+ }
+ std::cout << "END" << std::endl;
+ } else {
+ std::cout << "ERROR" << std::endl;
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+#ifndef HASHPROBE_H_INCLUDED
+#define HASHPROBE_H_INCLUDED
+
+#include "position.h"
+#include "types.h"
+
+#include <deque>
+#include <string>
+
+#include <grpc/grpc.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include "hashprobe.grpc.pb.h"
+
+class HashProbeImpl final : public hashprobe::HashProbe::Service {
+public:
+ grpc::Status Probe(grpc::ServerContext* context,
+ const hashprobe::HashProbeRequest* request,
+ hashprobe::HashProbeResponse *response);
+
+private:
+ void FillMove(Stockfish::Position* pos, Stockfish::Move move, hashprobe::HashProbeMove* decoded);
+ void ProbeMove(Stockfish::Position* pos, std::deque<Stockfish::StateInfo>* setup_states, bool invert, hashprobe::HashProbeLine* response);
+ void FillValue(Stockfish::Value value, hashprobe::HashProbeScore* score);
+};
+
+class HashProbeThread {
+public:
+ HashProbeThread(const std::string &server_address);
+ void Shutdown();
+
+private:
+ HashProbeImpl service;
+ grpc::ServerBuilder builder;
+ std::unique_ptr<grpc::Server> server;
+};
+
+#endif
--- /dev/null
+syntax = "proto3";
+package hashprobe;
+
+message HashProbeRequest {
+ string fen = 1;
+}
+message HashProbeResponse {
+ HashProbeLine root = 2;
+ repeated HashProbeLine line = 1;
+}
+message HashProbeLine {
+ HashProbeMove move = 1;
+ bool found = 2;
+
+ repeated HashProbeMove pv = 3;
+ HashProbeScore value = 4; // Dynamic eval (may be inexact, see the "bound" field)
+ HashProbeScore eval = 5; // Static eval
+ int32 depth = 6;
+
+ enum ValueBound {
+ BOUND_NONE = 0;
+ BOUND_UPPER = 1;
+ BOUND_LOWER = 2;
+ BOUND_EXACT = 3;
+ };
+ ValueBound bound = 7;
+}
+
+message HashProbeMove {
+ string from_sq = 1; // a1, a2, etc.
+ string to_sq = 2;
+ string promotion = 3; // Q, R, etc.
+
+ string pretty = 4; // e.g. Rxf6+
+}
+message HashProbeScore {
+ enum ScoreType {
+ SCORE_NONE = 0;
+ SCORE_CP = 1;
+ SCORE_MATE = 2;
+ }
+ ScoreType score_type = 1;
+ int32 score_cp = 2;
+ int32 score_mate = 3;
+}
+
+service HashProbe {
+ rpc Probe(HashProbeRequest) returns (HashProbeResponse) {}
+}