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

     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  */
    30 #include "pgn.h"
    31 #include <ctype.h>
    32 #include <stdlib.h>
    33 #include <string.h>
    35 int read_pgn(FILE* stream, GameState *gamestate, GameInfo *gameinfo) {
    36     int c, i;
    38     char result[8];
    40     char tagkey[32];
    41     char tagvalue[128];
    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         }
    76         if (strcmp("Result", tagkey) == 0) {
    77             memcpy(result, tagvalue, 8);
    78         }
    79     }
    81     // read moves
    82     if (fgetc(stream) != '.') {
    83         return EXIT_FAILURE;
    84     }
    86     char movestr[10];
    87     Move move;
    88     uint8_t curcol = WHITE;
    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);
   110         // TODO: parse comments
   111         while (isspace(c = fgetc(stream)));
   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         }
   131         // we have eaten the next valuable byte, so go back
   132         fseek(stream, -1, SEEK_CUR);
   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     }
   144     return EXIT_SUCCESS;
   145 }
   147 size_t write_pgn(FILE* stream, GameState *gamestate, GameInfo *gameinfo) {
   148     // TODO: tag pairs
   149     size_t bytes = 0;
   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);
   167     // moves
   168     int i = 1;
   169     for (MoveList *movelist = gamestate->movelist ;
   170         movelist ; movelist = movelist->next) {
   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         }
   178         // TODO: move time and maybe other comments
   180         // line break every 10 moves
   181         if (i % 20)  {
   182             fputc(' ', stream);
   183         } else {
   184             fputc('\n', stream);
   185         }
   186     }
   188     if (result[0] == '*') {
   189         fputc('\n', stream);
   190     } else {
   191         fprintf(stream, "%s\n", result);
   192     }
   195     return bytes;
   196 }

mercurial