Initial commit of GNU Go v3.8.
[sgk-go] / patterns / joseki.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:
*/