Updated README: Equal sign not required with `--mode` flag.
[sgk-go] / patterns / joseki.c
CommitLineData
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/* Convert joseki from sgf format to patterns.db format. */
25
26#include "board.h"
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <ctype.h>
32
33#define USAGE "\
34Usage : joseki prefix filename\n\
35"
36
37/* Joseki move types. */
38#define STANDARD 0
39#define URGENT 1
40#define MINOR 2
41#define TRICK 3
42#define ANTISUJI 4
43#define TENUKI_OK 5
44
45
46/* We don't want to play moves on edges of board which might have been
47 * cropped, since there might appear an accidential capture.
48 */
49#define SAFE_ON_BOARD(i, j) ((i) >= 0 && (j) >= 0\
50 && (i) < MAX_BOARD - 1 && (j) < MAX_BOARD - 1)
51
52static int boardsize;
53
54
55/* Identify the type of joseki move.
56 * FIXME: We might want the relax the requirement that this info comes
57 * as the very first character.
58 */
59static int
60identify_move_type(char *text)
61{
62 if (!text)
63 return STANDARD;
64
65 switch ((int) *text) {
66 case 'u':
67 case 'U':
68 return URGENT;
69 break;
70 case 'J':
71 case 'S':
72 return STANDARD;
73 break;
74 case 'j':
75 case 's':
76 return MINOR;
77 break;
78 case 'T':
79 return TRICK;
80 break;
81 case 't':
82 return TENUKI_OK;
83 break;
84 case '0':
85 case 'a':
86 case 'A':
87 return ANTISUJI;
88 break;
89 }
90
91 return STANDARD;
92}
93
94/* Copy the lines starting with a certain character to stdout. */
95static void
96write_selected_lines(char *text, char start_char)
97{
98 char *p;
99 if (!text)
100 return;
101 while (1) {
102 p = strchr(text, '\n');
103 if (p)
104 *p = 0;
105 if (*text == start_char)
106 printf("%s\n", text);
107 if (p) {
108 *p = '\n';
109 text = p+1;
110 }
111 else
112 break;
113 }
114}
115
116/* Is there any line starting with a certain character? */
117static int
118selected_line_exists(char *text, char start_char)
119{
120 char *p;
121 if (!text)
122 return 0;
123 while (1) {
124 if (*text == start_char)
125 return 1;
126 p = strchr(text, '\n');
127 if (p)
128 text = p+1;
129 else
130 break;
131 }
132 return 0;
133}
134
135/* Write the main diagram or the constraint diagram. In the former
136 * case, pass a NULL pointer for labels.
137 */
138static void
139write_diagram(int movei, int movej, int color, int marki, int markj,
140 char labels[MAX_BOARD][MAX_BOARD])
141{
142 int i, j;
143
144 for (i = -1; i <= marki; i++) {
145 for (j = markj; j >= 0; j--) {
146 if (i == -1)
147 printf("-");
148 else if (labels && labels[i][j])
149 printf("%c", labels[i][j]);
150 else if (i == movei && j == movej)
151 printf("*");
152 else if (BOARD(i, j) == color)
153 printf("O");
154 else if (BOARD(i, j) == OTHER_COLOR(color))
155 printf("X");
156 else
157 printf(".");
158 }
159 if (i == -1)
160 printf("+\n");
161 else
162 printf("|\n");
163 }
164}
165
166/* Write the colon line of the pattern. */
167static void
168write_colon_line(int move_type, char symmetry, char *text)
169{
170 char *p;
171
172 /* Locate a possible colon line in the sgf file comment. */
173 if (!text)
174 p = NULL;
175 else if (*text == ':')
176 p = text + 1;
177 else {
178 p = strstr(text, "\n:");
179 if (p)
180 p += 2;
181 }
182
183 printf(":%c,sF", symmetry);
184 switch (move_type) {
185 case URGENT:
186 printf("U");
187 break;
188 case STANDARD:
189 printf("J");
190 break;
191 case MINOR:
192 printf("j");
193 break;
194 case TRICK:
195 printf("T");
196 break;
197 case TENUKI_OK:
198 printf("t");
199 break;
200 case ANTISUJI:
201 printf("N");
202 break;
203 }
204
205 if (p) {
206 /* A little trick to guess whether the supplied colon line in the
207 * sgf file begins with a classification.
208 */
209 if (strchr(p, '(')
210 && (!strchr(p, ',') || strchr(p, ',') > strchr(p, '(')))
211 printf(",");
212 while (*p != 0 && *p != '\n')
213 fputc(*(p++), stdout);
214 }
215 printf("\n");
216}
217
218
219/* Check if the board and labels are symmetric. */
220static int
221board_is_symmetric(int n, char labels[MAX_BOARD][MAX_BOARD])
222{
223 int i;
224 int j;
225
226 for (i = 0; i <= n; i++) {
227 for (j = 0; j < i; j++) {
228 if (BOARD(i, j) != BOARD(j, i)
229 || (labels && labels[i][j] != labels[j][i]))
230 return 0;
231 }
232 }
233
234 return 1;
235}
236
237/* Write a pattern to stdout. */
238static void
239make_pattern(int movei, int movej, int color,
240 int marki, int markj, int multiple_marks,
241 char labels[MAX_BOARD][MAX_BOARD], char *text,
242 const char *prefix)
243{
244 static int pattern_number = 0;
245 int move_type;
246 char symmetry = '8';
247
248 pattern_number++;
249 move_type = identify_move_type(text);
250
251 printf("Pattern %s%d\n", prefix, pattern_number);
252
253 /* Write comments. */
254 write_selected_lines(text, '#');
255 printf("\n");
256
257 /* Write the main diagram. */
258 write_diagram(movei, movej, color, marki, markj, NULL);
259 printf("\n");
260
261 /* Write the colon line. */
262 if (movei == movej && marki == markj && board_is_symmetric(marki, labels))
263 symmetry = '/';
264 write_colon_line(move_type, symmetry, text);
265 printf("\n");
266
267 /* Write the constraint diagram if there are any labels, a
268 * constraint line, or an action line.
269 */
270 if (labels
271 || selected_line_exists(text, ';')
272 || selected_line_exists(text, '>')) {
273 write_diagram(movei, movej, color, marki, markj, labels);
274
275 printf("\n");
276
277 /* Write constraint and action lines. */
278 write_selected_lines(text, ';');
279 write_selected_lines(text, '>');
280 printf("\n");
281 }
282
283 printf("\n");
284
285 /* Basic sanity checking. We do this at the end to simplify debugging. */
286 if (multiple_marks)
287 fprintf(stderr, "Warning: Multiple square marks in pattern %s%d\n",
288 prefix, pattern_number);
289
290 if (is_suicide(POS(movei, movej), color)) {
291 fprintf(stderr, "Error: Illegal move in pattern %s%d\n",
292 prefix, pattern_number);
293 exit(EXIT_FAILURE);
294 }
295}
296
297
298/* Analyze the node properties in order to make a pattern. Then make
299 * recursive calls for child node and siblings.
300 */
301static void
302analyze_node(SGFNode *node, const char *prefix)
303{
304 SGFProperty *prop;
305 int i, j;
306 char labels[MAX_BOARD][MAX_BOARD];
307 int label_found = 0;
308 int movei = -1;
309 int movej = -1;
310 int color = EMPTY;
311 int marki = -1;
312 int markj = -1;
313 int multiple_marks = 0;
314 char *comment = NULL;
315
316 /* Clear the labels array. */
317 memset(labels, 0, MAX_BOARD * MAX_BOARD);
318
319 /* Check the node properties for a move, a square mark, labels, and
320 * a comment.
321 */
322 for (prop = node->props; prop; prop = prop->next) {
323 switch (prop->name) {
324 case SGFSQ: /* Square */
325 case SGFMA: /* Mark */
326 if (marki != -1)
327 multiple_marks = 1;
328 else {
329 get_moveXY(prop, &marki, &markj, boardsize);
330 markj = boardsize - 1 - markj;
331 }
332 break;
333
334 case SGFW: /* White move */
335 color = WHITE;
336 get_moveXY(prop, &movei, &movej, boardsize);
337 movej = boardsize - 1 - movej;
338 break;
339
340 case SGFB: /* Black move */
341 color = BLACK;
342 get_moveXY(prop, &movei, &movej, boardsize);
343 movej = boardsize - 1 - movej;
344 break;
345
346 case SGFLB: /* Label, with value like "mh:A" */
347 get_moveXY(prop, &i, &j, boardsize);
348 j = boardsize - 1 - j;
349 gg_assert(prop->value[2] == ':');
350 if (ON_BOARD2(i, j)) {
351 labels[i][j] = prop->value[3];
352 label_found = 1;
353 }
354 break;
355
356 case SGFC: /* Comment */
357 comment = prop->value;
358 break;
359 }
360 }
361
362 /* If we have a move and a square mark, produce a pattern. */
363 if (SAFE_ON_BOARD(movei, movej) && ON_BOARD2(marki, markj))
364 make_pattern(movei, movej, color, marki, markj, multiple_marks,
365 (label_found ? labels : NULL), comment, prefix);
366
367 /* Traverse child, if any. */
368 if (node->child) {
369 if (SAFE_ON_BOARD(movei, movej))
370 tryko(POS(movei, movej), color, NULL);
371 analyze_node(node->child, prefix);
372 if (SAFE_ON_BOARD(movei, movej))
373 popgo();
374 }
375
376 /* Traverse sibling, if any. */
377 if (node->next)
378 analyze_node(node->next, prefix);
379}
380
381
382int
383main(int argc, char *argv[])
384{
385 const char *filename;
386 const char *prefix;
387 SGFNode *sgf;
388
389 /* Check number of arguments. */
390 if (argc != 3) {
391 fprintf(stderr, USAGE);
392 exit(EXIT_FAILURE);
393 }
394
395 prefix = argv[1];
396 filename = argv[2];
397
398 /* Read the sgf file into a tree in memory. */
399 sgf = readsgffile(filename);
400 if (!sgf) {
401 fprintf(stderr, "%s: Couldn't open sgf file %s.\n", argv[0], filename);
402 exit(EXIT_FAILURE);
403 }
404
405#define PREAMBLE "\
406# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\n\
407# This is GNU Go, a Go program. Contact gnugo@gnu.org, or see #\n\
408# http://www.gnu.org/software/gnugo/ for more information. #\n\
409# #\n\
410# Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, #\n\
411# 2008 and 2009 by the Free Software Foundation. #\n\
412# #\n\
413# This program is free software; you can redistribute it and/or #\n\
414# modify it under the terms of the GNU General Public License as #\n\
415# published by the Free Software Foundation - version 3 or #\n\
416# (at your option) any later version. #\n\
417# #\n\
418# This program is distributed in the hope that it will be useful, #\n\
419# but WITHOUT ANY WARRANTY; without even the implied warranty of #\n\
420# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #\n\
421# GNU General Public License in file COPYING for more details. #\n\
422# #\n\
423# You should have received a copy of the GNU General Public #\n\
424# License along with this program; if not, write to the Free #\n\
425# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, #\n\
426# Boston, MA 02111, USA. #\n\
427# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\n\
428# This file is automatically generated by joseki. Do not edit #\n\
429# it directly. Instead, edit the corresponding sgf file. #\n\
430# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\n\
431\n\n\
432"
433
434 printf(PREAMBLE);
435 printf("attribute_map general\n\n");
436
437 /* Call the engine to setup and clear the board. */
438 board_size = MAX_BOARD;
439 clear_board();
440
441 /* Determine board size of the file. */
442 if (!sgfGetIntProperty(sgf, "SZ", &boardsize)) {
443 fprintf(stderr, "joseki: error: can't determine file board size\n");
444 return 1;
445 }
446
447 /* Walk through the tree and make patterns. */
448 analyze_node(sgf, prefix);
449
450 return 0;
451}
452
453
454/*
455 * Local Variables:
456 * tab-width: 8
457 * c-basic-offset: 2
458 * End:
459 */