forked from db3108/michi-c2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sgf.c
415 lines (373 loc) · 11.7 KB
/
sgf.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
// sgf.c -- Read and write SGF files for the michi-c gtp engine
#include "michi.h"
static char buf[128];
FILE *f; // source of the sgf data
Game *game; // game in which the sgf file will be written
int nmoves; // number of moves loaded from the sgf file
// ---------------------- Update of the Game struct ---------------------------
Game *new_game(Position *pos)
{
Game *game=michi_calloc(1, sizeof(Game));
game->pos = pos;
game_clear_board(game);
return game;
}
void free_game(Game *game)
{
free(game->pos);
free(game);
}
char* game_clear_board(Game *game)
{
game->handicap = 0;
slist_clear(game->moves);
slist_clear(game->placed_black_stones);
slist_clear(game->placed_white_stones);
return empty_position(game->pos);
}
int is_game_board_empty(Game *game)
{
if (slist_size(game->moves) != 0
|| slist_size(game->placed_black_stones) != 0
|| slist_size(game->placed_white_stones) != 0)
return 0;
else
return 1;
}
char* do_play(Game *game, Color c, Point pt)
// Play the move (updating the Game struct)
{
char *ret;
Color to_play_before;
int played=0;
Info m = 0;
Position *pos = game->pos;
to_play_before = board_color_to_play(pos);
board_set_color_to_play(pos, c);
if (point_color(pos,pt) == EMPTY) {
ret = play_move(pos, pt);
if (ret[0] == 0) {
m = pt + (board_captured_neighbors(pos) << 9)
+ (board_ko_old(pos) << 13)
+ ((to_play_before) << 22);
played = 1;
}
// else illegal move: nothing played
}
else if(pt == PASS_MOVE) {
ret = pass_move(pos);
m = pt + (board_captured_neighbors(pos) << 9)
+ (board_ko_old(pos) << 13)
+ ((to_play_before) << 22);
played = 1;
}
else ret ="Error Illegal move: point not EMPTY\n";
if (played) {
c2++;
slist_push(game->moves, m);
}
return ret;
}
// ------------------------ SGF Recursive Parser ------------------------------
// This file implement a Recursive Descent Parser for the SGF grammar
// References :
// [1] Aho, Lam, Sethi, Ullman. Compilers. 2nd Edition (The Dragon book)
// [2] http://www.red-bean.com/sgf/sgf4.html#2 (SGF)
// The SGF grammar has been adapted from [2] to make it Right Recursive
//
// The recognized grammar is the following :
// Collection = GameTree RestOfCollection
// RestOfCollection = GameTree RestOfCollection | None
// GameTree = "(" Sequence RestOfCollection ")"
// Sequence = Node RestOfSequence
// RestOfSequence = Node RestOfSequence | None
// Node = ";" Property { Property }
// Property = PropIdent { PropValue }
// PropIdent = see prop_name[] array below
// PropValue = "[" ValueType "]"
// ValueType = Point | None
// The code is simple. There is a function for each non terminal symbol
// of the grammar with the same name of the non terminal.
// The terminal symbols are handled by the lexical analyser yylex()
// Note: the code may look very inefficient at first sight with a lot of
// recursive functions. But it is not. Any current optimizing compiler
// (gcc, icc or Microsoft Visual Studio) is able to eliminate easily this
// "tail recursion".
// In addition, parsing sgf file is certainly not the most consuming task
// that michi must accomplish
int prop; // current property
int lineno=1; // current line of input
int symbol; // lookahead symbol
char yytext[8192]; // the text of the current token
int yyleng; // length of the string representation of the current token
int yyval; // value of the current token
#define UPPERCASE_STRING 256
#define LOWERCASE_STRING 257
#define STRING 258
#define POINT 260
#define NUMBER 261
#define REAL 262
char* prop_name[] = { "", "AB", "AW", "B", "W", "C",
"FF", "AP", "CA", "SZ", "KM", "DT",
"PL", "HA", "MULTIGOGM",
"N", "AE", "GM", "GN", "US", "GW",
"GB", "DM", "UC", "TE", "BM", "DO",
"IT",
NULL };
#define NOT_FOUND 0
int find(char *string, char *strlist[])
// Return 0 if string is not found in strlist, i != 0 if it is found at pos i
{
for (int i=1 ; strlist[i] != NULL ; i++) {
if (strcmp(string, strlist[i]) == 0)
return i;
}
return 0; // Not Found
}
int yylex(void)
{
int c, all_upper=1, all_lower=1, number=0, real=0;
while(isspace(c=fgetc(f)))
if (c == '\n') lineno++;
if (!isalnum(c) && c != '+' && c != '-')
return c;
else {
yyleng = 0;
yytext[yyleng++] = c;
if (isdigit(c) || c == '+' || c == '-') {
all_lower = 0;
all_upper = 0;
number = 1;
while ((c=fgetc(f)) != ']' && c != '[') {
if (!isdigit(c)) {
if (c == '.') {
real = 1;
}
else {
number = 0;
real = 0;
}
}
yytext[yyleng++] = c;
}
}
else {
while ((c=fgetc(f)) != ']' && c != '[') {
all_lower = all_lower && islower(c);
all_upper = all_upper && isupper(c);
yytext[yyleng++] = c;
}
}
ungetc(c, f);
yytext[yyleng] = 0;
if (real) return REAL;
if (number) return NUMBER;
if (all_upper) {
yyval = find(yytext, prop_name);
return UPPERCASE_STRING;
}
else if (all_lower) {
if (yyleng == 2)
return POINT;
else
return LOWERCASE_STRING;
}
return STRING;
}
}
char* token_name(int tok)
{
if (tok < 256) {
buf[0] = tok; buf[1] = 0;
}
else if (tok == POINT)
sprintf(buf,"POINT");
else if (tok == STRING)
sprintf(buf,"STRING");
else if (tok == UPPERCASE_STRING)
sprintf(buf,"UPPERCASE_STRING");
else if (tok == LOWERCASE_STRING)
sprintf(buf,"LOWERCASE_STRING");
else
sprintf(buf,"token not known");
return buf;
}
void yyaccept(int tok)
{
if (symbol == tok) symbol = yylex();
else {
fprintf(stderr, "line %d: Syntax Error\n", lineno);
fprintf(stderr, "expected symbol: \"%s\"", token_name(tok));
fprintf(stderr, ", read \"%s\"\n",token_name(symbol));
}
//printf("%s", token_name(symbol));
//if (symbol == UPPERCASE_STRING) printf(": %s", yytext);
//printf("\n");
}
void ValueType(void)
// ValueType = Point | Number | Real | Text | None
{
if (symbol == POINT || symbol == STRING || symbol == UPPERCASE_STRING
|| symbol == NUMBER || symbol == REAL) {
yyaccept(symbol);
//fprintf(stderr, "%s ", yytext);
}
}
void PropValue(void)
// PropValue = "[" ValueType "]"
{
yyaccept('['); ValueType();
// Property Value was read : use it to modify the game
int size = board_size(game->pos);
if (board_nmoves(game->pos) < nmoves-1
&& strcmp(prop_name[prop], "B") == 0) {
if (yytext[0] != 'B')
do_play(game, BLACK, parse_sgf_coord(yytext, size));
else
do_play(game, BLACK, PASS_MOVE);
}
else if (board_nmoves(game->pos) < nmoves-1
&& strcmp(prop_name[prop], "W") == 0) {
if (yytext[0] != 'W')
do_play(game, WHITE, parse_sgf_coord(yytext, size));
else
do_play(game, WHITE, PASS_MOVE);
}
else if (strcmp(prop_name[prop], "AB") == 0) {
Point pt = parse_sgf_coord(yytext, size);
slist_push(game->placed_black_stones, pt);
board_place_stone(game->pos, pt, BLACK);
}
else if (strcmp(prop_name[prop], "AW") == 0) {
Point pt = parse_sgf_coord(yytext, size);
slist_push(game->placed_white_stones, pt);
board_place_stone(game->pos, pt, WHITE);
}
else if (strcmp(prop_name[prop], "KM") == 0)
board_set_komi(game->pos, atof(yytext));
else if (strcmp(prop_name[prop], "SZ") == 0) {
if (is_game_board_empty(game)) {
board_set_size(game->pos, atoi(yytext));
game_clear_board(game);
}
else {
// Can happen if SZ occurs after AB or AW
Point bstones[BOARDSIZE], wstones[BOARDSIZE];
Position *pos=game->pos;
slist_clear(bstones); slist_clear(wstones);
slist_append(bstones, game->placed_black_stones);
slist_append(wstones, game->placed_white_stones);
board_set_size(game->pos, atoi(yytext));
game_clear_board(game);
FORALL_IN_SLIST(bstones, pt) {
board_set_color_to_play(pos, BLACK);
play_move(pos, pt);
slist_push(game->placed_black_stones, pt);
}
FORALL_IN_SLIST(wstones, pt) {
board_set_color_to_play(pos, WHITE);
play_move(pos, pt);
slist_push(game->placed_white_stones, pt);
}
}
}
else if (strcmp(prop_name[prop], "PL") == 0) {
if (strcmp(yytext, "B") == 0)
board_set_color_to_play(game->pos, BLACK);
else
board_set_color_to_play(game->pos, WHITE);
}
yyaccept(']');
}
void PropValues(void)
// Propvalues = { PropValue }
{
if (symbol == '[') {
PropValue(); PropValues();
}
}
void PropIdent(void)
{
if (symbol == UPPERCASE_STRING) {
prop = yyval;
//if (yyval > 0 )
// fprintf(stderr, "prop %s: ", yytext);
//else
// fprintf(stderr, "unknown %s: ", yytext);
yyaccept(UPPERCASE_STRING);
}
}
void Property(void)
// Property = PropIdent { PropValue }
{
PropIdent(); PropValue(); PropValues();
//fprintf(stderr, "\n");
}
void Properties(void)
// Properties = { Property }
{
if (symbol == UPPERCASE_STRING) {
Property(); Properties();
}
}
void Node(void)
// Node = ";" Properties
{
yyaccept(';'); Properties();
}
void RestOfSequence(void)
// RestOfSequence = Node RestOfSequence | None
{
if (symbol == ';') {
Node(); RestOfSequence();
}
}
void Sequence(void)
// Sequence = Node RestOfSequence
{
Node(); RestOfSequence();
}
void RestOfCollection(void);
void GameTree(void)
// GameTree = "(" Sequence RestOfCollection ")"
{
yyaccept('('); Sequence(); RestOfCollection(); yyaccept(')');
}
void RestOfCollection(void)
// RestOfCollection = GameTree RestOfCollection | None
{
if (symbol == '(') {
GameTree(); RestOfCollection();
}
}
void Collection(void)
// Collection = GameTree RestOfCollection
{
GameTree(); RestOfCollection();
}
char *loadsgf(Game *sgf_game, char *filename, int sgf_nmoves)
// Load a position from the SGF file filename using a recursive parser for the
// SGF grammar which is described at the top of the current file.
// See ref [1].
//
// The modifications of the game struct is done in ProPValue()
{
f = fopen(filename, "r"); // File shared between all the routines
game = sgf_game; // Game in which all the actions take place
nmoves = sgf_nmoves; // Number of moves loaded from sgf file
game_clear_board(game);
if (f != NULL) {
symbol = yylex();
Collection();
assert(symbol == '\n' || symbol == EOF);
if (board_color_to_play(game->pos) == BLACK)
strcpy(buf,"B");
else
strcpy(buf,"W");
fclose(f);
}
else {
sprintf(buf, "Error - can't open file %s", filename);
}
return buf;
}