src/chess/pgn.c

Mon, 16 Jun 2014 15:41:06 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 16 Jun 2014 15:41:06 +0200
changeset 51
84f2e380a434
parent 50
41017d0a72c5
child 54
eef745ba3774
permissions
-rw-r--r--

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;
}

mercurial