Commit | Line | Data |
---|---|---|
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 | ||
45 | void | |
46 | vgprintf(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 | ||
174 | void | |
175 | gfprintf(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 | ||
189 | int | |
190 | gprintf(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 | ||
205 | void | |
206 | mprintf(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 | */ | |
218 | static void | |
219 | dump_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 | ||
272 | void | |
273 | abortgo(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, "\ | |
289 | Please mail this message, including the debug output above, \ | |
290 | to gnugo@gnu.org\n"); | |
291 | } | |
292 | fprintf(stderr, "\n"); | |
293 | ||
294 | fflush(stderr); | |
295 | fflush(stdout); | |
296 | ||
297 | abort(); /* cause core dump */ | |
298 | } | |
299 | ||
300 | static const char *color_names[] = { | |
301 | COLOR_NAMES | |
302 | }; | |
303 | ||
304 | /* Convert a color value to a string. */ | |
305 | const char * | |
306 | color_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. */ | |
313 | const char * | |
314 | location_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 | ||
330 | void | |
331 | location_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 | ||
361 | int | |
362 | string_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. */ | |
392 | int | |
393 | is_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. */ | |
463 | void | |
464 | draw_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 | */ | |
481 | void | |
482 | simple_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 | */ | |
517 | void | |
518 | mark_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 | */ |