src/chess/pgn.c

Mon, 16 Jun 2014 13:45:31 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 16 Jun 2014 13:45:31 +0200
changeset 50
41017d0a72c5
child 54
eef745ba3774
permissions
-rw-r--r--

added pgn parser and writer (without comment support yet) + minor refactorings

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

mercurial