/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * 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. * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Convert joseki from sgf format to patterns.db format. */ #include "board.h" #include #include #include #include #define USAGE "\ Usage : joseki prefix filename\n\ " /* Joseki move types. */ #define STANDARD 0 #define URGENT 1 #define MINOR 2 #define TRICK 3 #define ANTISUJI 4 #define TENUKI_OK 5 /* We don't want to play moves on edges of board which might have been * cropped, since there might appear an accidential capture. */ #define SAFE_ON_BOARD(i, j) ((i) >= 0 && (j) >= 0\ && (i) < MAX_BOARD - 1 && (j) < MAX_BOARD - 1) static int boardsize; /* Identify the type of joseki move. * FIXME: We might want the relax the requirement that this info comes * as the very first character. */ static int identify_move_type(char *text) { if (!text) return STANDARD; switch ((int) *text) { case 'u': case 'U': return URGENT; break; case 'J': case 'S': return STANDARD; break; case 'j': case 's': return MINOR; break; case 'T': return TRICK; break; case 't': return TENUKI_OK; break; case '0': case 'a': case 'A': return ANTISUJI; break; } return STANDARD; } /* Copy the lines starting with a certain character to stdout. */ static void write_selected_lines(char *text, char start_char) { char *p; if (!text) return; while (1) { p = strchr(text, '\n'); if (p) *p = 0; if (*text == start_char) printf("%s\n", text); if (p) { *p = '\n'; text = p+1; } else break; } } /* Is there any line starting with a certain character? */ static int selected_line_exists(char *text, char start_char) { char *p; if (!text) return 0; while (1) { if (*text == start_char) return 1; p = strchr(text, '\n'); if (p) text = p+1; else break; } return 0; } /* Write the main diagram or the constraint diagram. In the former * case, pass a NULL pointer for labels. */ static void write_diagram(int movei, int movej, int color, int marki, int markj, char labels[MAX_BOARD][MAX_BOARD]) { int i, j; for (i = -1; i <= marki; i++) { for (j = markj; j >= 0; j--) { if (i == -1) printf("-"); else if (labels && labels[i][j]) printf("%c", labels[i][j]); else if (i == movei && j == movej) printf("*"); else if (BOARD(i, j) == color) printf("O"); else if (BOARD(i, j) == OTHER_COLOR(color)) printf("X"); else printf("."); } if (i == -1) printf("+\n"); else printf("|\n"); } } /* Write the colon line of the pattern. */ static void write_colon_line(int move_type, char symmetry, char *text) { char *p; /* Locate a possible colon line in the sgf file comment. */ if (!text) p = NULL; else if (*text == ':') p = text + 1; else { p = strstr(text, "\n:"); if (p) p += 2; } printf(":%c,sF", symmetry); switch (move_type) { case URGENT: printf("U"); break; case STANDARD: printf("J"); break; case MINOR: printf("j"); break; case TRICK: printf("T"); break; case TENUKI_OK: printf("t"); break; case ANTISUJI: printf("N"); break; } if (p) { /* A little trick to guess whether the supplied colon line in the * sgf file begins with a classification. */ if (strchr(p, '(') && (!strchr(p, ',') || strchr(p, ',') > strchr(p, '('))) printf(","); while (*p != 0 && *p != '\n') fputc(*(p++), stdout); } printf("\n"); } /* Check if the board and labels are symmetric. */ static int board_is_symmetric(int n, char labels[MAX_BOARD][MAX_BOARD]) { int i; int j; for (i = 0; i <= n; i++) { for (j = 0; j < i; j++) { if (BOARD(i, j) != BOARD(j, i) || (labels && labels[i][j] != labels[j][i])) return 0; } } return 1; } /* Write a pattern to stdout. */ static void make_pattern(int movei, int movej, int color, int marki, int markj, int multiple_marks, char labels[MAX_BOARD][MAX_BOARD], char *text, const char *prefix) { static int pattern_number = 0; int move_type; char symmetry = '8'; pattern_number++; move_type = identify_move_type(text); printf("Pattern %s%d\n", prefix, pattern_number); /* Write comments. */ write_selected_lines(text, '#'); printf("\n"); /* Write the main diagram. */ write_diagram(movei, movej, color, marki, markj, NULL); printf("\n"); /* Write the colon line. */ if (movei == movej && marki == markj && board_is_symmetric(marki, labels)) symmetry = '/'; write_colon_line(move_type, symmetry, text); printf("\n"); /* Write the constraint diagram if there are any labels, a * constraint line, or an action line. */ if (labels || selected_line_exists(text, ';') || selected_line_exists(text, '>')) { write_diagram(movei, movej, color, marki, markj, labels); printf("\n"); /* Write constraint and action lines. */ write_selected_lines(text, ';'); write_selected_lines(text, '>'); printf("\n"); } printf("\n"); /* Basic sanity checking. We do this at the end to simplify debugging. */ if (multiple_marks) fprintf(stderr, "Warning: Multiple square marks in pattern %s%d\n", prefix, pattern_number); if (is_suicide(POS(movei, movej), color)) { fprintf(stderr, "Error: Illegal move in pattern %s%d\n", prefix, pattern_number); exit(EXIT_FAILURE); } } /* Analyze the node properties in order to make a pattern. Then make * recursive calls for child node and siblings. */ static void analyze_node(SGFNode *node, const char *prefix) { SGFProperty *prop; int i, j; char labels[MAX_BOARD][MAX_BOARD]; int label_found = 0; int movei = -1; int movej = -1; int color = EMPTY; int marki = -1; int markj = -1; int multiple_marks = 0; char *comment = NULL; /* Clear the labels array. */ memset(labels, 0, MAX_BOARD * MAX_BOARD); /* Check the node properties for a move, a square mark, labels, and * a comment. */ for (prop = node->props; prop; prop = prop->next) { switch (prop->name) { case SGFSQ: /* Square */ case SGFMA: /* Mark */ if (marki != -1) multiple_marks = 1; else { get_moveXY(prop, &marki, &markj, boardsize); markj = boardsize - 1 - markj; } break; case SGFW: /* White move */ color = WHITE; get_moveXY(prop, &movei, &movej, boardsize); movej = boardsize - 1 - movej; break; case SGFB: /* Black move */ color = BLACK; get_moveXY(prop, &movei, &movej, boardsize); movej = boardsize - 1 - movej; break; case SGFLB: /* Label, with value like "mh:A" */ get_moveXY(prop, &i, &j, boardsize); j = boardsize - 1 - j; gg_assert(prop->value[2] == ':'); if (ON_BOARD2(i, j)) { labels[i][j] = prop->value[3]; label_found = 1; } break; case SGFC: /* Comment */ comment = prop->value; break; } } /* If we have a move and a square mark, produce a pattern. */ if (SAFE_ON_BOARD(movei, movej) && ON_BOARD2(marki, markj)) make_pattern(movei, movej, color, marki, markj, multiple_marks, (label_found ? labels : NULL), comment, prefix); /* Traverse child, if any. */ if (node->child) { if (SAFE_ON_BOARD(movei, movej)) tryko(POS(movei, movej), color, NULL); analyze_node(node->child, prefix); if (SAFE_ON_BOARD(movei, movej)) popgo(); } /* Traverse sibling, if any. */ if (node->next) analyze_node(node->next, prefix); } int main(int argc, char *argv[]) { const char *filename; const char *prefix; SGFNode *sgf; /* Check number of arguments. */ if (argc != 3) { fprintf(stderr, USAGE); exit(EXIT_FAILURE); } prefix = argv[1]; filename = argv[2]; /* Read the sgf file into a tree in memory. */ sgf = readsgffile(filename); if (!sgf) { fprintf(stderr, "%s: Couldn't open sgf file %s.\n", argv[0], filename); exit(EXIT_FAILURE); } #define PREAMBLE "\ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\n\ # This is GNU Go, a Go program. Contact gnugo@gnu.org, or see #\n\ # http://www.gnu.org/software/gnugo/ for more information. #\n\ # #\n\ # Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, #\n\ # 2008 and 2009 by the Free Software Foundation. #\n\ # #\n\ # This program is free software; you can redistribute it and/or #\n\ # modify it under the terms of the GNU General Public License as #\n\ # published by the Free Software Foundation - version 3 or #\n\ # (at your option) any later version. #\n\ # #\n\ # This program is distributed in the hope that it will be useful, #\n\ # but WITHOUT ANY WARRANTY; without even the implied warranty of #\n\ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #\n\ # GNU General Public License in file COPYING for more details. #\n\ # #\n\ # You should have received a copy of the GNU General Public #\n\ # License along with this program; if not, write to the Free #\n\ # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, #\n\ # Boston, MA 02111, USA. #\n\ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\n\ # This file is automatically generated by joseki. Do not edit #\n\ # it directly. Instead, edit the corresponding sgf file. #\n\ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\n\ \n\n\ " printf(PREAMBLE); printf("attribute_map general\n\n"); /* Call the engine to setup and clear the board. */ board_size = MAX_BOARD; clear_board(); /* Determine board size of the file. */ if (!sgfGetIntProperty(sgf, "SZ", &boardsize)) { fprintf(stderr, "joseki: error: can't determine file board size\n"); return 1; } /* Walk through the tree and make patterns. */ analyze_node(sgf, prefix); return 0; } /* * Local Variables: * tab-width: 8 * c-basic-offset: 2 * End: */