/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * This is GNU Go, a Go program. Contact gnugo@gnu.org, or see * * http://www.gnu.org/software/gnugo/ for more information. * * * * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * * 2008 and 2009 by the Free Software Foundation. * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation - version 3 or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License in file COPYING for more details. * * * * You should have received a copy of the GNU General Public * * License along with this program; if not, write to the Free * * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02111, USA. * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "board.h" #include "hash.h" #include "gg_utils.h" #include "sgftree.h" #include #include #include #include #include /* * This function underpins all the TRACE and DEBUG stuff. * It accepts %c, %d, %f, %s, and %x as usual. But it * also accepts %m, which takes TWO integers and writes a move. * Other accepted formats are * %H: Print a hashvalue. * %C: Print a color as a string. * Nasty bodge: %o at the start means outdent, i.e. cancel indent. */ void vgprintf(FILE *outputfile, const char *fmt, va_list ap) { if (fmt[0] == '%' && fmt[1] == 'o') fmt += 2; /* cancel indent */ else if (stackp > 0) fprintf(outputfile, "%.*s", stackp*2, " "); for (; *fmt; ++fmt) { if (*fmt == '%') { switch (*++fmt) { case 'c': { /* rules of promotion => passed as int, not char */ int c = va_arg(ap, int); putc(c, outputfile); break; } case 'd': { int d = va_arg(ap, int); fprintf(outputfile, "%d", d); break; } case 'x': { unsigned int d = va_arg(ap, unsigned int); fprintf(outputfile, "%x", d); break; } case 'f': { double f = va_arg(ap, double); /* passed as double, not float */ fprintf(outputfile, "%.2f", f); break; } case 's': { char *s = va_arg(ap, char *); fputs(s, outputfile); break; } case '2': fmt++; if (*fmt != 'm' && *fmt != 'M') { fprintf(outputfile, "\n\nUnknown format string '2%c'\n", *fmt); break; } /* else fall through - 2 modifier on %m is default. */ case 'm': case 'M': { char movename[4]; int m = va_arg(ap, int); int n = va_arg(ap, int); if (m == -1 && n == -1) fputs("PASS", outputfile); else if (!ON_BOARD2(m, n)) fprintf(outputfile, "[%d,%d]", m, n); else { /* Generate the move name. */ if (n < 8) movename[0] = n + 65; else movename[0] = n + 66; if (*fmt == 'm') sprintf(movename+1, "%d", board_size - m); else sprintf(movename+1, "%-2d", board_size - m); fputs(movename, outputfile); } break; } case '1': fmt++; if (*fmt != 'm' && *fmt != 'M') { fprintf(outputfile, "\n\nUnknown format string '1%c'\n", *fmt); break; } else { char movename[4]; int pos = va_arg(ap, int); int m = I(pos); int n = J(pos); if (pos == NO_MOVE) fputs("PASS", outputfile); else if (!ON_BOARD1(pos)) fprintf(outputfile, "[%d]", pos); else { /* Generate the move name. */ if (n < 8) movename[0] = n + 65; else movename[0] = n + 66; if (*fmt == 'm') sprintf(movename + 1, "%d", board_size - m); else sprintf(movename + 1, "%-2d", board_size - m); fputs(movename, outputfile); } break; } case 'H': { unsigned long h = va_arg(ap, unsigned long); fprintf(outputfile, "%lx", h); break; } case 'C': { int color = va_arg(ap, int); fputs(color_to_string(color), outputfile); break; } default: fprintf(outputfile, "\n\nUnknown format character '%c'\n", *fmt); break; } } else putc(*fmt, outputfile); } } /* * required wrapper around vgprintf, writes to outfile. */ void gfprintf(FILE *outfile, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vgprintf(outfile, fmt, ap); va_end(ap); } /* * required wrapper around vgprintf, writes to stderr. * Always returns 1 to allow use in short-circuit logical expressions. */ int gprintf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vgprintf(stderr, fmt, ap); va_end(ap); return 1; } /* * required wrapper around vgprintf, in contrast to gprintf this one * writes to stdout. */ void mprintf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vgprintf(stdout, fmt, ap); va_end(ap); } /* This writes the move history information in sgf format to stderr. * This is only intended as a stand-alone debug tool for use in * abortgo(). Anywhere else you should use the normal sgf library. */ static void dump_board_sgf(void) { int pos; int initial_colors_found = EMPTY; int color; int k; for (pos = BOARDMIN; pos < BOARDMAX; pos++) if (ON_BOARD(pos)) initial_colors_found |= initial_board[pos]; fprintf(stderr, "(;GM[1]FF[4]SZ[%d]KM[%.1f]HA[%d]GN[GNU Go %s stepped on a bug]\n", board_size, komi, handicap, gg_version()); for (color = WHITE; color <= BLACK; color++) { if (initial_colors_found & color) { fprintf(stderr, "A%s", color == WHITE ? "W" : "B"); for (k = 0, pos = BOARDMIN; pos < BOARDMAX; pos++) { if (ON_BOARD(pos) && initial_board[pos] == color) { fprintf(stderr, "[%c%c]", 'a' + J(pos), 'a' + I(pos)); k++; if (k % 16 == 0) fprintf(stderr, "\n"); } } if (k % 16 != 0) fprintf(stderr, "\n"); } } if (move_history_pointer > 0) { for (k = 0; k < move_history_pointer; k++) { fprintf(stderr, ";%s", move_history_color[k] == WHITE ? "W" : "B"); if (move_history_pos[k] == PASS_MOVE) fprintf(stderr, "[]"); else fprintf(stderr, "[%c%c]", 'a' + J(move_history_pos[k]), 'a' + I(move_history_pos[k])); if (k % 12 == 11) fprintf(stderr, "\n"); } if (k % 12 != 0) fprintf(stderr, "\n"); } fprintf(stderr, ")\n"); } /* * A wrapper around abort() which shows the state variables at the time * of the problem. (pos) is typically a related move, or NO_MOVE. */ void abortgo(const char *file, int line, const char *msg, int pos) { gprintf("%o\n\n***assertion failure:\n%s:%d - %s near %1m***\n\n", file, line, msg, pos); dump_stack(); /* Print the board at the top of the stack. */ simple_showboard(stderr); fprintf(stderr, "\n"); dump_board_sgf(); fprintf(stderr, "gnugo %s (seed %d): You stepped on a bug.\n", gg_version(), get_random_seed()); if (board_size >= 9 && board_size <= 19) { fprintf(stderr, "\ Please mail this message, including the debug output above, \ to gnugo@gnu.org\n"); } fprintf(stderr, "\n"); fflush(stderr); fflush(stdout); abort(); /* cause core dump */ } static const char *color_names[] = { COLOR_NAMES }; /* Convert a color value to a string. */ const char * color_to_string(int color) { gg_assert(color < NUM_KOMASTER_STATES); return color_names[color]; } /* Convert a location to a string. */ const char * location_to_string(int pos) { static int init = 0; static char buf[BOARDSIZE][5]; if (!init) { int pos; for (pos = 0; pos < BOARDSIZE; pos++) location_to_buffer(pos, buf[pos]); init = 1; } ASSERT1(pos >= 0 && pos < BOARDSIZE, pos); return buf[pos]; } /* Convert a location to a string, writing to a buffer. */ void location_to_buffer(int pos, char *buf) { char *bufp = buf; int i = I(pos); int j = J(pos); if (pos == NO_MOVE) { strcpy(buf, "Pass"); return; } *bufp = 'A'+j; if (*bufp >= 'I') (*bufp)++; bufp++; i = board_size - i; if (i > 9) *bufp++ = '0' + i/10; *bufp++ = '0' + i%10; *bufp = 0; } /* * Convert the string str to a 1D coordinate. Return NO_MOVE if invalid * string. */ int string_to_location(int boardsize, const char *str) { int m, n; if (*str == '\0') return NO_MOVE; if (!isalpha((int) *str)) return NO_MOVE; n = tolower((int) *str) - 'a'; if (tolower((int) *str) >= 'i') --n; if (n < 0 || n > boardsize - 1) return NO_MOVE; if (!isdigit((int) *(str + 1))) return NO_MOVE; m = boardsize - atoi(str + 1); if (m < 0 || m > boardsize - 1) return NO_MOVE; return POS(m, n); } /* Some simple functions to draw an ASCII board. */ /* True if the coordinate is a hoshi point. */ int is_hoshi_point(int m, int n) { int hoshi; int middle; /* No hoshi points on these boards. */ if (board_size == 2 || board_size == 4) return 0; /* In the middle of a 3x3 board. */ if (board_size == 3) { if (m == 1 && n == 1) return 1; return 0; } if (board_size == 5) { if (m == 1 && (n == 1 || n == 3)) return 1; if (m == 2 && n == 2) return 1; if (m == 3 && (n == 1 || n == 3)) return 1; return 0; } /* 3-3 points are hoshi on sizes 7--11, 4-4 on larger. */ if (board_size <= 11) hoshi = 2; else hoshi = 3; /* Coordinate for midpoint. */ middle = board_size/2; /* Normalize the coordinates by mirroring to the lower numbers. */ if (m >= middle) m = board_size - 1 - m; if (n >= middle) n = board_size - 1 - n; /* Is this a corner hoshi? */ if (m == hoshi && n == hoshi) return 1; /* If even sized board, only hoshi points in the corner. */ if (board_size%2 == 0) return 0; /* Less than 12 in board size only middle point. */ if (board_size < 12) { if (m == middle && n == middle) return 1; return 0; } /* Is this a midpoint hoshi? */ if ((m == hoshi || m == middle) && (n == hoshi || n == middle)) return 1; /* No more chances. */ return 0; } /* Print a line with coordinate letters above the board. */ void draw_letter_coordinates(FILE *outfile) { int i; int ch; fprintf(outfile, " "); for (i = 0, ch = 'A'; i < board_size; i++, ch++) { if (ch == 'I') ch++; fprintf(outfile, " %c", ch); } } /* Bare bones version of showboard(0). No fancy options, no hint of * color, and you can choose where to write it. */ void simple_showboard(FILE *outfile) { int i, j; draw_letter_coordinates(outfile); for (i = 0; i < board_size; i++) { fprintf(outfile, "\n%2d", board_size - i); for (j = 0; j < board_size; j++) { if (BOARD(i, j) == EMPTY) fprintf(outfile, " %c", is_hoshi_point(i, j) ? '+' : '.'); else fprintf(outfile, " %c", BOARD(i, j) == BLACK ? 'X' : 'O'); } fprintf(outfile, " %d", board_size - i); if ((board_size < 10 && i == board_size-2) || (board_size >= 10 && i == 8)) fprintf(outfile, " WHITE (O) has captured %d stones", black_captured); if ((board_size < 10 && i == board_size-1) || (board_size >= 10 && i == 9)) fprintf(outfile, " BLACK (X) has captured %d stones", white_captured); } fprintf(outfile, "\n"); draw_letter_coordinates(outfile); } /* Adds square marks for each goal intersecion in the current sgf_dumptree. * This function cannot be in sgf/ as it has to understand the 1-D board. */ void mark_goal_in_sgf(signed char goal[BOARDMAX]) { int pos; SGFNode *node; if (!sgf_dumptree) return; node = sgftreeNodeCheck(sgf_dumptree); for (pos = BOARDMIN; pos < BOARDMAX; pos++) if (ON_BOARD(pos) && goal[pos]) sgfSquare(node, I(pos), J(pos)); } /* * Local Variables: * tab-width: 8 * c-basic-offset: 2 * End: */