playoutp_done: Introduce, use in ELO
[pachi/json.git] / gtp.c
blob5dc5f3833d8fc21be0bc472424b2507276e87466
1 #define DEBUG
2 #include <assert.h>
3 #include <ctype.h>
4 #include <stdarg.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
9 #include "board.h"
10 #include "debug.h"
11 #include "engine.h"
12 #include "gtp.h"
13 #include "mq.h"
14 #include "uct/uct.h"
15 #include "version.h"
17 void
18 gtp_prefix(char prefix, int id)
20 if (id >= 0)
21 printf("%c%d ", prefix, id);
22 else
23 printf("%c ", prefix);
26 void
27 gtp_flush(void)
29 putchar('\n');
30 fflush(stdout);
33 void
34 gtp_output(char prefix, int id, va_list params)
36 gtp_prefix(prefix, id);
37 char *s;
38 while ((s = va_arg(params, char *))) {
39 fputs(s, stdout);
41 putchar('\n');
42 gtp_flush();
45 void
46 gtp_reply(int id, ...)
48 va_list params;
49 va_start(params, id);
50 gtp_output('=', id, params);
51 va_end(params);
54 void
55 gtp_error(int id, ...)
57 va_list params;
58 va_start(params, id);
59 gtp_output('?', id, params);
60 va_end(params);
64 /* XXX: THIS IS TOTALLY INSECURE!!!!
65 * Even basic input checking is missing. */
67 void
68 gtp_parse(struct board *board, struct engine *engine, char *buf)
70 #define next_tok(to_) \
71 to_ = next; \
72 next = next + strcspn(next, " \t\r\n"); \
73 if (*next) { \
74 *next = 0; next++; \
75 next += strspn(next, " \t\r\n"); \
78 if (strchr(buf, '#'))
79 *strchr(buf, '#') = 0;
81 char *cmd, *next = buf;
82 next_tok(cmd);
84 int id = -1;
85 if (isdigit(*cmd)) {
86 id = atoi(cmd);
87 next_tok(cmd);
90 if (!*cmd)
91 return;
93 if (!strcasecmp(cmd, "protocol_version")) {
94 gtp_reply(id, "2", NULL);
96 } else if (!strcasecmp(cmd, "name")) {
97 /* KGS hack */
98 gtp_reply(id, "Pachi ", engine->name, NULL);
100 } else if (!strcasecmp(cmd, "version")) {
101 gtp_reply(id, PACHI_VERSION, ": ", engine->comment, NULL);
103 /* TODO: known_command */
105 } else if (!strcasecmp(cmd, "list_commands")) {
106 gtp_reply(id, "protocol_version\nname\nversion\nlist_commands\nquit\nboardsize\nclear_board\nkomi\nplay\ngenmove\nset_free_handicap\nplace_free_handicap\nfinal_status_list\nkgs-chat", NULL);
108 } else if (!strcasecmp(cmd, "quit")) {
109 gtp_reply(id, NULL);
110 exit(0);
112 } else if (!strcasecmp(cmd, "boardsize")) {
113 char *arg;
114 next_tok(arg);
115 board_resize(board, atoi(arg));
117 gtp_reply(id, NULL);
119 } else if (!strcasecmp(cmd, "clear_board")) {
120 if (board->es) {
121 assert(engine && engine->done_board_state);
122 engine->done_board_state(engine, board);
124 board_clear(board);
125 engine_reset = true;
126 if (DEBUGL(1))
127 board_print(board, stderr);
128 gtp_reply(id, NULL);
130 } else if (!strcasecmp(cmd, "komi")) {
131 char *arg;
132 next_tok(arg);
133 sscanf(arg, "%f", &board->komi);
135 if (DEBUGL(1))
136 board_print(board, stderr);
137 gtp_reply(id, NULL);
139 } else if (!strcasecmp(cmd, "play")) {
140 struct move m;
142 char *arg;
143 next_tok(arg);
144 m.color = str2stone(arg);
145 next_tok(arg);
146 coord_t *c = str2coord(arg, board_size(board));
147 m.coord = *c; coord_done(c);
148 char *reply = NULL;
150 if (DEBUGL(1))
151 fprintf(stderr, "got move %d,%d,%d\n", m.color, coord_x(m.coord, board), coord_y(m.coord, board));
152 if (engine->notify_play)
153 reply = engine->notify_play(engine, board, &m);
154 if (board_play(board, &m) < 0) {
155 if (DEBUGL(0)) {
156 fprintf(stderr, "! ILLEGAL MOVE %d,%d,%d\n", m.color, coord_x(m.coord, board), coord_y(m.coord, board));
157 board_print(board, stderr);
159 gtp_error(id, "illegal move", NULL);
160 } else {
161 if (DEBUGL(1))
162 board_print(board, stderr);
163 gtp_reply(id, reply, NULL);
166 } else if (!strcasecmp(cmd, "genmove")) {
167 char *arg;
168 next_tok(arg);
169 enum stone color = str2stone(arg);
170 coord_t *c = engine->genmove(engine, board, color);
171 struct move m = { *c, color };
172 board_play(board, &m);
173 char *str = coord2str(*c, board);
174 if (DEBUGL(1))
175 fprintf(stderr, "playing move %s\n", str);
176 if (DEBUGL(1)) {
177 board_print_custom(board, stderr, engine->printhook);
179 gtp_reply(id, str, NULL);
180 free(str); coord_done(c);
182 } else if (!strcasecmp(cmd, "set_free_handicap")) {
183 struct move m;
184 m.color = S_BLACK;
186 char *arg;
187 next_tok(arg);
188 do {
189 coord_t *c = str2coord(arg, board_size(board));
190 m.coord = *c; coord_done(c);
191 if (DEBUGL(1))
192 fprintf(stderr, "setting handicap %d,%d\n", coord_x(m.coord, board), coord_y(m.coord, board));
194 if (board_play(board, &m) < 0) {
195 if (DEBUGL(0))
196 fprintf(stderr, "! ILLEGAL MOVE %d,%d,%d\n", m.color, coord_x(m.coord, board), coord_y(m.coord, board));
197 gtp_error(id, "illegal move", NULL);
199 board->handicap++;
200 next_tok(arg);
201 } while (*arg);
202 if (DEBUGL(1))
203 board_print(board, stderr);
204 gtp_reply(id, NULL);
206 /* TODO: Engine should choose free handicap; however, it tends to take
207 * overly long to think it all out, and unless it's clever its
208 * handicap stones won't be of much help. ;-) */
209 } else if (!strcasecmp(cmd, "place_free_handicap")
210 || !strcasecmp(cmd, "fixed_handicap")) {
211 char *arg;
212 next_tok(arg);
213 int stones = atoi(arg);
215 gtp_prefix('=', id);
216 board_handicap(board, stones, stdout);
217 if (DEBUGL(1))
218 board_print(board, stderr);
219 putchar('\n');
220 gtp_flush();
222 } else if (!strcasecmp(cmd, "final_score")) {
223 struct move_queue q = { .moves = 0 };
224 if (engine->dead_group_list)
225 engine->dead_group_list(engine, board, &q);
226 float score = board_official_score(board, &q);
227 char str[64];
228 if (DEBUGL(1))
229 fprintf(stderr, "counted score %.1f\n", score);
230 if (score == 0) {
231 gtp_reply(id, "0", NULL);
232 } else if (score > 0) {
233 snprintf(str, 64, "W+%.1f", score);
234 gtp_reply(id, str, NULL);
235 } else {
236 snprintf(str, 64, "B+%.1f", -score);
237 gtp_reply(id, str, NULL);
240 /* XXX: This is a huge hack. */
241 } else if (!strcasecmp(cmd, "final_status_list")) {
242 char *arg;
243 next_tok(arg);
244 struct move_queue q = { .moves = 0 };
245 if (engine->dead_group_list)
246 engine->dead_group_list(engine, board, &q);
247 /* else we return empty list - i.e. engine not supporting
248 * this assumes all stones alive at the game end. */
249 if (!strcasecmp(arg, "dead")) {
250 gtp_prefix('=', id);
251 for (int i = 0; i < q.moves; i++) {
252 foreach_in_group(board, q.move[i]) {
253 printf("%s ", coord2sstr(c, board));
254 } foreach_in_group_end;
255 putchar('\n');
257 if (!q.moves)
258 putchar('\n');
259 gtp_flush();
260 } else if (!strcasecmp(arg, "seki") || !strcasecmp(arg, "alive")) {
261 gtp_prefix('=', id);
262 bool printed_group = false;
263 foreach_point(board) { // foreach_group, effectively
264 group_t g = group_at(board, c);
265 if (!g || g != c) continue;
267 for (int i = 0; i < q.moves; i++) {
268 if (q.move[i] == g)
269 goto next_group;
271 foreach_in_group(board, g) {
272 printf("%s ", coord2sstr(c, board));
273 } foreach_in_group_end;
274 putchar('\n');
275 printed_group = true;
276 next_group:;
277 } foreach_point_end;
278 if (!printed_group)
279 putchar('\n');
280 gtp_flush();
281 } else {
282 gtp_error(id, "illegal status specifier", NULL);
285 /* Custom commands for handling UCT opening book */
286 } else if (!strcasecmp(cmd, "uct_genbook")) {
287 /* Board must be initialized properly, as if for genmove;
288 * makes sense only as 'uct_genbook b'. */
289 char *arg;
290 next_tok(arg);
291 enum stone color = str2stone(arg);
292 if (uct_genbook(engine, board, color))
293 gtp_reply(id, NULL);
294 else
295 gtp_error(id, "error generating book", NULL);
297 } else if (!strcasecmp(cmd, "uct_dumpbook")) {
298 char *arg;
299 next_tok(arg);
300 enum stone color = str2stone(arg);
301 uct_dumpbook(engine, board, color);
302 gtp_reply(id, NULL);
304 } else if (!strcasecmp(cmd, "kgs-chat")) {
305 char *loc;
306 next_tok(loc);
307 char *src;
308 next_tok(src);
309 char *msg;
310 next_tok(msg);
311 char *reply = NULL;
312 if (engine->chat)
313 reply = engine->chat(engine, board, msg);
314 if (reply)
315 gtp_reply(id, reply, NULL);
316 else
317 gtp_error(id, "unknown chat command", NULL);
319 } else {
320 gtp_error(id, "unknown command", NULL);
323 #undef next_tok