Before starting to tinker, ran clang-format on files that seem like relevant starting...
[sgk-go] / interface / gtp.c
CommitLineData
7eeb782e
AT
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
2 * This is GNU Go, a Go program. Contact gnugo@gnu.org, or see *
3 * http://www.gnu.org/software/gnugo/ for more information. *
4 * *
5 * To facilitate development of the Go Text Protocol, the two *
6 * files gtp.c and gtp.h are licensed under less restrictive *
7 * terms than the rest of GNU Go. *
8 * *
9 * Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 and *
10 * 2009 by the Free Software Foundation. *
11 * *
12 * Permission is hereby granted, free of charge, to any person *
13 * obtaining a copy of this file gtp.c, to deal in the Software *
14 * without restriction, including without limitation the rights *
15 * to use, copy, modify, merge, publish, distribute, and/or *
16 * sell copies of the Software, and to permit persons to whom *
17 * the Software is furnished to do so, provided that the above *
18 * copyright notice(s) and this permission notice appear in all *
19 * copies of the Software and that both the above copyright *
20 * notice(s) and this permission notice appear in supporting *
21 * documentation. *
22 * *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY *
24 * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE *
25 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR *
26 * PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO *
27 * EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS *
28 * NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR *
29 * CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING *
30 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF *
31 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT *
32 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS *
33 * SOFTWARE. *
34 * *
35 * Except as contained in this notice, the name of a copyright *
36 * holder shall not be used in advertising or otherwise to *
37 * promote the sale, use or other dealings in this Software *
38 * without prior written authorization of the copyright holder. *
39\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
40
c150f57c
AT
41#include <assert.h>
42#include <ctype.h>
7eeb782e
AT
43#include <stdio.h>
44#include <string.h>
7eeb782e
AT
45
46#include "gtp.h"
47
48/* These are copied from gnugo.h. We don't include this file in order
49 * to remain as independent as possible of GNU Go internals.
50 */
c150f57c
AT
51#define EMPTY 0
52#define WHITE 1
53#define BLACK 2
7eeb782e
AT
54
55/* We need to keep track of the board size in order to be able to
56 * convert between coordinate descriptions. We could also have passed
57 * the board size in all calls needing it, but that would be
58 * unnecessarily inconvenient.
59 */
60static int gtp_boardsize = -1;
61
62/* Vertex transformation hooks. */
63static gtp_transform_ptr vertex_transform_input_hook = NULL;
64static gtp_transform_ptr vertex_transform_output_hook = NULL;
65
66/* Current id number. We keep track of this internally rather than
67 * pass it to the functions processing the commands, since those can't
68 * do anything useful with it anyway.
69 */
70static int current_id;
71
72/* The file all GTP output goes to. This is made global for the user
73 * of this file may want to use functions other than gtp_printf() etc.
74 * Set by gtp_main_loop().
75 */
c150f57c 76FILE* gtp_output_file = NULL;
7eeb782e
AT
77
78/* Read filehandle gtp_input linewise and interpret as GTP commands. */
c150f57c
AT
79void gtp_main_loop(struct gtp_command commands[],
80 FILE* gtp_input, FILE* gtp_output, FILE* gtp_dump_commands)
7eeb782e 81{
c150f57c
AT
82 char line[GTP_BUFSIZE];
83 char command[GTP_BUFSIZE];
84 char* p;
85 int i;
86 int n;
87 int status = GTP_OK;
88
89 gtp_output_file = gtp_output;
90
91 while (status == GTP_OK) {
92 /* Read a line from gtp_input. */
93 if (!fgets(line, GTP_BUFSIZE, gtp_input))
94 break; /* EOF or some error */
95
96 if (gtp_dump_commands) {
97 fputs(line, gtp_dump_commands);
98 fflush(gtp_dump_commands);
99 }
100
101 /* Preprocess the line. */
102 for (i = 0, p = line; line[i]; i++) {
103 char c = line[i];
104 /* Convert HT (9) to SPACE (32). */
105 if (c == 9)
106 *p++ = 32;
107 /* Remove CR (13) and all other control characters except LF (10). */
108 else if ((c > 0 && c <= 9)
109 || (c >= 11 && c <= 31)
110 || c == 127)
111 continue;
112 /* Remove comments. */
113 else if (c == '#')
114 break;
115 /* Keep ordinary text. */
116 else
117 *p++ = c;
118 }
119 /* Terminate string. */
120 *p = 0;
121
122 p = line;
123
124 /* Look for an identification number. */
125 if (sscanf(p, "%d%n", &current_id, &n) == 1)
126 p += n;
127 else
128 current_id = -1; /* No identification number. */
129
130 /* Look for command name. */
131 if (sscanf(p, " %s %n", command, &n) < 1)
132 continue; /* Whitespace only on this line, ignore. */
133 p += n;
134
135 /* Search the list of commands and call the corresponding function
7eeb782e
AT
136 * if it's found.
137 */
c150f57c
AT
138 for (i = 0; commands[i].name != NULL; i++) {
139 if (strcmp(command, commands[i].name) == 0) {
140 status = (*commands[i].function)(p);
141 break;
142 }
143 }
144 if (commands[i].name == NULL)
145 gtp_failure("unknown command");
146
147 if (status == GTP_FATAL)
148 gtp_panic();
7eeb782e 149 }
7eeb782e
AT
150}
151
152/* Set the board size used in coordinate conversions. */
c150f57c 153void gtp_internal_set_boardsize(int size)
7eeb782e 154{
c150f57c 155 gtp_boardsize = size;
7eeb782e
AT
156}
157
158/* If you need to transform the coordinates on input or output, use
159 * these functions to set hook functions which are called any time
160 * coordinates are read or about to be written. In GNU Go this is used
161 * to simulate rotated boards in regression tests.
162 */
c150f57c 163void gtp_set_vertex_transform_hooks(gtp_transform_ptr in, gtp_transform_ptr out)
7eeb782e 164{
c150f57c
AT
165 vertex_transform_input_hook = in;
166 vertex_transform_output_hook = out;
7eeb782e
AT
167}
168
169/*
170 * This function works like printf, except that it only understands
171 * very few of the standard formats, to be precise %c, %d, %f, %s.
172 * But it also accepts %m, which takes two integers and writes a vertex,
173 * and %C, which takes a color value and writes a color string.
174 */
c150f57c 175void gtp_mprintf(const char* fmt, ...)
7eeb782e 176{
c150f57c
AT
177 va_list ap;
178 va_start(ap, fmt);
179
180 for (; *fmt; ++fmt) {
181 if (*fmt == '%') {
182 switch (*++fmt) {
183 case 'c': {
184 /* rules of promotion => passed as int, not char */
185 int c = va_arg(ap, int);
186 putc(c, gtp_output_file);
187 break;
188 }
189 case 'd': {
190 int d = va_arg(ap, int);
191 fprintf(gtp_output_file, "%d", d);
192 break;
193 }
194 case 'f': {
195 double f = va_arg(ap, double); /* passed as double, not float */
196 fprintf(gtp_output_file, "%f", f);
197 break;
198 }
199 case 's': {
200 char* s = va_arg(ap, char*);
201 fputs(s, gtp_output_file);
202 break;
203 }
204 case 'm': {
205 int m = va_arg(ap, int);
206 int n = va_arg(ap, int);
207 gtp_print_vertex(m, n);
208 break;
209 }
210 case 'C': {
211 int color = va_arg(ap, int);
212 if (color == WHITE)
213 fputs("white", gtp_output_file);
214 else if (color == BLACK)
215 fputs("black", gtp_output_file);
216 else
217 fputs("empty", gtp_output_file);
218 break;
219 }
220 default:
221 /* FIXME: Should go to `stderr' instead? */
222 fprintf(gtp_output_file, "\n\nUnknown format character '%c'\n", *fmt);
223 break;
224 }
225 } else
226 putc(*fmt, gtp_output_file);
7eeb782e 227 }
c150f57c 228 va_end(ap);
7eeb782e
AT
229}
230
7eeb782e 231/* This currently works exactly like printf. */
c150f57c 232void gtp_printf(const char* format, ...)
7eeb782e 233{
c150f57c
AT
234 va_list ap;
235 va_start(ap, format);
236 vfprintf(gtp_output_file, format, ap);
237 va_end(ap);
7eeb782e
AT
238}
239
7eeb782e
AT
240/* Write success or failure indication plus identity number if one was
241 * given.
242 */
c150f57c 243void gtp_start_response(int status)
7eeb782e 244{
c150f57c
AT
245 if (status == GTP_SUCCESS)
246 gtp_printf("=");
247 else
248 gtp_printf("?");
7eeb782e 249
c150f57c
AT
250 if (current_id < 0)
251 gtp_printf(" ");
252 else
253 gtp_printf("%d ", current_id);
254}
7eeb782e
AT
255
256/* Finish a GTP response by writing a double newline and returning GTP_OK. */
c150f57c 257int gtp_finish_response()
7eeb782e 258{
c150f57c
AT
259 gtp_printf("\n\n");
260 return GTP_OK;
7eeb782e
AT
261}
262
7eeb782e
AT
263/* Write a full success response. Except for the id number, the call
264 * is just like one to printf.
265 */
c150f57c 266int gtp_success(const char* format, ...)
7eeb782e 267{
c150f57c
AT
268 va_list ap;
269 gtp_start_response(GTP_SUCCESS);
270 va_start(ap, format);
271 vfprintf(gtp_output_file, format, ap);
272 va_end(ap);
273 return gtp_finish_response();
7eeb782e
AT
274}
275
7eeb782e 276/* Write a full failure response. The call is identical to gtp_success. */
c150f57c 277int gtp_failure(const char* format, ...)
7eeb782e 278{
c150f57c
AT
279 va_list ap;
280 gtp_start_response(GTP_FAILURE);
281 va_start(ap, format);
282 vfprintf(gtp_output_file, format, ap);
283 va_end(ap);
284 return gtp_finish_response();
7eeb782e
AT
285}
286
7eeb782e 287/* Write a panic message. */
c150f57c 288void gtp_panic()
7eeb782e 289{
c150f57c 290 gtp_printf("! panic\n\n");
7eeb782e
AT
291}
292
7eeb782e
AT
293/* Convert a string describing a color, "b", "black", "w", or "white",
294 * to GNU Go's integer representation of colors. Return the number of
295 * characters read from the string s.
296 */
c150f57c 297int gtp_decode_color(char* s, int* color)
7eeb782e 298{
c150f57c
AT
299 char color_string[7];
300 int i;
301 int n;
302
303 assert(gtp_boardsize > 0);
304
305 if (sscanf(s, "%6s%n", color_string, &n) != 1)
306 return 0;
7eeb782e 307
c150f57c
AT
308 for (i = 0; i < (int)strlen(color_string); i++)
309 color_string[i] = tolower((int)color_string[i]);
310
311 if (strcmp(color_string, "b") == 0
312 || strcmp(color_string, "black") == 0)
313 *color = BLACK;
314 else if (strcmp(color_string, "w") == 0
315 || strcmp(color_string, "white") == 0)
316 *color = WHITE;
317 else
318 return 0;
319
320 return n;
321}
7eeb782e
AT
322
323/* Convert an intersection given by a string to two coordinates
324 * according to GNU Go's convention. Return the number of characters
325 * read from the string s.
326 */
c150f57c 327int gtp_decode_coord(char* s, int* i, int* j)
7eeb782e 328{
c150f57c
AT
329 char column;
330 int row;
331 int n;
7eeb782e 332
c150f57c 333 assert(gtp_boardsize > 0);
7eeb782e 334
c150f57c
AT
335 if (sscanf(s, " %c%d%n", &column, &row, &n) != 2)
336 return 0;
7eeb782e 337
c150f57c
AT
338 if (tolower((int)column) == 'i')
339 return 0;
340 *j = tolower((int)column) - 'a';
341 if (tolower((int)column) > 'i')
342 --*j;
7eeb782e 343
c150f57c 344 *i = gtp_boardsize - row;
7eeb782e 345
c150f57c
AT
346 if (*i < 0 || *i >= gtp_boardsize || *j < 0 || *j >= gtp_boardsize)
347 return 0;
7eeb782e 348
c150f57c
AT
349 if (vertex_transform_input_hook != NULL)
350 (*vertex_transform_input_hook)(*i, *j, i, j);
351
352 return n;
7eeb782e
AT
353}
354
355/* Convert a move, i.e. "b" or "w" followed by a vertex to a color and
356 * coordinates. Return the number of characters read from the string
357 * s. The vertex may be "pass" and then the coordinates are set to (-1, -1).
358 */
c150f57c 359int gtp_decode_move(char* s, int* color, int* i, int* j)
7eeb782e 360{
c150f57c
AT
361 int n1, n2;
362 int k;
363
364 assert(gtp_boardsize > 0);
365
366 n1 = gtp_decode_color(s, color);
367 if (n1 == 0)
368 return 0;
369
370 n2 = gtp_decode_coord(s + n1, i, j);
371 if (n2 == 0) {
372 char buf[6];
373 if (sscanf(s + n1, "%5s%n", buf, &n2) != 1)
374 return 0;
375 for (k = 0; k < (int)strlen(buf); k++)
376 buf[k] = tolower((int)buf[k]);
377 if (strcmp(buf, "pass") != 0)
378 return 0;
379 *i = -1;
380 *j = -1;
381 }
382
383 return n1 + n2;
7eeb782e
AT
384}
385
386/* This a bubble sort. Given the expected size of the sets to
387 * sort, it's probably not worth the overhead to set up a call to
388 * qsort.
389 */
390static void
391sort_moves(int n, int movei[], int movej[])
392{
c150f57c
AT
393 int b, a;
394 for (b = n - 1; b > 0; b--) {
395 for (a = 0; a < b; a++) {
396 if (movei[a] > movei[b]
397 || (movei[a] == movei[b] && movej[a] > movej[b])) {
398 int tmp;
399 tmp = movei[b];
400 movei[b] = movei[a];
401 movei[a] = tmp;
402 tmp = movej[b];
403 movej[b] = movej[a];
404 movej[a] = tmp;
405 }
406 }
7eeb782e 407 }
7eeb782e
AT
408}
409
410/* Write a number of space separated vertices. The moves are sorted
411 * before being written.
412 */
c150f57c 413void gtp_print_vertices(int n, int movei[], int movej[])
7eeb782e 414{
c150f57c
AT
415 int k;
416 int ri, rj;
417
418 assert(gtp_boardsize > 0);
419
420 sort_moves(n, movei, movej);
421 for (k = 0; k < n; k++) {
422 if (k > 0)
423 gtp_printf(" ");
424 if (movei[k] == -1 && movej[k] == -1)
425 gtp_printf("PASS");
426 else if (movei[k] < 0 || movei[k] >= gtp_boardsize
427 || movej[k] < 0 || movej[k] >= gtp_boardsize)
428 gtp_printf("??");
429 else {
430 if (vertex_transform_output_hook != NULL)
431 (*vertex_transform_output_hook)(movei[k], movej[k], &ri, &rj);
432 else {
433 ri = movei[k];
434 rj = movej[k];
435 }
436 gtp_printf("%c%d", 'A' + rj + (rj >= 8), gtp_boardsize - ri);
437 }
7eeb782e 438 }
7eeb782e
AT
439}
440
441/* Write a single move. */
c150f57c 442void gtp_print_vertex(int i, int j)
7eeb782e 443{
c150f57c 444 gtp_print_vertices(1, &i, &j);
7eeb782e
AT
445}
446
7eeb782e
AT
447/*
448 * Local Variables:
c150f57c
AT
449 * tab-width: 4
450 * c-basic-offset: 4
7eeb782e
AT
451 * End:
452 */