Mon, 16 Jun 2014 15:41:06 +0200
added support for game continuation over network + fixed major bug in checkmate anticipation when the king is attacked diagonally
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2014 Mike Becker. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #include "pgn.h" #include <ctype.h> #include <stdlib.h> #include <string.h> int read_pgn(FILE* stream, GameState *gamestate, GameInfo *gameinfo) { int c, i; char result[8]; char tagkey[32]; char tagvalue[128]; // read tag pairs _Bool readmoves = 0; while (!readmoves) { while (isspace(c = fgetc(stream))); if (c == '1') { readmoves = 1; break; } if (c != '[') { return EXIT_FAILURE; } while (isspace(c = fgetc(stream))); i = 0; do { tagkey[i++] = c; } while (!isspace(c = fgetc(stream))); tagkey[i] = '\0'; while (isspace(c = fgetc(stream))); if (c != '"') { return EXIT_FAILURE; } i = 0; while ((c = fgetc(stream)) != '"') { if (c == '\n') { return EXIT_FAILURE; } tagvalue[i++] = c; } tagvalue[i] = '\0'; if (fgetc(stream) != ']') { return EXIT_FAILURE; } if (strcmp("Result", tagkey) == 0) { memcpy(result, tagvalue, 8); } } // read moves if (fgetc(stream) != '.') { return EXIT_FAILURE; } char movestr[10]; Move move; uint8_t curcol = WHITE; while (readmoves) { // move while (isspace(c = fgetc(stream))); i = 0; do { movestr[i++] = c; if (i >= 10) { return EXIT_FAILURE; } } while (!isspace(c = fgetc(stream))); movestr[i] = '\0'; if (eval_move(gamestate, movestr, &move, curcol) != VALID_MOVE_SYNTAX) { return EXIT_FAILURE; } if (validate_move(gamestate, &move) != VALID_MOVE_SEMANTICS) { return EXIT_FAILURE; } apply_move(gamestate, &move); // TODO: parse comments while (isspace(c = fgetc(stream))); // end of game data encountered if (c == EOF) { break; } if (c == '1' || c == '0') { c = fgetc(stream); if (c == '-') { gamestate->resign = !gamestate->checkmate; break; } else if (c == '/') { gamestate->remis = !gamestate->stalemate; break; } else { // oops, it was a move number, go back! fseek(stream, -1, SEEK_CUR); } } // we have eaten the next valuable byte, so go back fseek(stream, -1, SEEK_CUR); // skip move number after black move if (curcol == BLACK) { while (isdigit(c = fgetc(stream))); if (c != '.') { return EXIT_FAILURE; } } curcol = opponent_color(curcol); } return EXIT_SUCCESS; } size_t write_pgn(FILE* stream, GameState *gamestate, GameInfo *gameinfo) { // TODO: tag pairs size_t bytes = 0; // Result char *result; if (gamestate->stalemate || gamestate->remis) { result = "1/2-1/2"; } else if (gamestate->checkmate || gamestate->resign) { if (gamestate->lastmove) { result = (gamestate->lastmove->move.piece & COLOR_MASK) == WHITE ? "1-0" : "0-1"; } else { result = "0-1"; } } else { result = "*"; } fprintf(stream, "[Result \"%s\"]\n\n", result); // moves int i = 1; for (MoveList *movelist = gamestate->movelist ; movelist ; movelist = movelist->next) { if (++i % 2 == 0) { fprintf(stream, "%d. %s", i/2, movelist->move.string); } else { fprintf(stream, " %s", movelist->move.string); } // TODO: move time and maybe other comments // line break every 10 moves if (i % 20) { fputc(' ', stream); } else { fputc('\n', stream); } } if (result[0] == '*') { fputc('\n', stream); } else { fprintf(stream, "%s\n", result); } return bytes; }