src/chess/pgn.c

Wed, 26 Aug 2015 14:46:42 +0200

author
Mike Becker <universe@uap-core.de>
date
Wed, 26 Aug 2015 14:46:42 +0200
changeset 54
eef745ba3774
parent 50
41017d0a72c5
child 55
54ea19938d57
permissions
-rw-r--r--

implemented FEN

     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 }
   198 static size_t fen_pieces(char *str, GameState *gamestate) {
   199     size_t i = 0;
   200     for (int row = 7 ; row >= 0 ; row--) {
   201         unsigned int skip = 0;
   202         for (int file = 0 ; file < 8 ; file++) {
   203             if (gamestate->board[row][file]) {
   204                 if (skip > 0) {
   205                     str[i++] = '0'+skip;
   206                     skip = 0;
   207                 }
   208                 switch (gamestate->board[row][file] & ~ENPASSANT_THREAT) {
   209                 case WHITE|KING: str[i++] = 'K'; break;
   210                 case WHITE|QUEEN: str[i++] = 'Q'; break;
   211                 case WHITE|BISHOP: str[i++] = 'B'; break;
   212                 case WHITE|KNIGHT: str[i++] = 'N'; break;
   213                 case WHITE|ROOK: str[i++] = 'R'; break;
   214                 case WHITE|PAWN: str[i++] = 'P'; break;
   215                 case BLACK|KING: str[i++] = 'k'; break;
   216                 case BLACK|QUEEN: str[i++] = 'q'; break;
   217                 case BLACK|BISHOP: str[i++] = 'b'; break;
   218                 case BLACK|KNIGHT: str[i++] = 'n'; break;
   219                 case BLACK|ROOK: str[i++] = 'r'; break;
   220                 case BLACK|PAWN: str[i++] = 'p'; break;
   221                 }
   222             } else {
   223                 skip++;
   224             }
   225         }
   226         if (skip > 0) {
   227             str[i++] = '0'+skip;
   228         }
   229         if (row > 0) {
   230             str[i++] = '/';
   231         }
   232     }
   234     return i;
   235 }
   237 static size_t fen_color(char *str, GameState *gamestate) {
   238     uint8_t color = opponent_color(gamestate->lastmove ?
   239         (gamestate->lastmove->move.piece & COLOR_MASK) : BLACK);
   241     str[0] = color == WHITE ? 'w' : 'b';
   243     return 1;
   244 }
   246 static _Bool fen_castling_chkmoved(GameState *gamestate,
   247     uint8_t row, uint8_t file) {
   249     MoveList *ml = gamestate->movelist;
   250     while (ml) {
   251         if (ml->move.fromfile == file && ml->move.fromrow == row) {
   252             return 1;
   253         }
   254         ml = ml->next;
   255     }
   257     return 0;
   258 }
   260 static size_t fen_castling(char *str, GameState *gamestate) {
   261     _Bool K, Q, k, q;
   263     if (fen_castling_chkmoved(gamestate, rowidx('1'), fileidx('e'))) {
   264         K = Q = 0;
   265     } else {
   266         K = !fen_castling_chkmoved(gamestate, rowidx('1'), fileidx('h'));
   267         Q = !fen_castling_chkmoved(gamestate, rowidx('1'), fileidx('a'));
   268     }
   269     if (fen_castling_chkmoved(gamestate, rowidx('8'), fileidx('e'))) {
   270         k = q = 0;
   271     } else {
   272         k = !fen_castling_chkmoved(gamestate, rowidx('8'), fileidx('h'));
   273         q = !fen_castling_chkmoved(gamestate, rowidx('8'), fileidx('a'));
   274     }
   276     size_t i = 0;
   277     if (K) str[i++] = 'K';
   278     if (Q) str[i++] = 'Q';
   279     if (k) str[i++] = 'k';
   280     if (q) str[i++] = 'q';
   281     if (!i) str[i++] = '-';
   283     return i;
   284 }
   286 static size_t fen_enpassant(char *str, GameState *gamestate) {
   288     str[0] = '-'; str[1] = '\0';
   290     for (int file = 0 ; file < 8 ; file++) {
   291         if (gamestate->board[3][file] & ENPASSANT_THREAT) {
   292             str[0] = filechr(file);
   293             str[1] = rowchr(2);
   294         }
   295         if (gamestate->board[4][file] & ENPASSANT_THREAT) {
   296             str[0] = filechr(file);
   297             str[1] = rowchr(5);
   298         }
   299     }
   301     return str[0] == '-' ? 1 : 2;
   302 }
   304 static size_t fen_halfmove(char *str, GameState *gamestate) {
   306     unsigned int i = 0;
   307     for (MoveList *move = gamestate->movelist ; move ; move = move->next) {
   308         if (move->move.capture || (move->move.piece & PIECE_MASK) == PAWN) {
   309             i = 0;
   310         } else {
   311             i++;
   312         }
   313     }
   315     char m[8];
   316     size_t len = sprintf(m, "%u", i);
   317     memcpy(str, m, len);
   319     return len;
   320 }
   322 static size_t fen_movenr(char *str, GameState *gamestate) {
   324     MoveList *move = gamestate->movelist;
   325     unsigned int i = 1;
   326     while (move) {
   327         i++;
   328         move = move->next;
   329     }
   331     char m[8];
   332     size_t len = sprintf(m, "%u", i);
   333     memcpy(str, m, len);
   335     return len;
   336 }
   338 void compute_fen(char *str, GameState *gamestate) {
   339     str += fen_pieces(str, gamestate);
   340     *str = ' '; str++;
   341     str += fen_color(str, gamestate);
   342     *str = ' '; str++;
   343     str += fen_castling(str, gamestate);
   344     *str = ' '; str++;
   345     str += fen_enpassant(str, gamestate);
   346     *str = ' '; str++;
   347     str += fen_halfmove(str, gamestate);
   348     *str = ' '; str++;
   349     str += fen_movenr(str, gamestate);
   350     str[0] = '\0';
   351 }

mercurial