src/chess/pgn.c

changeset 145
21b76056a178
parent 143
e9159c70ff9e
equal deleted inserted replaced
144:c3ae96ed44d1 145:21b76056a178
34 #include <string.h> 34 #include <string.h>
35 #include <sys/time.h> 35 #include <sys/time.h>
36 #include <time.h> 36 #include <time.h>
37 37
38 enum { 38 enum {
39 pgn_error_missing_quote = 1, 39 pgn_no_error = 0,
40 pgn_error_missing_quote,
40 pgn_error_missing_bracket, 41 pgn_error_missing_bracket,
41 pgn_error_missing_brace, 42 pgn_error_missing_brace,
42 pgn_error_missing_dot, 43 pgn_error_missing_dot,
43 pgn_error_move_syntax, 44 pgn_error_move_syntax,
44 pgn_error_move_semantics, 45 pgn_error_move_semantics,
45 pgn_error_result_syntax, 46 pgn_error_result_syntax,
47 pgn_error_result_mismatch,
46 pgn_error_unexpected_eof, 48 pgn_error_unexpected_eof,
47 }; 49 };
48 50
49 static const char* pgn_error_strings[] = { 51 static const char* pgn_error_strings[] = {
50 "No Error.", 52 "No Error.",
53 "Tags must be enclosed in square brackets: '[Key \"Value\"]'.", 55 "Tags must be enclosed in square brackets: '[Key \"Value\"]'.",
54 "Move numbers must be terminated with a dot (e.g. '13.' - not '13').", 56 "Move numbers must be terminated with a dot (e.g. '13.' - not '13').",
55 "Move is syntactically incorrect.", 57 "Move is syntactically incorrect.",
56 "Move is not valid according to chess rules.", 58 "Move is not valid according to chess rules.",
57 "Result syntax is incorrect. Expected 1-0, 0-1, 1/2-1/2, or *.", 59 "Result syntax is incorrect. Expected 1-0, 0-1, 1/2-1/2, or *.",
60 "Result at end of move text does not match result in tag roster.",
58 "Unexpected end of data.", 61 "Unexpected end of data.",
59 }; 62 };
60 63
61 const char* pgn_error_str(int code) { 64 const char* pgn_error_str(int code) {
62 return pgn_error_strings[code]; 65 return pgn_error_strings[code];
66 }
67
68 static const char *pgn_result(GameState *gamestate) {
69 if (gamestate->stalemate || gamestate->remis) {
70 return "1/2-1/2";
71 } else if (gamestate->wresign) {
72 return "0-1";
73 } else if (gamestate->bresign) {
74 return "1-0";
75 } else if (gamestate->checkmate) {
76 if (current_color(gamestate) == BLACK) {
77 return "1-0";
78 } else {
79 return "0-1";
80 }
81 } else {
82 return "*";
83 }
63 } 84 }
64 85
65 int parse_pgn(const char *pgndata, GameState *gamestate) { 86 int parse_pgn(const char *pgndata, GameState *gamestate) {
66 int i; 87 int i;
67 char c; 88 char c;
68 89
69 char result[8]; 90 char result[8] = {0};
70 91
71 char tagkey[32]; 92 char tagkey[32];
72 char tagvalue[128]; 93 char tagvalue[128];
73 94
74 /* read tag pairs */ 95 /* read tag pairs */
101 if (*(pgndata++) != ']') { 122 if (*(pgndata++) != ']') {
102 return pgn_error_missing_bracket; 123 return pgn_error_missing_bracket;
103 } 124 }
104 125
105 // TODO: read clock info 126 // TODO: read clock info
127
106 if (strcmp("Result", tagkey) == 0) { 128 if (strcmp("Result", tagkey) == 0) {
107 // TODO: why are we parsing this? it is never used!
108 memcpy(result, tagvalue, 8); 129 memcpy(result, tagvalue, 8);
109 } 130 }
110 } 131 }
111 132
112 /* read moves */ 133 /* read moves */
115 } 136 }
116 137
117 char movestr[10]; 138 char movestr[10];
118 Move move; 139 Move move;
119 uint8_t curcol = WHITE; 140 uint8_t curcol = WHITE;
141 bool movetext_ends_with_result = false;
120 142
121 while (true) { 143 while (true) {
122 /* move */ 144 /* move */
123 while (isspace(c = *(pgndata++))); 145 while (isspace(c = *(pgndata++)));
124 i = 0; 146 i = 0;
130 } while (!isspace(c = *(pgndata++))); 152 } while (!isspace(c = *(pgndata++)));
131 movestr[i] = '\0'; 153 movestr[i] = '\0';
132 if (eval_move(gamestate, movestr, &move, curcol) 154 if (eval_move(gamestate, movestr, &move, curcol)
133 != VALID_MOVE_SYNTAX) { 155 != VALID_MOVE_SYNTAX) {
134 return pgn_error_move_syntax; 156 return pgn_error_move_syntax;
135 } 157 }
136 if (validate_move(gamestate, &move) != VALID_MOVE_SEMANTICS) { 158 if (validate_move(gamestate, &move) != VALID_MOVE_SEMANTICS) {
137 return pgn_error_move_semantics; 159 return pgn_error_move_semantics;
138 } 160 }
139 apply_move(gamestate, &move); 161 apply_move(gamestate, &move);
140 162
172 gamestate->bresign = true; 194 gamestate->bresign = true;
173 } else { 195 } else {
174 return pgn_error_result_syntax; 196 return pgn_error_result_syntax;
175 } 197 }
176 } 198 }
199 movetext_ends_with_result = true;
177 break; 200 break;
178 } else if (c == '/') { 201 } else if (c == '/') {
179 gamestate->remis = !gamestate->stalemate; 202 gamestate->remis = !gamestate->stalemate;
203 movetext_ends_with_result = true;
180 break; 204 break;
181 } else { 205 } else {
182 /* oops, it was a move number, go back! */ 206 /* oops, it was a move number, go back! */
183 pgndata--; 207 pgndata--;
184 } 208 }
194 return pgn_error_missing_dot; 218 return pgn_error_missing_dot;
195 } 219 }
196 } 220 }
197 curcol = opponent_color(curcol); 221 curcol = opponent_color(curcol);
198 } 222 }
199 223
200 return 0; 224 /* sanity check result - if it was specified */
225 if (movetext_ends_with_result && result[0]) {
226 if (strncmp(result, pgn_result(gamestate), 8) != 0) {
227 fprintf(stderr, "Fuck it: %s\n", result);
228 return pgn_error_result_mismatch;
229 }
230 }
231
232 return pgn_no_error;
201 } 233 }
202 234
203 int read_pgn(FILE *stream, GameState *gamestate) { 235 int read_pgn(FILE *stream, GameState *gamestate) {
204 fseek(stream, 0, SEEK_END); 236 fseek(stream, 0, SEEK_END);
205 long int size = ftell(stream); 237 long int size = ftell(stream);
281 fprintf(stream, "[Site \"%s\"]\n", "Somewhere on Earth"); 313 fprintf(stream, "[Site \"%s\"]\n", "Somewhere on Earth");
282 fprintf(stream, "[Date \"%s\"]\n", pgn_date(gamestate)); 314 fprintf(stream, "[Date \"%s\"]\n", pgn_date(gamestate));
283 fprintf(stream, "[Round \"%s\"]\n", "-"); 315 fprintf(stream, "[Round \"%s\"]\n", "-");
284 fprintf(stream, "[White \"%s\"]\n", pgn_player_name(gamestate, WHITE)); 316 fprintf(stream, "[White \"%s\"]\n", pgn_player_name(gamestate, WHITE));
285 fprintf(stream, "[Black \"%s\"]\n", pgn_player_name(gamestate, BLACK)); 317 fprintf(stream, "[Black \"%s\"]\n", pgn_player_name(gamestate, BLACK));
286 318 const char *result = pgn_result(gamestate);
287 char *result;
288 if (gamestate->stalemate || gamestate->remis) {
289 result = "1/2-1/2";
290 } else if (gamestate->wresign) {
291 result = "0-1";
292 } else if (gamestate->bresign) {
293 result = "1-0";
294 } else if (gamestate->checkmate) {
295 if (current_color(gamestate) == BLACK) {
296 result = "1-0";
297 } else {
298 result = "0-1";
299 }
300 } else {
301 result = "*";
302 }
303 fprintf(stream, "[Result \"%s\"]\n\n", result); 319 fprintf(stream, "[Result \"%s\"]\n\n", result);
304 320
305 // TODO: add optional clock info 321 // TODO: add optional clock info
306 322
307 /* moves */ 323 /* moves */

mercurial