Add support for probing the engine hash (requires gRPC-patched Stockfish).
[remoteglot] / www / js / hash-lookup.js
1 var grpc = require('grpc');
2 var Chess = require(__dirname + '/chess.min.js').Chess;
3
4 var PROTO_PATH = __dirname + '/hashprobe.proto';
5 var hashprobe_proto = grpc.load(PROTO_PATH).hashprobe;
6
7 // TODO: Make destination configurable.
8 var client = new hashprobe_proto.HashProbe('localhost:50051', grpc.credentials.createInsecure());
9
10 var handle_request = function(fen, response) {
11         client.probe({fen: fen}, function(err, probe_response) {
12                 if (err) {
13                         response.writeHead(500, {});
14                         response.end();
15                 } else {
16                         handle_response(fen, response, probe_response);
17                 }
18         });
19 }
20 exports.handle_request = handle_request;
21
22 var handle_response = function(fen, response, probe_response) {
23         var board = new Chess();
24
25         var lines = {};
26
27         var root = translate_line(board, fen, probe_response['root'], true);
28         for (var i = 0; i < probe_response['line'].length; ++i) {
29                 var line = probe_response['line'][i];
30                 var uci_move = line['move']['from_sq'] + line['move']['to_sq'] + line['move']['promotion'];
31                 lines[uci_move] = translate_line(board, fen, line, false);
32         }
33
34         var text = JSON.stringify({
35                 root: root,
36                 lines: lines
37         });
38         var headers = {
39                 'Content-Type': 'text/json; charset=utf-8'
40                 //'Content-Length': text.length
41         };
42         response.writeHead(200, headers);
43         response.write(text);
44         response.end();
45 }
46
47 var translate_line = function(board, fen, line, pretty_score) {
48         var r = {};
49         board.load(fen);
50         var toplay = board.turn();
51
52         if (line['move'] && line['move']['from_sq']) {
53                 r['pretty_move'] = board.move({ from: line['move']['from_sq'], to: line['move']['to_sq'], promotion: line['move']['promotion'] }).san;
54         } else {
55                 r['pretty_move'] = '';
56         }
57         r['sort_key'] = r['pretty_move'];
58         if (!line['found']) {
59                 r['pv_pretty'] = [];
60                 r['score_sort_key'] = -100000000;
61                 return r;
62         }
63         r['depth'] = line['depth'];
64
65         // Convert the PV.
66         var pv = [];
67         if (r['pretty_move']) {
68                 pv.push(r['pretty_move']);
69         }
70         for (var j = 0; j < line['pv'].length; ++j) {
71                 var move = line['pv'][j];
72                 var decoded = board.move({ from: move['from_sq'], to: move['to_sq'], promotion: move['promotion'] });
73                 if (decoded === null) {
74                         break;
75                 }
76                 pv.push(decoded.san);
77         }
78         r['pv_pretty'] = pv;
79
80         // Write out the pretty score.
81         // TODO: mates!
82         var score = pretty_score ? 'Score: ' : '';
83
84         if (line['bound'] === 'BOUND_UPPER') {
85                 score += '≤\u00a0';
86         } else if (line['bound'] === 'BOUND_LOWER') {
87                 score += '≥\u00a0';
88         }
89
90         var value = line['value']['score_cp'];
91         if (value > 0) {
92                 score += '+' + (value / 100.0).toFixed(2);
93         } else if (value < 0) {
94                 score += (value / 100.0).toFixed(2);
95         } else if (value == 0) {
96                 score += '0.00';
97         } else {
98                 score += '';
99         }
100         r['pretty_score'] = score;
101         r['score_sort_key'] = score_sort_key(line['value'], toplay === 'b') * 200 + r['depth'];
102
103         return r;
104 }
105
106 var score_sort_key = function(score, invert) {
107         if (score['score_type'] === 'SCORE_MATE') {
108                 var mate = score['score_mate'];
109                 var score;
110                 if (mate > 0) {
111                         // Side to move mates
112                         score = 99999 - mate;
113                 } else {
114                         // Side to move is getting mated (note the double negative for mate)
115                         score = -99999 - mate;
116                 }
117                 if (invert) {
118                         score = -score;
119                 }
120                 return score;
121         } else if (score['score_type'] === 'SCORE_CP') {
122                 var score = score['score_cp'];
123                 if (invert) {
124                         score = -score;
125                 }
126                 return score;
127         }
128
129         return null;
130 }