]> git.sesse.net Git - remoteglot/blob - server/hash-lookup.js
Convert from grpc to grpc-js, since the former is deprecated and no longer compiles...
[remoteglot] / server / hash-lookup.js
1 var grpc = require('@grpc/grpc-js');
2 var Chess = require('../www/js/chess.js').Chess;
3
4 var PROTO_PATH = __dirname + '/hashprobe.proto';
5 var protoLoader = require('@grpc/proto-loader');
6 var packageDefinition = protoLoader.loadSync(
7     PROTO_PATH,
8     {keepCase: true,
9      longs: String,
10      enums: String,
11      defaults: true,
12      oneofs: true
13     });
14 var hashprobe_proto = grpc.loadPackageDefinition(packageDefinition).hashprobe;
15
16 var board = new Chess();
17
18 var clients = [];
19 var current_servers = [];
20
21 var need_reinit = function(servers) {
22         if (servers.length != current_servers.length) {
23                 return true;
24         }
25         for (var i = 0; i < servers.length; ++i) {
26                 if (servers[i] != current_servers[i]) {
27                         return true;
28                 }
29         }
30         return false;
31 }
32 exports.need_reinit = need_reinit;
33
34 var init = function(servers) {
35         clients = [];
36         for (var i = 0; i < servers.length; ++i) {
37                 clients.push(new hashprobe_proto.HashProbe(servers[i], grpc.credentials.createInsecure()));
38         }
39         current_servers = servers;
40 }
41 exports.init = init;
42
43 var handle_request = function(fen, response) {
44         if (fen === undefined || fen === null || fen === '' || !board.validate_fen(fen).valid) {
45                 response.writeHead(400, {});
46                 response.end();
47                 return;
48         }
49
50         var rpc_status = {
51                 failed: false,
52                 left: clients.length,
53                 responses: [],
54         }
55         for (var i = 0; i < clients.length; ++i) {
56                 clients[i].probe({fen: fen}, function(err, probe_response) {
57                         if (err) {
58                                 rpc_status.failed = true;
59                         } else {
60                                 rpc_status.responses.push(probe_response);
61                         }
62                         if (--rpc_status.left == 0) {
63                                 // All probes have come back.
64                                 if (rpc_status.failed) {
65                                         response.writeHead(500, {});
66                                         response.end();
67                                 } else {
68                                         handle_response(fen, response, rpc_status.responses);
69                                 }
70                         }
71                 });
72         }
73 }
74 exports.handle_request = handle_request;
75
76 var handle_response = function(fen, response, probe_responses) {
77         var probe_response = reconcile_responses(probe_responses);
78         var lines = {};
79
80         var root = translate_line(board, fen, probe_response['root']);
81         for (var i = 0; i < probe_response['line'].length; ++i) {
82                 var line = probe_response['line'][i];
83                 var pretty_move = line['move']['pretty'];
84                 lines[pretty_move] = translate_line(board, fen, line);
85         }
86
87         var text = JSON.stringify({
88                 root: root,
89                 lines: lines
90         });
91         var headers = {
92                 'Content-Type': 'text/json; charset=utf-8'
93                 //'Content-Length': text.length
94         };
95         response.writeHead(200, headers);
96         response.write(text);
97         response.end();
98 }
99
100 var reconcile_responses = function(probe_responses) {
101         var probe_response = {};
102
103         // Select the root that has searched the deepest, plain and simple.
104         probe_response['root'] = probe_responses[0]['root'];
105         for (var i = 1; i < probe_responses.length; ++i) {
106                 var root = probe_responses[i]['root'];
107                 if (root['depth'] > probe_response['root']['depth']) {
108                         probe_response['root'] = root;
109                 }
110         }
111
112         // Do the same ting for each move, combining on move.
113         var moves = {};
114         for (var i = 0; i < probe_responses.length; ++i) {
115                 for (var j = 0; j < probe_responses[i]['line'].length; ++j) {
116                         var line = probe_responses[i]['line'][j];
117                         var pretty_move = line['move']['pretty'];
118
119                         if (!moves[pretty_move]) {
120                                 moves[pretty_move] = line;
121                         } else {
122                                 moves[pretty_move] = reconcile_moves(line, moves[pretty_move]);
123                         }
124                 }
125         }
126         probe_response['line'] = [];
127         for (var move in moves) {
128                 probe_response['line'].push(moves[move]);
129         }
130         return probe_response;
131 }
132
133 var reconcile_moves = function(a, b) {
134         // Prefer exact bounds, unless the depth is just so much higher.
135         if (a['bound'] === 'BOUND_EXACT' &&
136             b['bound'] !== 'BOUND_EXACT' &&
137             a['depth'] + 10 >= b['depth']) {
138                 return a;
139         }
140         if (b['bound'] === 'BOUND_EXACT' &&
141             a['bound'] !== 'BOUND_EXACT' &&
142             b['depth'] + 10 >= a['depth']) {
143                 return b;
144         }
145
146         if (a['depth'] > b['depth']) {
147                 return a;
148         } else {
149                 return b;
150         }
151 }       
152
153 var translate_line = function(board, fen, line) {
154         var r = {};
155
156         if (line['move'] && line['move']['pretty']) {
157                 r['move'] = line['move']['pretty']
158         } else {
159                 r['move'] = '';
160         }
161         if (!line['found']) {
162                 r['pv'] = [];
163                 return r;
164         }
165         r['depth'] = line['depth'];
166
167         // Convert the PV.
168         var pv = [];
169         if (r['move']) {
170                 pv.push(r['move']);
171         }
172         for (var j = 0; j < line['pv'].length; ++j) {
173                 var move = line['pv'][j];
174                 pv.push(move['pretty']);
175         }
176         r['pv'] = pv;
177
178         // Convert the score. Use the static eval if no search.
179         var value = line['value'] || line['eval'];
180         var score = null;
181         if (value['score_type'] === 'SCORE_CP') {
182                 score = ['cp', value['score_cp']];
183         } else if (value['score_type'] === 'SCORE_MATE') {
184                 score = ['m', value['score_mate']];
185         }
186         if (score) {
187                 if (line['bound'] === 'BOUND_UPPER') {
188                         score.push('≤');
189                 } else if (line['bound'] === 'BOUND_LOWER') {
190                         score.push('≥');
191                 }
192         }
193
194         r['score'] = score;
195
196         return r;
197 }