var grpc = require('grpc');
-var Chess = require('../www/js/chess.min.js').Chess;
+var Chess = require('../www/js/chess.js').Chess;
var PROTO_PATH = __dirname + '/hashprobe.proto';
var hashprobe_proto = grpc.load(PROTO_PATH).hashprobe;
-// TODO: Make destination configurable.
-var client = new hashprobe_proto.HashProbe('localhost:50051', grpc.credentials.createInsecure());
-
var board = new Chess();
+var clients = [];
+var current_servers = [];
+
+var need_reinit = function(servers) {
+ if (servers.length != current_servers.length) {
+ return true;
+ }
+ for (var i = 0; i < servers.length; ++i) {
+ if (servers[i] != current_servers[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+exports.need_reinit = need_reinit;
+
+var init = function(servers) {
+ clients = [];
+ for (var i = 0; i < servers.length; ++i) {
+ clients.push(new hashprobe_proto.HashProbe(servers[i], grpc.credentials.createInsecure()));
+ }
+ current_servers = servers;
+}
+exports.init = init;
+
var handle_request = function(fen, response) {
- if (!board.validate_fen(fen).valid) {
+ if (fen === undefined || fen === null || fen === '' || !board.validate_fen(fen).valid) {
response.writeHead(400, {});
response.end();
return;
}
- client.probe({fen: fen}, function(err, probe_response) {
- if (err) {
- response.writeHead(500, {});
- response.end();
- } else {
- handle_response(fen, response, probe_response);
- }
- });
+
+ var rpc_status = {
+ failed: false,
+ left: clients.length,
+ responses: [],
+ }
+ for (var i = 0; i < clients.length; ++i) {
+ clients[i].probe({fen: fen}, function(err, probe_response) {
+ if (err) {
+ rpc_status.failed = true;
+ } else {
+ rpc_status.responses.push(probe_response);
+ }
+ if (--rpc_status.left == 0) {
+ // All probes have come back.
+ if (rpc_status.failed) {
+ response.writeHead(500, {});
+ response.end();
+ } else {
+ handle_response(fen, response, rpc_status.responses);
+ }
+ }
+ });
+ }
}
exports.handle_request = handle_request;
-var handle_response = function(fen, response, probe_response) {
+var handle_response = function(fen, response, probe_responses) {
+ var probe_response = reconcile_responses(probe_responses);
var lines = {};
var root = translate_line(board, fen, probe_response['root']);
for (var i = 0; i < probe_response['line'].length; ++i) {
var line = probe_response['line'][i];
- var uci_move = line['move']['from_sq'] + line['move']['to_sq'] + line['move']['promotion'];
- lines[uci_move] = translate_line(board, fen, line);
+ var pretty_move = line['move']['pretty'];
+ lines[pretty_move] = translate_line(board, fen, line);
}
var text = JSON.stringify({
response.end();
}
+var reconcile_responses = function(probe_responses) {
+ var probe_response = {};
+
+ // Select the root that has searched the deepest, plain and simple.
+ probe_response['root'] = probe_responses[0]['root'];
+ for (var i = 1; i < probe_responses.length; ++i) {
+ var root = probe_responses[i]['root'];
+ if (root['depth'] > probe_response['root']['depth']) {
+ probe_response['root'] = root;
+ }
+ }
+
+ // Do the same ting for each move, combining on move.
+ var moves = {};
+ for (var i = 0; i < probe_responses.length; ++i) {
+ for (var j = 0; j < probe_responses[i]['line'].length; ++j) {
+ var line = probe_responses[i]['line'][j];
+ var pretty_move = line['move']['pretty'];
+
+ if (!moves[pretty_move]) {
+ moves[pretty_move] = line;
+ } else {
+ moves[pretty_move] = reconcile_moves(line, moves[pretty_move]);
+ }
+ }
+ }
+ probe_response['line'] = [];
+ for (var move in moves) {
+ probe_response['line'].push(moves[move]);
+ }
+ return probe_response;
+}
+
+var reconcile_moves = function(a, b) {
+ // Prefer exact bounds, unless the depth is just so much higher.
+ if (a['bound'] === 'BOUND_EXACT' &&
+ b['bound'] !== 'BOUND_EXACT' &&
+ a['depth'] + 10 >= b['depth']) {
+ return a;
+ }
+ if (b['bound'] === 'BOUND_EXACT' &&
+ a['bound'] !== 'BOUND_EXACT' &&
+ b['depth'] + 10 >= a['depth']) {
+ return b;
+ }
+
+ if (a['depth'] > b['depth']) {
+ return a;
+ } else {
+ return b;
+ }
+}
+
var translate_line = function(board, fen, line) {
var r = {};
- board.load(fen);
- var toplay = board.turn();
-
- if (line['move'] && line['move']['from_sq']) {
- var promo = line['move']['promotion'];
- if (promo) {
- r['pretty_move'] = board.move({ from: line['move']['from_sq'], to: line['move']['to_sq'], promotion: promo.toLowerCase() }).san;
- } else {
- r['pretty_move'] = board.move({ from: line['move']['from_sq'], to: line['move']['to_sq'] }).san;
- }
+
+ if (line['move'] && line['move']['pretty']) {
+ r['move'] = line['move']['pretty']
} else {
- r['pretty_move'] = '';
+ r['move'] = '';
}
- r['sort_key'] = r['pretty_move'];
if (!line['found']) {
- r['pv_pretty'] = [];
+ r['pv'] = [];
return r;
}
r['depth'] = line['depth'];
// Convert the PV.
var pv = [];
- if (r['pretty_move']) {
- pv.push(r['pretty_move']);
+ if (r['move']) {
+ pv.push(r['move']);
}
for (var j = 0; j < line['pv'].length; ++j) {
var move = line['pv'][j];
- var decoded = board.move({ from: move['from_sq'], to: move['to_sq'], promotion: move['promotion'] });
- if (decoded === null) {
- break;
- }
- pv.push(decoded.san);
+ pv.push(move['pretty']);
}
- r['pv_pretty'] = pv;
+ r['pv'] = pv;
// Convert the score. Use the static eval if no search.
var value = line['value'] || line['eval'];