pattern_match_spatial(): Use the incrementally matched hashes
[pachi/json.git] / gtp.c
blob7de5cf2809f762e67193aee554cf9b87e098415c
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->done_board_state);
122 engine->done_board_state(engine, board);
124 board_clear(board);
125 if (DEBUGL(1))
126 board_print(board, stderr);
127 gtp_reply(id, NULL);
129 } else if (!strcasecmp(cmd, "komi")) {
130 char *arg;
131 next_tok(arg);
132 sscanf(arg, "%f", &board->komi);
134 if (DEBUGL(1))
135 board_print(board, stderr);
136 gtp_reply(id, NULL);
138 } else if (!strcasecmp(cmd, "play")) {
139 struct move m;
141 char *arg;
142 next_tok(arg);
143 m.color = str2stone(arg);
144 next_tok(arg);
145 coord_t *c = str2coord(arg, board_size(board));
146 m.coord = *c; coord_done(c);
147 char *reply = NULL;
149 if (DEBUGL(1))
150 fprintf(stderr, "got move %d,%d,%d\n", m.color, coord_x(m.coord, board), coord_y(m.coord, board));
151 if (engine->notify_play)
152 reply = engine->notify_play(engine, board, &m);
153 if (board_play(board, &m) < 0) {
154 if (DEBUGL(0)) {
155 fprintf(stderr, "! ILLEGAL MOVE %d,%d,%d\n", m.color, coord_x(m.coord, board), coord_y(m.coord, board));
156 board_print(board, stderr);
158 gtp_error(id, "illegal move", NULL);
159 } else {
160 if (DEBUGL(1))
161 board_print(board, stderr);
162 gtp_reply(id, reply, NULL);
165 } else if (!strcasecmp(cmd, "genmove")) {
166 char *arg;
167 next_tok(arg);
168 enum stone color = str2stone(arg);
169 coord_t *c = engine->genmove(engine, board, color);
170 struct move m = { *c, color };
171 board_play(board, &m);
172 char *str = coord2str(*c, board);
173 if (DEBUGL(1))
174 fprintf(stderr, "playing move %s\n", str);
175 if (DEBUGL(1)) {
176 board_print_custom(board, stderr, engine->printhook);
178 gtp_reply(id, str, NULL);
179 free(str); coord_done(c);
181 } else if (!strcasecmp(cmd, "set_free_handicap")) {
182 struct move m;
183 m.color = S_BLACK;
185 char *arg;
186 next_tok(arg);
187 do {
188 coord_t *c = str2coord(arg, board_size(board));
189 m.coord = *c; coord_done(c);
190 if (DEBUGL(1))
191 fprintf(stderr, "setting handicap %d,%d\n", coord_x(m.coord, board), coord_y(m.coord, board));
193 if (board_play(board, &m) < 0) {
194 if (DEBUGL(0))
195 fprintf(stderr, "! ILLEGAL MOVE %d,%d,%d\n", m.color, coord_x(m.coord, board), coord_y(m.coord, board));
196 gtp_error(id, "illegal move", NULL);
198 board->handicap++;
199 next_tok(arg);
200 } while (*arg);
201 if (DEBUGL(1))
202 board_print(board, stderr);
203 gtp_reply(id, NULL);
205 /* TODO: Engine should choose free handicap; however, it tends to take
206 * overly long to think it all out, and unless it's clever its
207 * handicap stones won't be of much help. ;-) */
208 } else if (!strcasecmp(cmd, "place_free_handicap")
209 || !strcasecmp(cmd, "fixed_handicap")) {
210 char *arg;
211 next_tok(arg);
212 int stones = atoi(arg);
214 gtp_prefix('=', id);
215 board_handicap(board, stones, stdout);
216 if (DEBUGL(1))
217 board_print(board, stderr);
218 putchar('\n');
219 gtp_flush();
221 } else if (!strcasecmp(cmd, "final_score")) {
222 struct move_queue q = { .moves = 0 };
223 if (engine->dead_group_list)
224 engine->dead_group_list(engine, board, &q);
225 float score = board_official_score(board, &q);
226 char str[64];
227 if (DEBUGL(1))
228 fprintf(stderr, "counted score %.1f\n", score);
229 if (score == 0) {
230 gtp_reply(id, "0", NULL);
231 } else if (score > 0) {
232 snprintf(str, 64, "W+%.1f", score);
233 gtp_reply(id, str, NULL);
234 } else {
235 snprintf(str, 64, "B+%.1f", -score);
236 gtp_reply(id, str, NULL);
239 /* XXX: This is a huge hack. */
240 } else if (!strcasecmp(cmd, "final_status_list")) {
241 char *arg;
242 next_tok(arg);
243 struct move_queue q = { .moves = 0 };
244 if (engine->dead_group_list)
245 engine->dead_group_list(engine, board, &q);
246 /* else we return empty list - i.e. engine not supporting
247 * this assumes all stones alive at the game end. */
248 if (!strcasecmp(arg, "dead")) {
249 gtp_prefix('=', id);
250 for (int i = 0; i < q.moves; i++) {
251 foreach_in_group(board, q.move[i]) {
252 printf("%s ", coord2sstr(c, board));
253 } foreach_in_group_end;
254 putchar('\n');
256 if (!q.moves)
257 putchar('\n');
258 gtp_flush();
259 } else if (!strcasecmp(arg, "seki") || !strcasecmp(arg, "alive")) {
260 gtp_prefix('=', id);
261 bool printed_group = false;
262 foreach_point(board) { // foreach_group, effectively
263 group_t g = group_at(board, c);
264 if (!g || g != c) continue;
266 for (int i = 0; i < q.moves; i++) {
267 if (q.move[i] == g)
268 goto next_group;
270 foreach_in_group(board, g) {
271 printf("%s ", coord2sstr(c, board));
272 } foreach_in_group_end;
273 putchar('\n');
274 printed_group = true;
275 next_group:;
276 } foreach_point_end;
277 if (!printed_group)
278 putchar('\n');
279 gtp_flush();
280 } else {
281 gtp_error(id, "illegal status specifier", NULL);
284 /* Custom commands for handling UCT opening book */
285 } else if (!strcasecmp(cmd, "uct_genbook")) {
286 /* Board must be initialized properly, as if for genmove;
287 * makes sense only as 'uct_genbook b'. */
288 char *arg;
289 next_tok(arg);
290 enum stone color = str2stone(arg);
291 if (uct_genbook(engine, board, color))
292 gtp_reply(id, NULL);
293 else
294 gtp_error(id, "error generating book", NULL);
296 } else if (!strcasecmp(cmd, "uct_dumpbook")) {
297 char *arg;
298 next_tok(arg);
299 enum stone color = str2stone(arg);
300 uct_dumpbook(engine, board, color);
301 gtp_reply(id, NULL);
303 } else if (!strcasecmp(cmd, "kgs-chat")) {
304 char *loc;
305 next_tok(loc);
306 char *src;
307 next_tok(src);
308 char *msg;
309 next_tok(msg);
310 char *reply = NULL;
311 if (engine->chat)
312 reply = engine->chat(engine, board, msg);
313 if (reply)
314 gtp_reply(id, reply, NULL);
315 else
316 gtp_error(id, "unknown chat command", NULL);
318 } else {
319 gtp_error(id, "unknown command", NULL);
322 #undef next_tok