Updated README: Equal sign not required with `--mode` flag.
[sgk-go] / engine / printutils.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 * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, *
6 * 2008 and 2009 by the Free Software Foundation. *
7 * *
8 * This program is free software; you can redistribute it and/or *
9 * modify it under the terms of the GNU General Public License as *
10 * published by the Free Software Foundation - version 3 or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License in file COPYING for more details. *
17 * *
18 * You should have received a copy of the GNU General Public *
19 * License along with this program; if not, write to the Free *
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
21 * Boston, MA 02111, USA. *
22\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
23
24#include "board.h"
25#include "hash.h"
26#include "gg_utils.h"
27#include "sgftree.h"
28
29#include <stdio.h>
30#include <string.h>
31#include <stdlib.h>
32#include <stdarg.h>
33#include <ctype.h>
34
35/*
36 * This function underpins all the TRACE and DEBUG stuff.
37 * It accepts %c, %d, %f, %s, and %x as usual. But it
38 * also accepts %m, which takes TWO integers and writes a move.
39 * Other accepted formats are
40 * %H: Print a hashvalue.
41 * %C: Print a color as a string.
42 * Nasty bodge: %o at the start means outdent, i.e. cancel indent.
43 */
44
45void
46vgprintf(FILE *outputfile, const char *fmt, va_list ap)
47{
48 if (fmt[0] == '%' && fmt[1] == 'o')
49 fmt += 2; /* cancel indent */
50 else if (stackp > 0)
51 fprintf(outputfile, "%.*s", stackp*2, " ");
52
53 for (; *fmt; ++fmt) {
54 if (*fmt == '%') {
55 switch (*++fmt) {
56 case 'c':
57 {
58 /* rules of promotion => passed as int, not char */
59 int c = va_arg(ap, int);
60 putc(c, outputfile);
61 break;
62 }
63 case 'd':
64 {
65 int d = va_arg(ap, int);
66 fprintf(outputfile, "%d", d);
67 break;
68 }
69 case 'x':
70 {
71 unsigned int d = va_arg(ap, unsigned int);
72 fprintf(outputfile, "%x", d);
73 break;
74 }
75 case 'f':
76 {
77 double f = va_arg(ap, double); /* passed as double, not float */
78 fprintf(outputfile, "%.2f", f);
79 break;
80 }
81 case 's':
82 {
83 char *s = va_arg(ap, char *);
84 fputs(s, outputfile);
85 break;
86 }
87 case '2':
88 fmt++;
89 if (*fmt != 'm' && *fmt != 'M') {
90 fprintf(outputfile, "\n\nUnknown format string '2%c'\n", *fmt);
91 break;
92 }
93 /* else fall through - 2 modifier on %m is default. */
94 case 'm':
95 case 'M':
96 {
97 char movename[4];
98 int m = va_arg(ap, int);
99 int n = va_arg(ap, int);
100 if (m == -1 && n == -1)
101 fputs("PASS", outputfile);
102 else if (!ON_BOARD2(m, n))
103 fprintf(outputfile, "[%d,%d]", m, n);
104 else {
105 /* Generate the move name. */
106 if (n < 8)
107 movename[0] = n + 65;
108 else
109 movename[0] = n + 66;
110 if (*fmt == 'm')
111 sprintf(movename+1, "%d", board_size - m);
112 else
113 sprintf(movename+1, "%-2d", board_size - m);
114 fputs(movename, outputfile);
115 }
116 break;
117 }
118 case '1':
119 fmt++;
120 if (*fmt != 'm' && *fmt != 'M') {
121 fprintf(outputfile, "\n\nUnknown format string '1%c'\n", *fmt);
122 break;
123 }
124 else {
125 char movename[4];
126 int pos = va_arg(ap, int);
127 int m = I(pos);
128 int n = J(pos);
129 if (pos == NO_MOVE)
130 fputs("PASS", outputfile);
131 else if (!ON_BOARD1(pos))
132 fprintf(outputfile, "[%d]", pos);
133 else {
134 /* Generate the move name. */
135 if (n < 8)
136 movename[0] = n + 65;
137 else
138 movename[0] = n + 66;
139 if (*fmt == 'm')
140 sprintf(movename + 1, "%d", board_size - m);
141 else
142 sprintf(movename + 1, "%-2d", board_size - m);
143 fputs(movename, outputfile);
144 }
145 break;
146 }
147 case 'H':
148 {
149 unsigned long h = va_arg(ap, unsigned long);
150 fprintf(outputfile, "%lx", h);
151 break;
152 }
153 case 'C':
154 {
155 int color = va_arg(ap, int);
156 fputs(color_to_string(color), outputfile);
157 break;
158 }
159 default:
160 fprintf(outputfile, "\n\nUnknown format character '%c'\n", *fmt);
161 break;
162 }
163 }
164 else
165 putc(*fmt, outputfile);
166 }
167}
168
169
170/*
171 * required wrapper around vgprintf, writes to outfile.
172 */
173
174void
175gfprintf(FILE *outfile, const char *fmt, ...)
176{
177 va_list ap;
178 va_start(ap, fmt);
179 vgprintf(outfile, fmt, ap);
180 va_end(ap);
181}
182
183
184/*
185 * required wrapper around vgprintf, writes to stderr.
186 * Always returns 1 to allow use in short-circuit logical expressions.
187 */
188
189int
190gprintf(const char *fmt, ...)
191{
192 va_list ap;
193 va_start(ap, fmt);
194 vgprintf(stderr, fmt, ap);
195 va_end(ap);
196 return 1;
197}
198
199
200/*
201 * required wrapper around vgprintf, in contrast to gprintf this one
202 * writes to stdout.
203 */
204
205void
206mprintf(const char *fmt, ...)
207{
208 va_list ap;
209 va_start(ap, fmt);
210 vgprintf(stdout, fmt, ap);
211 va_end(ap);
212}
213
214/* This writes the move history information in sgf format to stderr.
215 * This is only intended as a stand-alone debug tool for use in
216 * abortgo(). Anywhere else you should use the normal sgf library.
217 */
218static void
219dump_board_sgf(void)
220{
221 int pos;
222 int initial_colors_found = EMPTY;
223 int color;
224 int k;
225
226 for (pos = BOARDMIN; pos < BOARDMAX; pos++)
227 if (ON_BOARD(pos))
228 initial_colors_found |= initial_board[pos];
229
230 fprintf(stderr, "(;GM[1]FF[4]SZ[%d]KM[%.1f]HA[%d]GN[GNU Go %s stepped on a bug]\n",
231 board_size, komi, handicap, gg_version());
232
233 for (color = WHITE; color <= BLACK; color++) {
234 if (initial_colors_found & color) {
235 fprintf(stderr, "A%s", color == WHITE ? "W" : "B");
236 for (k = 0, pos = BOARDMIN; pos < BOARDMAX; pos++) {
237 if (ON_BOARD(pos) && initial_board[pos] == color) {
238 fprintf(stderr, "[%c%c]", 'a' + J(pos), 'a' + I(pos));
239 k++;
240 if (k % 16 == 0)
241 fprintf(stderr, "\n");
242 }
243 }
244 if (k % 16 != 0)
245 fprintf(stderr, "\n");
246 }
247 }
248
249 if (move_history_pointer > 0) {
250 for (k = 0; k < move_history_pointer; k++) {
251 fprintf(stderr, ";%s", move_history_color[k] == WHITE ? "W" : "B");
252 if (move_history_pos[k] == PASS_MOVE)
253 fprintf(stderr, "[]");
254 else
255 fprintf(stderr, "[%c%c]", 'a' + J(move_history_pos[k]),
256 'a' + I(move_history_pos[k]));
257
258 if (k % 12 == 11)
259 fprintf(stderr, "\n");
260 }
261 if (k % 12 != 0)
262 fprintf(stderr, "\n");
263 }
264 fprintf(stderr, ")\n");
265}
266
267/*
268 * A wrapper around abort() which shows the state variables at the time
269 * of the problem. (pos) is typically a related move, or NO_MOVE.
270 */
271
272void
273abortgo(const char *file, int line, const char *msg, int pos)
274{
275 gprintf("%o\n\n***assertion failure:\n%s:%d - %s near %1m***\n\n",
276 file, line, msg, pos);
277 dump_stack();
278
279 /* Print the board at the top of the stack. */
280 simple_showboard(stderr);
281 fprintf(stderr, "\n");
282
283 dump_board_sgf();
284
285 fprintf(stderr, "gnugo %s (seed %d): You stepped on a bug.\n",
286 gg_version(), get_random_seed());
287 if (board_size >= 9 && board_size <= 19) {
288 fprintf(stderr, "\
289Please mail this message, including the debug output above, \
290to gnugo@gnu.org\n");
291 }
292 fprintf(stderr, "\n");
293
294 fflush(stderr);
295 fflush(stdout);
296
297 abort(); /* cause core dump */
298}
299
300static const char *color_names[] = {
301 COLOR_NAMES
302};
303
304/* Convert a color value to a string. */
305const char *
306color_to_string(int color)
307{
308 gg_assert(color < NUM_KOMASTER_STATES);
309 return color_names[color];
310}
311
312/* Convert a location to a string. */
313const char *
314location_to_string(int pos)
315{
316 static int init = 0;
317 static char buf[BOARDSIZE][5];
318 if (!init) {
319 int pos;
320 for (pos = 0; pos < BOARDSIZE; pos++)
321 location_to_buffer(pos, buf[pos]);
322 init = 1;
323 }
324 ASSERT1(pos >= 0 && pos < BOARDSIZE, pos);
325 return buf[pos];
326}
327
328/* Convert a location to a string, writing to a buffer. */
329
330void
331location_to_buffer(int pos, char *buf)
332{
333 char *bufp = buf;
334 int i = I(pos);
335 int j = J(pos);
336
337 if (pos == NO_MOVE) {
338 strcpy(buf, "Pass");
339 return;
340 }
341
342 *bufp = 'A'+j;
343 if (*bufp >= 'I')
344 (*bufp)++;
345 bufp++;
346
347 i = board_size - i;
348 if (i > 9)
349 *bufp++ = '0' + i/10;
350 *bufp++ = '0' + i%10;
351
352 *bufp = 0;
353}
354
355
356/*
357 * Convert the string str to a 1D coordinate. Return NO_MOVE if invalid
358 * string.
359 */
360
361int
362string_to_location(int boardsize, const char *str)
363{
364 int m, n;
365
366 if (*str == '\0')
367 return NO_MOVE;
368
369 if (!isalpha((int) *str))
370 return NO_MOVE;
371
372 n = tolower((int) *str) - 'a';
373 if (tolower((int) *str) >= 'i')
374 --n;
375 if (n < 0 || n > boardsize - 1)
376 return NO_MOVE;
377
378 if (!isdigit((int) *(str + 1)))
379 return NO_MOVE;
380
381 m = boardsize - atoi(str + 1);
382 if (m < 0 || m > boardsize - 1)
383 return NO_MOVE;
384
385 return POS(m, n);
386}
387
388
389/* Some simple functions to draw an ASCII board. */
390
391/* True if the coordinate is a hoshi point. */
392int
393is_hoshi_point(int m, int n)
394{
395 int hoshi;
396 int middle;
397
398 /* No hoshi points on these boards. */
399 if (board_size == 2 || board_size == 4)
400 return 0;
401
402 /* In the middle of a 3x3 board. */
403 if (board_size == 3) {
404 if (m == 1 && n == 1)
405 return 1;
406
407 return 0;
408 }
409
410 if (board_size == 5) {
411 if (m == 1 && (n == 1 || n == 3))
412 return 1;
413 if (m == 2 && n == 2)
414 return 1;
415 if (m == 3 && (n == 1 || n == 3))
416 return 1;
417
418 return 0;
419 }
420
421 /* 3-3 points are hoshi on sizes 7--11, 4-4 on larger. */
422 if (board_size <= 11)
423 hoshi = 2;
424 else
425 hoshi = 3;
426
427 /* Coordinate for midpoint. */
428 middle = board_size/2;
429
430 /* Normalize the coordinates by mirroring to the lower numbers. */
431 if (m >= middle)
432 m = board_size - 1 - m;
433 if (n >= middle)
434 n = board_size - 1 - n;
435
436 /* Is this a corner hoshi? */
437 if (m == hoshi && n == hoshi)
438 return 1;
439
440 /* If even sized board, only hoshi points in the corner. */
441 if (board_size%2 == 0)
442 return 0;
443
444 /* Less than 12 in board size only middle point. */
445 if (board_size < 12) {
446 if (m == middle && n == middle)
447 return 1;
448
449 return 0;
450 }
451
452 /* Is this a midpoint hoshi? */
453 if ((m == hoshi || m == middle)
454 && (n == hoshi || n == middle))
455 return 1;
456
457 /* No more chances. */
458 return 0;
459}
460
461
462/* Print a line with coordinate letters above the board. */
463void
464draw_letter_coordinates(FILE *outfile)
465{
466 int i;
467 int ch;
468
469 fprintf(outfile, " ");
470 for (i = 0, ch = 'A'; i < board_size; i++, ch++) {
471 if (ch == 'I')
472 ch++;
473 fprintf(outfile, " %c", ch);
474 }
475}
476
477
478/* Bare bones version of showboard(0). No fancy options, no hint of
479 * color, and you can choose where to write it.
480 */
481void
482simple_showboard(FILE *outfile)
483{
484 int i, j;
485
486 draw_letter_coordinates(outfile);
487
488 for (i = 0; i < board_size; i++) {
489 fprintf(outfile, "\n%2d", board_size - i);
490
491 for (j = 0; j < board_size; j++) {
492 if (BOARD(i, j) == EMPTY)
493 fprintf(outfile, " %c", is_hoshi_point(i, j) ? '+' : '.');
494 else
495 fprintf(outfile, " %c", BOARD(i, j) == BLACK ? 'X' : 'O');
496 }
497
498 fprintf(outfile, " %d", board_size - i);
499
500 if ((board_size < 10 && i == board_size-2)
501 || (board_size >= 10 && i == 8))
502 fprintf(outfile, " WHITE (O) has captured %d stones", black_captured);
503
504 if ((board_size < 10 && i == board_size-1)
505 || (board_size >= 10 && i == 9))
506 fprintf(outfile, " BLACK (X) has captured %d stones", white_captured);
507 }
508
509 fprintf(outfile, "\n");
510 draw_letter_coordinates(outfile);
511}
512
513
514/* Adds square marks for each goal intersecion in the current sgf_dumptree.
515 * This function cannot be in sgf/ as it has to understand the 1-D board.
516 */
517void
518mark_goal_in_sgf(signed char goal[BOARDMAX])
519{
520 int pos;
521 SGFNode *node;
522
523 if (!sgf_dumptree)
524 return;
525 node = sgftreeNodeCheck(sgf_dumptree);
526
527 for (pos = BOARDMIN; pos < BOARDMAX; pos++)
528 if (ON_BOARD(pos) && goal[pos])
529 sgfSquare(node, I(pos), J(pos));
530}
531
532
533
534/*
535 * Local Variables:
536 * tab-width: 8
537 * c-basic-offset: 2
538 * End:
539 */