src/chess/pgn.c

changeset 50
41017d0a72c5
child 54
eef745ba3774
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/chess/pgn.c	Mon Jun 16 13:45:31 2014 +0200
@@ -0,0 +1,196 @@
+/*
+ * 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;
+}

mercurial