|
1 /* |
|
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
|
3 * |
|
4 * Copyright 2014 Mike Becker. All rights reserved. |
|
5 * |
|
6 * Redistribution and use in source and binary forms, with or without |
|
7 * modification, are permitted provided that the following conditions are met: |
|
8 * |
|
9 * 1. Redistributions of source code must retain the above copyright |
|
10 * notice, this list of conditions and the following disclaimer. |
|
11 * |
|
12 * 2. Redistributions in binary form must reproduce the above copyright |
|
13 * notice, this list of conditions and the following disclaimer in the |
|
14 * documentation and/or other materials provided with the distribution. |
|
15 * |
|
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
26 * POSSIBILITY OF SUCH DAMAGE. |
|
27 * |
|
28 */ |
|
29 |
|
30 #include "pgn.h" |
|
31 #include <ctype.h> |
|
32 #include <stdlib.h> |
|
33 #include <string.h> |
|
34 |
|
35 int read_pgn(FILE* stream, GameState *gamestate, GameInfo *gameinfo) { |
|
36 int c, i; |
|
37 |
|
38 char result[8]; |
|
39 |
|
40 char tagkey[32]; |
|
41 char tagvalue[128]; |
|
42 |
|
43 // read tag pairs |
|
44 _Bool readmoves = 0; |
|
45 while (!readmoves) { |
|
46 while (isspace(c = fgetc(stream))); |
|
47 if (c == '1') { |
|
48 readmoves = 1; |
|
49 break; |
|
50 } |
|
51 if (c != '[') { |
|
52 return EXIT_FAILURE; |
|
53 } |
|
54 while (isspace(c = fgetc(stream))); |
|
55 i = 0; |
|
56 do { |
|
57 tagkey[i++] = c; |
|
58 } while (!isspace(c = fgetc(stream))); |
|
59 tagkey[i] = '\0'; |
|
60 while (isspace(c = fgetc(stream))); |
|
61 if (c != '"') { |
|
62 return EXIT_FAILURE; |
|
63 } |
|
64 i = 0; |
|
65 while ((c = fgetc(stream)) != '"') { |
|
66 if (c == '\n') { |
|
67 return EXIT_FAILURE; |
|
68 } |
|
69 tagvalue[i++] = c; |
|
70 } |
|
71 tagvalue[i] = '\0'; |
|
72 if (fgetc(stream) != ']') { |
|
73 return EXIT_FAILURE; |
|
74 } |
|
75 |
|
76 if (strcmp("Result", tagkey) == 0) { |
|
77 memcpy(result, tagvalue, 8); |
|
78 } |
|
79 } |
|
80 |
|
81 // read moves |
|
82 if (fgetc(stream) != '.') { |
|
83 return EXIT_FAILURE; |
|
84 } |
|
85 |
|
86 char movestr[10]; |
|
87 Move move; |
|
88 uint8_t curcol = WHITE; |
|
89 |
|
90 while (readmoves) { |
|
91 // move |
|
92 while (isspace(c = fgetc(stream))); |
|
93 i = 0; |
|
94 do { |
|
95 movestr[i++] = c; |
|
96 if (i >= 10) { |
|
97 return EXIT_FAILURE; |
|
98 } |
|
99 } while (!isspace(c = fgetc(stream))); |
|
100 movestr[i] = '\0'; |
|
101 if (eval_move(gamestate, movestr, &move, curcol) |
|
102 != VALID_MOVE_SYNTAX) { |
|
103 return EXIT_FAILURE; |
|
104 } |
|
105 if (validate_move(gamestate, &move) != VALID_MOVE_SEMANTICS) { |
|
106 return EXIT_FAILURE; |
|
107 } |
|
108 apply_move(gamestate, &move); |
|
109 |
|
110 // TODO: parse comments |
|
111 while (isspace(c = fgetc(stream))); |
|
112 |
|
113 // end of game data encountered |
|
114 if (c == EOF) { |
|
115 break; |
|
116 } |
|
117 if (c == '1' || c == '0') { |
|
118 c = fgetc(stream); |
|
119 if (c == '-') { |
|
120 gamestate->resign = !gamestate->checkmate; |
|
121 break; |
|
122 } else if (c == '/') { |
|
123 gamestate->remis = !gamestate->stalemate; |
|
124 break; |
|
125 } else { |
|
126 // oops, it was a move number, go back! |
|
127 fseek(stream, -1, SEEK_CUR); |
|
128 } |
|
129 } |
|
130 |
|
131 // we have eaten the next valuable byte, so go back |
|
132 fseek(stream, -1, SEEK_CUR); |
|
133 |
|
134 // skip move number after black move |
|
135 if (curcol == BLACK) { |
|
136 while (isdigit(c = fgetc(stream))); |
|
137 if (c != '.') { |
|
138 return EXIT_FAILURE; |
|
139 } |
|
140 } |
|
141 curcol = opponent_color(curcol); |
|
142 } |
|
143 |
|
144 return EXIT_SUCCESS; |
|
145 } |
|
146 |
|
147 size_t write_pgn(FILE* stream, GameState *gamestate, GameInfo *gameinfo) { |
|
148 // TODO: tag pairs |
|
149 size_t bytes = 0; |
|
150 |
|
151 // Result |
|
152 char *result; |
|
153 if (gamestate->stalemate || gamestate->remis) { |
|
154 result = "1/2-1/2"; |
|
155 } else if (gamestate->checkmate || gamestate->resign) { |
|
156 if (gamestate->lastmove) { |
|
157 result = (gamestate->lastmove->move.piece & COLOR_MASK) == WHITE ? |
|
158 "1-0" : "0-1"; |
|
159 } else { |
|
160 result = "0-1"; |
|
161 } |
|
162 } else { |
|
163 result = "*"; |
|
164 } |
|
165 fprintf(stream, "[Result \"%s\"]\n\n", result); |
|
166 |
|
167 // moves |
|
168 int i = 1; |
|
169 for (MoveList *movelist = gamestate->movelist ; |
|
170 movelist ; movelist = movelist->next) { |
|
171 |
|
172 if (++i % 2 == 0) { |
|
173 fprintf(stream, "%d. %s", i/2, movelist->move.string); |
|
174 } else { |
|
175 fprintf(stream, " %s", movelist->move.string); |
|
176 } |
|
177 |
|
178 // TODO: move time and maybe other comments |
|
179 |
|
180 // line break every 10 moves |
|
181 if (i % 20) { |
|
182 fputc(' ', stream); |
|
183 } else { |
|
184 fputc('\n', stream); |
|
185 } |
|
186 } |
|
187 |
|
188 if (result[0] == '*') { |
|
189 fputc('\n', stream); |
|
190 } else { |
|
191 fprintf(stream, "%s\n", result); |
|
192 } |
|
193 |
|
194 |
|
195 return bytes; |
|
196 } |