460 lines
12 KiB
C
460 lines
12 KiB
C
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#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:
|
|
*/
|