Modified colors for compatibility with Tektronix 4107 terminal in ANSI mode.
[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
41#include <stdio.h>
42#include <string.h>
43#include <ctype.h>
44#include <assert.h>
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 */
51#define EMPTY 0
52#define WHITE 1
53#define BLACK 2
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 */
76FILE *gtp_output_file = NULL;
77
78
79/* Read filehandle gtp_input linewise and interpret as GTP commands. */
80void
81gtp_main_loop(struct gtp_command commands[],
82 FILE *gtp_input, FILE *gtp_output, FILE *gtp_dump_commands)
83{
84 char line[GTP_BUFSIZE];
85 char command[GTP_BUFSIZE];
86 char *p;
87 int i;
88 int n;
89 int status = GTP_OK;
90
91 gtp_output_file = gtp_output;
92
93 while (status == GTP_OK) {
94 /* Read a line from gtp_input. */
95 if (!fgets(line, GTP_BUFSIZE, gtp_input))
96 break; /* EOF or some error */
97
98 if (gtp_dump_commands) {
99 fputs(line, gtp_dump_commands);
100 fflush(gtp_dump_commands);
101 }
102
103 /* Preprocess the line. */
104 for (i = 0, p = line; line[i]; i++) {
105 char c = line[i];
106 /* Convert HT (9) to SPACE (32). */
107 if (c == 9)
108 *p++ = 32;
109 /* Remove CR (13) and all other control characters except LF (10). */
110 else if ((c > 0 && c <= 9)
111 || (c >= 11 && c <= 31)
112 || c == 127)
113 continue;
114 /* Remove comments. */
115 else if (c == '#')
116 break;
117 /* Keep ordinary text. */
118 else
119 *p++ = c;
120 }
121 /* Terminate string. */
122 *p = 0;
123
124 p = line;
125
126 /* Look for an identification number. */
127 if (sscanf(p, "%d%n", &current_id, &n) == 1)
128 p += n;
129 else
130 current_id = -1; /* No identification number. */
131
132 /* Look for command name. */
133 if (sscanf(p, " %s %n", command, &n) < 1)
134 continue; /* Whitespace only on this line, ignore. */
135 p += n;
136
137 /* Search the list of commands and call the corresponding function
138 * if it's found.
139 */
140 for (i = 0; commands[i].name != NULL; i++) {
141 if (strcmp(command, commands[i].name) == 0) {
142 status = (*commands[i].function)(p);
143 break;
144 }
145 }
146 if (commands[i].name == NULL)
147 gtp_failure("unknown command");
148
149 if (status == GTP_FATAL)
150 gtp_panic();
151 }
152}
153
154/* Set the board size used in coordinate conversions. */
155void
156gtp_internal_set_boardsize(int size)
157{
158 gtp_boardsize = size;
159}
160
161/* If you need to transform the coordinates on input or output, use
162 * these functions to set hook functions which are called any time
163 * coordinates are read or about to be written. In GNU Go this is used
164 * to simulate rotated boards in regression tests.
165 */
166void
167gtp_set_vertex_transform_hooks(gtp_transform_ptr in, gtp_transform_ptr out)
168{
169 vertex_transform_input_hook = in;
170 vertex_transform_output_hook = out;
171}
172
173/*
174 * This function works like printf, except that it only understands
175 * very few of the standard formats, to be precise %c, %d, %f, %s.
176 * But it also accepts %m, which takes two integers and writes a vertex,
177 * and %C, which takes a color value and writes a color string.
178 */
179void
180gtp_mprintf(const char *fmt, ...)
181{
182 va_list ap;
183 va_start(ap, fmt);
184
185 for (; *fmt; ++fmt) {
186 if (*fmt == '%') {
187 switch (*++fmt) {
188 case 'c':
189 {
190 /* rules of promotion => passed as int, not char */
191 int c = va_arg(ap, int);
192 putc(c, gtp_output_file);
193 break;
194 }
195 case 'd':
196 {
197 int d = va_arg(ap, int);
198 fprintf(gtp_output_file, "%d", d);
199 break;
200 }
201 case 'f':
202 {
203 double f = va_arg(ap, double); /* passed as double, not float */
204 fprintf(gtp_output_file, "%f", f);
205 break;
206 }
207 case 's':
208 {
209 char *s = va_arg(ap, char *);
210 fputs(s, gtp_output_file);
211 break;
212 }
213 case 'm':
214 {
215 int m = va_arg(ap, int);
216 int n = va_arg(ap, int);
217 gtp_print_vertex(m, n);
218 break;
219 }
220 case 'C':
221 {
222 int color = va_arg(ap, int);
223 if (color == WHITE)
224 fputs("white", gtp_output_file);
225 else if (color == BLACK)
226 fputs("black", gtp_output_file);
227 else
228 fputs("empty", gtp_output_file);
229 break;
230 }
231 default:
232 /* FIXME: Should go to `stderr' instead? */
233 fprintf(gtp_output_file, "\n\nUnknown format character '%c'\n", *fmt);
234 break;
235 }
236 }
237 else
238 putc(*fmt, gtp_output_file);
239 }
240 va_end(ap);
241}
242
243
244/* This currently works exactly like printf. */
245void
246gtp_printf(const char *format, ...)
247{
248 va_list ap;
249 va_start(ap, format);
250 vfprintf(gtp_output_file, format, ap);
251 va_end(ap);
252}
253
254
255/* Write success or failure indication plus identity number if one was
256 * given.
257 */
258void
259gtp_start_response(int status)
260{
261 if (status == GTP_SUCCESS)
262 gtp_printf("=");
263 else
264 gtp_printf("?");
265
266 if (current_id < 0)
267 gtp_printf(" ");
268 else
269 gtp_printf("%d ", current_id);
270}
271
272
273/* Finish a GTP response by writing a double newline and returning GTP_OK. */
274int
275gtp_finish_response()
276{
277 gtp_printf("\n\n");
278 return GTP_OK;
279}
280
281
282/* Write a full success response. Except for the id number, the call
283 * is just like one to printf.
284 */
285int
286gtp_success(const char *format, ...)
287{
288 va_list ap;
289 gtp_start_response(GTP_SUCCESS);
290 va_start(ap, format);
291 vfprintf(gtp_output_file, format, ap);
292 va_end(ap);
293 return gtp_finish_response();
294}
295
296
297/* Write a full failure response. The call is identical to gtp_success. */
298int
299gtp_failure(const char *format, ...)
300{
301 va_list ap;
302 gtp_start_response(GTP_FAILURE);
303 va_start(ap, format);
304 vfprintf(gtp_output_file, format, ap);
305 va_end(ap);
306 return gtp_finish_response();
307}
308
309
310/* Write a panic message. */
311void
312gtp_panic()
313{
314 gtp_printf("! panic\n\n");
315}
316
317
318/* Convert a string describing a color, "b", "black", "w", or "white",
319 * to GNU Go's integer representation of colors. Return the number of
320 * characters read from the string s.
321 */
322int
323gtp_decode_color(char *s, int *color)
324{
325 char color_string[7];
326 int i;
327 int n;
328
329 assert(gtp_boardsize > 0);
330
331 if (sscanf(s, "%6s%n", color_string, &n) != 1)
332 return 0;
333
334 for (i = 0; i < (int) strlen(color_string); i++)
335 color_string[i] = tolower((int) color_string[i]);
336
337 if (strcmp(color_string, "b") == 0
338 || strcmp(color_string, "black") == 0)
339 *color = BLACK;
340 else if (strcmp(color_string, "w") == 0
341 || strcmp(color_string, "white") == 0)
342 *color = WHITE;
343 else
344 return 0;
345
346 return n;
347}
348
349
350/* Convert an intersection given by a string to two coordinates
351 * according to GNU Go's convention. Return the number of characters
352 * read from the string s.
353 */
354int
355gtp_decode_coord(char *s, int *i, int *j)
356{
357 char column;
358 int row;
359 int n;
360
361 assert(gtp_boardsize > 0);
362
363 if (sscanf(s, " %c%d%n", &column, &row, &n) != 2)
364 return 0;
365
366 if (tolower((int) column) == 'i')
367 return 0;
368 *j = tolower((int) column) - 'a';
369 if (tolower((int) column) > 'i')
370 --*j;
371
372 *i = gtp_boardsize - row;
373
374 if (*i < 0 || *i >= gtp_boardsize || *j < 0 || *j >= gtp_boardsize)
375 return 0;
376
377 if (vertex_transform_input_hook != NULL)
378 (*vertex_transform_input_hook)(*i, *j, i, j);
379
380 return n;
381}
382
383/* Convert a move, i.e. "b" or "w" followed by a vertex to a color and
384 * coordinates. Return the number of characters read from the string
385 * s. The vertex may be "pass" and then the coordinates are set to (-1, -1).
386 */
387int
388gtp_decode_move(char *s, int *color, int *i, int *j)
389{
390 int n1, n2;
391 int k;
392
393 assert(gtp_boardsize > 0);
394
395 n1 = gtp_decode_color(s, color);
396 if (n1 == 0)
397 return 0;
398
399 n2 = gtp_decode_coord(s + n1, i, j);
400 if (n2 == 0) {
401 char buf[6];
402 if (sscanf(s + n1, "%5s%n", buf, &n2) != 1)
403 return 0;
404 for (k = 0; k < (int) strlen(buf); k++)
405 buf[k] = tolower((int) buf[k]);
406 if (strcmp(buf, "pass") != 0)
407 return 0;
408 *i = -1;
409 *j = -1;
410 }
411
412 return n1 + n2;
413}
414
415/* This a bubble sort. Given the expected size of the sets to
416 * sort, it's probably not worth the overhead to set up a call to
417 * qsort.
418 */
419static void
420sort_moves(int n, int movei[], int movej[])
421{
422 int b, a;
423 for (b = n-1; b > 0; b--) {
424 for (a = 0; a < b; a++) {
425 if (movei[a] > movei[b]
426 || (movei[a] == movei[b] && movej[a] > movej[b])) {
427 int tmp;
428 tmp = movei[b];
429 movei[b] = movei[a];
430 movei[a] = tmp;
431 tmp = movej[b];
432 movej[b] = movej[a];
433 movej[a] = tmp;
434 }
435 }
436 }
437}
438
439/* Write a number of space separated vertices. The moves are sorted
440 * before being written.
441 */
442void
443gtp_print_vertices(int n, int movei[], int movej[])
444{
445 int k;
446 int ri, rj;
447
448 assert(gtp_boardsize > 0);
449
450 sort_moves(n, movei, movej);
451 for (k = 0; k < n; k++) {
452 if (k > 0)
453 gtp_printf(" ");
454 if (movei[k] == -1 && movej[k] == -1)
455 gtp_printf("PASS");
456 else if (movei[k] < 0 || movei[k] >= gtp_boardsize
457 || movej[k] < 0 || movej[k] >= gtp_boardsize)
458 gtp_printf("??");
459 else {
460 if (vertex_transform_output_hook != NULL)
461 (*vertex_transform_output_hook)(movei[k], movej[k], &ri, &rj);
462 else {
463 ri = movei[k];
464 rj = movej[k];
465 }
466 gtp_printf("%c%d", 'A' + rj + (rj >= 8), gtp_boardsize - ri);
467 }
468 }
469}
470
471/* Write a single move. */
472void
473gtp_print_vertex(int i, int j)
474{
475 gtp_print_vertices(1, &i, &j);
476}
477
478
479/*
480 * Local Variables:
481 * tab-width: 8
482 * c-basic-offset: 2
483 * End:
484 */