Subversion Repositories Games.Chess Giants

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.     Protector -- a UCI chess engine
  3.  
  4.     Copyright (C) 2009-2010 Raimund Heid (Raimund_Heid@yahoo.com)
  5.  
  6.     This program is free software: you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation, either version 3 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     This program is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  
  19. */
  20.  
  21. #include <assert.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <ctype.h>
  25. #include "io.h"
  26. #include "pgn.h"
  27. #include "bitboard.h"
  28. #include "position.h"
  29. #include "movegeneration.h"
  30. #include "fen.h"
  31.  
  32. #define BUFSIZE 8192
  33. #define INCREMENT 1024
  34.  
  35. #define STATE_INIT      0
  36. #define STATE_1         1
  37. #define STATE_2         2
  38. #define STATE_IGNORE    3
  39.  
  40. static char pieceName[16];
  41.  
  42. /*
  43.  ******************************************************************************
  44.  *
  45.  *   File operations
  46.  *
  47.  ******************************************************************************
  48.  */
  49.  
  50. static int scanForIndex(PGNFile * pgnfile, int state, char buffer[],
  51.                         size_t bufsize, size_t offset)
  52. {
  53.    size_t i = 0;
  54.  
  55.    while (i < bufsize)
  56.    {
  57.       switch (buffer[i++])
  58.       {
  59.       case '\n':
  60.  
  61.          if (state < STATE_IGNORE)
  62.          {
  63.             state++;
  64.          }
  65.  
  66.          break;
  67.  
  68.       case '\r':
  69.       case ' ':
  70.          continue;
  71.  
  72.       case '{':
  73.  
  74.          if (state < STATE_IGNORE)
  75.          {
  76.             state = STATE_IGNORE;
  77.          }
  78.          else
  79.          {
  80.             state++;
  81.          }
  82.  
  83.          break;
  84.  
  85.       case '}':
  86.  
  87.          if (state > STATE_IGNORE)
  88.          {
  89.             state--;
  90.          }
  91.          else
  92.          {
  93.             state = 0;
  94.          }
  95.  
  96.          break;
  97.  
  98.       case '[':
  99.  
  100.          if (state == 2)
  101.          {
  102.             if (pgnfile->numGames + 1 == pgnfile->indexSize)
  103.             {
  104.                pgnfile->indexSize += INCREMENT;
  105.                pgnfile->index = (long *) realloc(pgnfile->index,
  106.                                                  pgnfile->indexSize *
  107.                                                  sizeof(long));
  108.             }
  109.  
  110.             pgnfile->index[pgnfile->numGames++] = (long) (i + offset - 1L);
  111.             assert(pgnfile->numGames < pgnfile->indexSize);
  112.          }
  113.  
  114.          break;
  115.  
  116.       default:
  117.  
  118.          if (state != STATE_IGNORE)
  119.          {
  120.             state = 0;
  121.          }
  122.       }
  123.    }
  124.  
  125.    return state;
  126. }
  127.  
  128. static void buildIndex(PGNFile * pgnfile)
  129. {
  130.    char buffer[BUFSIZE];
  131.    int state = STATE_2;
  132.    size_t numRead;
  133.  
  134.    rewind(pgnfile->file);
  135.  
  136.    do
  137.    {
  138.       long pos = ftell(pgnfile->file);
  139.  
  140.       numRead = fread(buffer, 1, BUFSIZE, pgnfile->file);
  141.       state = scanForIndex(pgnfile, state, buffer, numRead, pos);
  142.    }
  143.    while (numRead == BUFSIZE);
  144.  
  145.    fseek(pgnfile->file, 0, SEEK_END);
  146.    pgnfile->index[pgnfile->numGames] = ftell(pgnfile->file) + 1;
  147. }
  148.  
  149. int openPGNFile(PGNFile * pgnfile, const char *filename)
  150. {
  151.    pgnfile->index = (long *) malloc(INCREMENT * sizeof(long));
  152.  
  153.    pgnfile->indexSize = INCREMENT;
  154.    pgnfile->numGames = 0;
  155.    pgnfile->file = fopen(filename, "rb");
  156.  
  157.    if (pgnfile->index != 0 && pgnfile->file != 0)
  158.    {
  159.       buildIndex(pgnfile);
  160.       return 0;
  161.    }
  162.    else
  163.    {
  164.       return -1;
  165.    }
  166. }
  167.  
  168. void closePGNFile(PGNFile * pgnfile)
  169. {
  170.    if (pgnfile->index != 0)
  171.    {
  172.       free(pgnfile->index);
  173.    }
  174.  
  175.    pgnfile->indexSize = 0;
  176.    pgnfile->numGames = 0;
  177.  
  178.    if (pgnfile->file != 0)
  179.    {
  180.       fclose(pgnfile->file);
  181.       pgnfile->file = 0;
  182.    }
  183. }
  184.  
  185. static char *getGameText(PGNFile * pgnfile, int number)
  186. {
  187.    long start, end, length;
  188.    char *buffer;
  189.  
  190.    if (number < 1 || number > pgnfile->numGames)
  191.    {
  192.       return 0;
  193.    }
  194.  
  195.    start = pgnfile->index[number - 1];
  196.    end = pgnfile->index[number] - 1;
  197.    length = (int) (end - start);
  198.  
  199.    if ((buffer = malloc(length + 1)) == NULL)
  200.    {
  201.       return 0;
  202.    }
  203.  
  204.    fseek(pgnfile->file, start, SEEK_SET);
  205.    fread(buffer, 1, length, pgnfile->file);
  206.    buffer[length] = '\0';
  207.    trim(buffer);
  208.  
  209.    return buffer;
  210. }
  211.  
  212. /*
  213.  ******************************************************************************
  214.  *
  215.  *   Object generation and destruction
  216.  *
  217.  ******************************************************************************
  218.  */
  219.  
  220. static Gamemove *getGamemove(const Position * position,
  221.                              Gamemove * previousMove, PGNGame * game)
  222. {
  223.    Gamemove *new = &(game->moveHeap[game->nextMoveFromHeap++]);
  224.  
  225.    new->from = NO_SQUARE;
  226.    new->to = NO_SQUARE;
  227.    new->newPiece = NO_PIECE;
  228.    new->position = *position;
  229.    new->previousMove = previousMove;
  230.    new->nextMove = new->alternativeMove = 0;
  231.    new->comment = new->glyphs = 0;
  232.  
  233.    return new;
  234. }
  235.  
  236. Move gameMove2Move(const Gamemove * gamemove)
  237. {
  238.    return getPackedMove(gamemove->from, gamemove->to, gamemove->newPiece);
  239. }
  240.  
  241. void initializePGNGame(PGNGame * game)
  242. {
  243.    game->setup[0] = game->fen[0] = '\0';
  244.    game->moveText = 0;
  245.    game->firstMove = game->lastMove = 0;
  246.    game->nextMoveFromHeap = 0;
  247. }
  248.  
  249. void resetPGNGame(PGNGame * game)
  250. {
  251.    if (game->moveText != 0)
  252.    {
  253.       free(game->moveText);
  254.    }
  255.  
  256.    initializePGNGame(game);
  257. }
  258.  
  259. void freePgnGame(PGNGame * game)
  260. {
  261.    resetPGNGame(game);
  262.    free(game);
  263. }
  264.  
  265. /*
  266.  ******************************************************************************
  267.  *
  268.  *   Parser operations
  269.  *
  270.  ******************************************************************************
  271.  */
  272.  
  273. static Move parsePawnMove(const Position * position, const char *moveText)
  274. {
  275.    Square from = NO_SQUARE, to = NO_SQUARE;
  276.    Piece newPiece = NO_PIECE;
  277.    int pawnStep = (position->activeColor == WHITE ? 8 : -8);
  278.    size_t textLength = strlen(moveText);
  279.  
  280.    if (textLength < 2)
  281.    {
  282.       return NO_MOVE;
  283.    }
  284.  
  285.    assert(textLength <= 6);
  286.  
  287.    /*
  288.     * capture or push?
  289.     */
  290.    if (textLength >= 4 && moveText[1] == 'x')
  291.    {
  292.       to = getSquare(moveText[2] - 'a', moveText[3] - '1');
  293.       from = (Square)
  294.          (getSquare(moveText[0] - 'a', moveText[3] - '1') - pawnStep);
  295.    }
  296.    else
  297.    {
  298.       to = getSquare(moveText[0] - 'a', moveText[1] - '1');
  299.       from = (Square) (to - pawnStep);
  300.  
  301.       if (position->piece[from] == NO_PIECE)
  302.       {
  303.          from = (Square) (from - pawnStep);
  304.       }
  305.    }
  306.  
  307.    /*
  308.     * promotion?
  309.     */
  310.    if (textLength >= 4 && moveText[textLength - 2] == '=')
  311.    {
  312.       switch (moveText[textLength - 1])
  313.       {
  314.       case 'R':
  315.          newPiece = WHITE_ROOK;
  316.          break;
  317.       case 'B':
  318.          newPiece = WHITE_BISHOP;
  319.          break;
  320.       case 'N':
  321.          newPiece = WHITE_KNIGHT;
  322.          break;
  323.       default:
  324.          newPiece = WHITE_QUEEN;
  325.       }
  326.    }
  327.  
  328.    return getPackedMove(from, to, newPiece);
  329. }
  330.  
  331. static Move parseCastlingMove(const Position * position, const char *moveText)
  332. {
  333.    const Rank rank = (position->activeColor == WHITE ? RANK_1 : RANK_8);
  334.    const File file = (strcmp(moveText, "O-O") == 0 ? FILE_G : FILE_C);
  335.  
  336.    assert(strlen(moveText) >= 3 && strlen(moveText) <= 5);
  337.  
  338.    return getPackedMove(getSquare(FILE_E, rank), getSquare(file, rank),
  339.                         NO_PIECE);
  340. }
  341.  
  342. static Move parsePieceMove(const Position * position,
  343.                            const char *moveText, const PieceType pieceType)
  344. {
  345.    Square from = NO_SQUARE, to = NO_SQUARE;
  346.    Piece newPiece = NO_PIECE;
  347.    size_t textLength = strlen(moveText);
  348.    int fromFile = -1, fromRank = -1;
  349.    size_t pointer = 1;
  350.    char currentChar;
  351.    Bitboard candidates;
  352.  
  353.    if (textLength < 3)
  354.    {
  355.       return NO_MOVE;
  356.    }
  357.  
  358.    assert(strlen(moveText) <= 6);
  359.  
  360.    to = getSquare(moveText[textLength - 2] - 'a',
  361.                   moveText[textLength - 1] - '1');
  362.  
  363.    while (pointer < textLength - 2)
  364.    {
  365.       currentChar = moveText[pointer++];
  366.  
  367.       if (currentChar >= 'a' && currentChar <= 'h')
  368.       {
  369.          fromFile = currentChar - 'a';
  370.       }
  371.  
  372.       if (currentChar >= '1' && currentChar <= '8')
  373.       {
  374.          fromRank = currentChar - '1';
  375.       }
  376.    }
  377.  
  378.    if (fromFile != -1 && fromRank != -1)
  379.    {
  380.       from = getSquare(fromFile, fromRank);
  381.    }
  382.    else
  383.    {
  384.       candidates =
  385.          getDirectAttackers(position, to, position->activeColor,
  386.                             position->allPieces) &
  387.          position->piecesOfType[pieceType | position->activeColor];
  388.  
  389.       if (fromFile != -1)
  390.       {
  391.          candidates &= getSquaresOfFile((File) fromFile);
  392.       }
  393.       else if (fromRank != -1)
  394.       {
  395.          candidates &= getSquaresOfRank((Rank) fromRank);
  396.       }
  397.  
  398.       if (candidates == EMPTY_BITBOARD)
  399.       {
  400.          return NO_MOVE;
  401.       }
  402.  
  403.       from = getLastSquare(&candidates);
  404.  
  405.       while (candidates != EMPTY_BITBOARD)
  406.       {
  407.          const Move move = getPackedMove(from, to, newPiece);
  408.  
  409.          if (moveIsLegal(position, move))
  410.          {
  411.             return move;
  412.          }
  413.  
  414.          from = getLastSquare(&candidates);
  415.       }
  416.    }
  417.  
  418.    return getPackedMove(from, to, newPiece);
  419. }
  420.  
  421. static Move parsePGNMove(const char *moveText, Position * position)
  422. {
  423.    char moveTextBuffer[16];
  424.    int i = 0;
  425.  
  426.    while (i < 15 && moveText[i] != 0 && moveText[i] != '+' &&
  427.           moveText[i] != '#' && moveText[i] != '!' &&
  428.           moveText[i] != '?' && moveText[i] != ')' &&
  429.           !isspace((int) moveText[i]))
  430.    {
  431.       moveTextBuffer[i] = moveText[i];
  432.       i++;
  433.    }
  434.  
  435.    moveTextBuffer[i] = 0;
  436.  
  437.    switch (moveTextBuffer[0])
  438.    {
  439.    case 'a':
  440.    case 'b':
  441.    case 'c':
  442.    case 'd':
  443.    case 'e':
  444.    case 'f':
  445.    case 'g':
  446.    case 'h':
  447.       return parsePawnMove(position, moveTextBuffer);
  448.       break;
  449.    case 'K':
  450.       return parsePieceMove(position, moveTextBuffer, KING);
  451.       break;
  452.    case 'Q':
  453.       return parsePieceMove(position, moveTextBuffer, QUEEN);
  454.       break;
  455.    case 'R':
  456.       return parsePieceMove(position, moveTextBuffer, ROOK);
  457.       break;
  458.    case 'B':
  459.       return parsePieceMove(position, moveTextBuffer, BISHOP);
  460.       break;
  461.    case 'N':
  462.       return parsePieceMove(position, moveTextBuffer, KNIGHT);
  463.       break;
  464.    case 'O':
  465.       return parseCastlingMove(position, moveTextBuffer);
  466.       break;
  467.    default:
  468.  
  469.       return NO_MOVE;
  470.    }
  471. }
  472.  
  473. Move interpretPGNMove(const char *moveText, PGNGame * game)
  474. {
  475.    Variation variation;
  476.  
  477.    initializeVariationFromGame(&variation, game);
  478.  
  479.    return parsePGNMove(moveText, &variation.singlePosition);
  480. }
  481.  
  482. int appendMove(PGNGame * game, const Move move)
  483. {
  484.    Variation variation;
  485.  
  486.    initializeVariationFromGame(&variation, game);
  487.  
  488.    if (moveIsLegal(&variation.singlePosition, move))
  489.    {
  490.       Gamemove *newMove =
  491.          getGamemove(&variation.singlePosition, game->lastMove, game);
  492.  
  493.       newMove->from = getFromSquare(move);
  494.       newMove->to = getToSquare(move);
  495.       newMove->newPiece = getNewPiece(move);
  496.  
  497.       if (game->lastMove == 0)
  498.       {
  499.          game->firstMove = game->lastMove = newMove;
  500.       }
  501.       else
  502.       {
  503.          game->lastMove->nextMove = newMove;
  504.          game->lastMove = newMove;
  505.       }
  506.  
  507.       return 0;
  508.    }
  509.    else
  510.    {
  511.       return -1;
  512.    }
  513. }
  514.  
  515. void takebackLastMove(PGNGame * game)
  516. {
  517.    if (game->lastMove != 0)
  518.    {
  519.       if (game->lastMove == game->firstMove)
  520.       {
  521.          game->lastMove = 0;
  522.       }
  523.       else
  524.       {
  525.          game->lastMove = game->lastMove->previousMove;
  526.       }
  527.    }
  528. }
  529.  
  530. static void addAlternativeMove(Gamemove * move, Gamemove * alternative)
  531. {
  532.    if (move->alternativeMove != 0)
  533.    {
  534.       addAlternativeMove(move->alternativeMove, alternative);
  535.    }
  536.    else
  537.    {
  538.       move->alternativeMove = alternative;
  539.       alternative->previousMove = move->previousMove;
  540.    }
  541. }
  542.  
  543. static Gamemove *parseMoveText(const char *pgnMoveText,
  544.                                size_t * offset, const Position * position,
  545.                                PGNGame * game)
  546. {
  547.    bool variationTerminated = FALSE;
  548.    Gamemove *baseMove = 0, *lastMove = 0;
  549.    Variation variation;
  550.    char currentChar;
  551.    const char *token;
  552.    const bool debugOutput = FALSE;
  553.  
  554.    setBasePosition(&variation, position);
  555.    prepareSearch(&variation);
  556.  
  557.    if (debugOutput)
  558.    {
  559.       logDebug("Starting pgn parsing at: >%s<\n", &pgnMoveText[*offset]);
  560.    }
  561.  
  562.    while (variationTerminated == FALSE &&
  563.           (currentChar = pgnMoveText[(*offset)++]) != '\0')
  564.    {
  565.       if (strchr(" \r\n0123456789.", currentChar) != 0)
  566.       {
  567.          continue;
  568.       }
  569.  
  570.       token = &(pgnMoveText[*offset - 1]);
  571.  
  572.       if (debugOutput)
  573.       {
  574.          logDebug("\nCurrent token: >%s<\n", token);
  575.       }
  576.  
  577.       if (currentChar == '{')
  578.       {
  579.          char *comment = getToken(token + 1, "}");
  580.  
  581.          if (debugOutput)
  582.          {
  583.             logDebug("Starting annotation: >%s<\n", comment);
  584.          }
  585.  
  586.          (*offset) += strlen(comment);
  587.  
  588.          if (lastMove != 0)
  589.          {
  590.             lastMove->comment = comment;
  591.          }
  592.          else
  593.          {
  594.             free(comment);
  595.          }
  596.  
  597.          continue;
  598.       }
  599.  
  600.       if (currentChar == '(')
  601.       {
  602.          if (debugOutput)
  603.          {
  604.             logDebug("Starting subvariation.\n");
  605.          }
  606.  
  607.          if (lastMove != 0)
  608.          {
  609.             addAlternativeMove(lastMove,
  610.                                parseMoveText(pgnMoveText, offset,
  611.                                              &lastMove->position, game));
  612.          }
  613.       }
  614.  
  615.       if (currentChar == ')')
  616.       {
  617.          variationTerminated = TRUE;
  618.  
  619.          if (debugOutput)
  620.          {
  621.             logDebug("Subvariation terminated.\n");
  622.          }
  623.       }
  624.  
  625.       if (currentChar == '$')
  626.       {
  627.          char *glyph = getToken(token, " )\r\n");
  628.  
  629.          if (lastMove != 0)
  630.          {
  631.             if (lastMove->glyphs != 0)
  632.             {
  633.                if (debugOutput)
  634.                {
  635.                   logDebug("Existing glyphs: >%s<\n", lastMove->glyphs);
  636.                }
  637.  
  638.                lastMove->glyphs = realloc(lastMove->glyphs,
  639.                                           strlen(lastMove->glyphs) +
  640.                                           strlen(glyph) + 2);
  641.                strcat(lastMove->glyphs, " ");
  642.                strcat(lastMove->glyphs, glyph);
  643.                free(glyph);
  644.             }
  645.             else
  646.             {
  647.                if (debugOutput)
  648.                {
  649.                   logDebug("Initializing glyphs...\n");
  650.                }
  651.  
  652.                lastMove->glyphs = glyph;
  653.             }
  654.  
  655.             if (debugOutput)
  656.             {
  657.                logDebug("glyphs: >%s<\n", lastMove->glyphs);
  658.             }
  659.          }
  660.          else
  661.          {
  662.             free(glyph);
  663.          }
  664.       }
  665.  
  666.       if (strchr("KQRBNOabcdefgh", currentChar) != 0)
  667.       {
  668.          Move move;
  669.  
  670.          /*dumpPosition(variation->currentPosition); */
  671.  
  672.          if (debugOutput)
  673.          {
  674.             logDebug("Move token: >%s<\n", token);
  675.          }
  676.  
  677.          move = parsePGNMove(token, &variation.singlePosition);
  678.  
  679.          if (moveIsLegal(&variation.singlePosition, move))
  680.          {
  681.             Gamemove *newMove =
  682.                getGamemove(&variation.singlePosition, lastMove, game);
  683.  
  684.             newMove->from = getFromSquare(move);
  685.             newMove->to = getToSquare(move);
  686.             newMove->newPiece = getNewPiece(move);
  687.  
  688.             if (lastMove == 0)
  689.             {
  690.                baseMove = newMove;
  691.             }
  692.             else
  693.             {
  694.                lastMove->nextMove = newMove;
  695.             }
  696.  
  697.             lastMove = newMove;
  698.             makeMove(&variation, move);
  699.             setBasePosition(&variation, &variation.singlePosition);
  700.          }
  701.          else
  702.          {
  703.             logDebug("### Illegal move: %s\n", token);
  704.             dumpMove(move);
  705.             dumpPosition(&variation.singlePosition);
  706.             exit(-1);
  707.  
  708.             break;
  709.          }
  710.  
  711.          while (isspace((int) pgnMoveText[*offset]) == FALSE &&
  712.                 pgnMoveText[*offset] != ')')
  713.          {
  714.             (*offset)++;
  715.          }
  716.       }
  717.    }
  718.  
  719.    return baseMove;
  720. }
  721.  
  722. static Gamemove *parseMoveSection(const char *pgnMoveText,
  723.                                   const Position * position, PGNGame * game)
  724. {
  725.    size_t offset = 0;
  726.  
  727.    return parseMoveText(pgnMoveText, &offset, position, game);
  728. }
  729.  
  730. static void parseRoasterValue(const char *pgn, const char *name,
  731.                               char value[PGN_ROASTERLINE_SIZE])
  732. {
  733.    char *nameBegin, *valueBegin, *valueEnd;
  734.    char buffer[2 * PGN_ROASTERLINE_SIZE];
  735.    size_t valueLength;
  736.  
  737.    strcpy(value, "");
  738.    strcpy(buffer, "[");
  739.    strcat(buffer, name);
  740.  
  741.    if ((nameBegin = strstr(pgn, buffer)) == 0)
  742.    {
  743.       return;
  744.    }
  745.  
  746.    if ((valueBegin = strstr(nameBegin, "\"")) == 0)
  747.    {
  748.       return;
  749.    }
  750.  
  751.    if ((valueEnd = strstr(valueBegin, "\"]")) == 0)
  752.    {
  753.       return;
  754.    }
  755.  
  756.    valueLength = min(PGN_ROASTERLINE_SIZE, valueEnd - valueBegin - 1);
  757.    memcpy(value, valueBegin + 1, valueLength);
  758.    value[valueLength] = '\0';
  759. }
  760.  
  761. static void parseRoasterValues(const char *pgn, PGNGame * pgngame)
  762. {
  763.    char *moves;
  764.    Variation variation;
  765.  
  766.    parseRoasterValue(pgn, "Event", pgngame->event);
  767.    parseRoasterValue(pgn, "Site", pgngame->site);
  768.    parseRoasterValue(pgn, "Date", pgngame->date);
  769.    parseRoasterValue(pgn, "Round", pgngame->round);
  770.    parseRoasterValue(pgn, "White", pgngame->white);
  771.    parseRoasterValue(pgn, "Black", pgngame->black);
  772.    parseRoasterValue(pgn, "Result", pgngame->result);
  773.    parseRoasterValue(pgn, "SetUp", pgngame->setup);
  774.    parseRoasterValue(pgn, "FEN", pgngame->fen);
  775.    parseRoasterValue(pgn, "WhiteTitle", pgngame->whiteTitle);
  776.    parseRoasterValue(pgn, "BlackTitle", pgngame->blackTitle);
  777.    parseRoasterValue(pgn, "WhiteElo", pgngame->whiteElo);
  778.    parseRoasterValue(pgn, "BlackElo", pgngame->blackElo);
  779.    parseRoasterValue(pgn, "ECO", pgngame->eco);
  780.    parseRoasterValue(pgn, "NIC", pgngame->nic);
  781.    parseRoasterValue(pgn, "TimeControl", pgngame->timeControl);
  782.    parseRoasterValue(pgn, "Termination", pgngame->termination);
  783.    pgngame->moveText = 0;
  784.    pgngame->firstMove = 0;
  785.  
  786.    initializeVariationFromGame(&variation, pgngame);
  787.  
  788.    if ((moves = strstr(pgn, "\n\n")) == 0)
  789.    {
  790.       moves = strstr(pgn, "\r\n\r\n");
  791.    }
  792.  
  793.    if (moves != 0)
  794.    {
  795.       if ((pgngame->moveText = malloc(strlen(moves) + 1)) != 0)
  796.       {
  797.          strcpy(pgngame->moveText, moves);
  798.          trim(pgngame->moveText);
  799.  
  800.          pgngame->firstMove =
  801.             parseMoveSection(pgngame->moveText, &variation.singlePosition,
  802.                              pgngame);
  803.       }
  804.    }
  805. }
  806.  
  807. PGNGame *getGame(PGNFile * pgnfile, int number)
  808. {
  809.    PGNGame *pgngame;
  810.    char *gameText = getGameText(pgnfile, number);
  811.  
  812.    if (gameText == 0)
  813.    {
  814.       return 0;
  815.    }
  816.  
  817.    if ((pgngame = (PGNGame *) malloc(sizeof(PGNGame))) == NULL)
  818.    {
  819.       return 0;
  820.    }
  821.  
  822.    initializePGNGame(pgngame);
  823.    parseRoasterValues(gameText, pgngame);
  824.    free(gameText);
  825.  
  826.    return pgngame;
  827. }
  828.  
  829. /*
  830.  ******************************************************************************
  831.  *
  832.  *   Generator operations
  833.  *
  834.  ******************************************************************************
  835.  */
  836.  
  837. #define SAME_PIECE 1
  838. #define SAME_FILE 2
  839. #define SAME_RANK 4
  840.  
  841. static int examineAlternativeMoves(Variation * variation, const Move move)
  842. {
  843.    const Square from = getFromSquare(move);
  844.    const Square to = getToSquare(move);
  845.    const Piece newPiece = getNewPiece(move);
  846.    int result = 0;
  847.    Position *position = &variation->singlePosition;
  848.    Piece piece = position->piece[from];
  849.    Square square;
  850.    Bitboard candidates =
  851.       getDirectAttackers(position, to, position->activeColor,
  852.                          position->allPieces) & position->piecesOfType[piece];
  853.  
  854.    clearSquare(candidates, from);
  855.  
  856.    ITERATE_BITBOARD(&candidates, square)
  857.    {
  858.       const Move tmp = getPackedMove(square, to, newPiece);
  859.  
  860.       if (moveIsLegal(position, tmp))
  861.       {
  862.          result |= SAME_PIECE;
  863.  
  864.          if (file(square) == file(from))
  865.          {
  866.             result |= SAME_FILE;
  867.          }
  868.  
  869.          if (rank(square) == rank(from))
  870.          {
  871.             result |= SAME_RANK;
  872.          }
  873.       }
  874.    }
  875.  
  876.    return result;
  877. }
  878.  
  879. void generateMoveText(Variation * variation, const Move move, char *pgnMove)
  880. {
  881.    Position *position = &variation->singlePosition;
  882.    const char *castlings[] = { "O-O", "O-O-O" };
  883.    const Square from = getFromSquare(move);
  884.    const Square to = getToSquare(move);
  885.    const Piece newPiece = getNewPiece(move);
  886.  
  887.    Piece movingPiece = position->piece[from];
  888.    char pieceSign[2], origin[3], destination[6], captureSign[2];
  889.    char checkSign[2], promotionSign[3];
  890.    int ambiguityCheckResult;
  891.  
  892.    getSquareName(to, destination);
  893.    pieceSign[0] = origin[0] = captureSign[0] = checkSign[0] = 0;
  894.    promotionSign[0] = 0;
  895.  
  896.    if (!moveIsLegal(position, move))
  897.    {
  898.       getSquareName(from, origin);
  899.       sprintf(pgnMove, "(illegal move: %s-%s)", origin, destination);
  900.  
  901.       return;
  902.    }
  903.    else
  904.    {
  905.       makeMove(variation, move);
  906.  
  907.       if (activeKingIsSafe(&variation->singlePosition) == FALSE)
  908.       {
  909.          Movelist legalMoves;
  910.  
  911.          getLegalMoves(variation, &legalMoves);
  912.  
  913.          if (legalMoves.numberOfMoves == 0)
  914.          {
  915.             strcpy(checkSign, "#");
  916.          }
  917.          else
  918.          {
  919.             strcpy(checkSign, "+");
  920.          }
  921.       }
  922.  
  923.       unmakeLastMove(variation);
  924.    }
  925.  
  926.    if (pieceType(movingPiece) == PAWN)
  927.    {
  928.       if (file(from) != file(to))
  929.       {
  930.          strcpy(captureSign, "x");
  931.          sprintf(origin, "%c", fileName(file(from)));
  932.       }
  933.  
  934.       if (newPiece != NO_PIECE)
  935.       {
  936.          sprintf(promotionSign, "=%c", pieceName[newPiece]);
  937.       }
  938.    }
  939.    else
  940.    {
  941.       if (pieceType(movingPiece) == KING && _distance[from][to] > 1)
  942.       {
  943.          strcpy(destination, castlings[file(to) == FILE_G ? 0 : 1]);
  944.       }
  945.       else
  946.       {
  947.          sprintf(pieceSign, "%c", pieceName[pieceType(movingPiece)]);
  948.  
  949.          if (position->piece[to] != NO_PIECE)
  950.          {
  951.             strcpy(captureSign, "x");
  952.          }
  953.  
  954.          ambiguityCheckResult = examineAlternativeMoves(variation, move);
  955.  
  956.          if (ambiguityCheckResult & SAME_PIECE)
  957.          {
  958.             switch (ambiguityCheckResult)
  959.             {
  960.             case SAME_PIECE | SAME_FILE | SAME_RANK:
  961.                getSquareName(from, origin);
  962.                break;
  963.  
  964.             case SAME_PIECE | SAME_FILE:
  965.                sprintf(origin, "%c", rankName(rank(from)));
  966.                break;
  967.  
  968.             default:
  969.                sprintf(origin, "%c", fileName(file(from)));
  970.             }
  971.          }
  972.       }
  973.    }
  974.  
  975.    sprintf(pgnMove, "%s%s%s%s%s%s", pieceSign, origin, captureSign,
  976.            destination, promotionSign, checkSign);
  977. }
  978.  
  979. static char *generateMoveSection(Gamemove * gamemove, const char *result)
  980. {
  981.    String moveText = getEmptyString();
  982.    char moveBuffer[64], *temp;
  983.    Variation variation;
  984.    bool restart = TRUE;
  985.  
  986.    initializeVariation(&variation, FEN_GAMESTART);
  987.  
  988.    while (gamemove != 0)
  989.    {
  990.       if (restart == TRUE && gamemove->position.activeColor == BLACK)
  991.       {
  992.          appendToString(&moveText, " %d...", gamemove->position.moveNumber);
  993.       }
  994.  
  995.       restart = FALSE;
  996.  
  997.       setBasePosition(&variation, &gamemove->position);
  998.       generateMoveText(&variation,
  999.                        getPackedMove(gamemove->from, gamemove->to,
  1000.                                      gamemove->newPiece), moveBuffer);
  1001.  
  1002.       if (gamemove->position.activeColor == WHITE)
  1003.       {
  1004.          appendToString(&moveText, " %d. %s", gamemove->position.moveNumber,
  1005.                         moveBuffer);
  1006.       }
  1007.       else
  1008.       {
  1009.          appendToString(&moveText, " %s", moveBuffer);
  1010.       }
  1011.  
  1012.       if (gamemove->glyphs != 0)
  1013.       {
  1014.          appendToString(&moveText, " %s", gamemove->glyphs);
  1015.       }
  1016.  
  1017.       if (gamemove->comment != 0)
  1018.       {
  1019.          appendToString(&moveText, " {%s}", gamemove->comment);
  1020.       }
  1021.  
  1022.       if (gamemove->alternativeMove != 0)
  1023.       {
  1024.          temp = generateMoveSection(gamemove->alternativeMove, "");
  1025.          appendToString(&moveText, " (%s)", temp);
  1026.          free(temp);
  1027.  
  1028.          restart = TRUE;
  1029.       }
  1030.  
  1031.       gamemove = gamemove->nextMove;
  1032.    }
  1033.  
  1034.    if (result[0] != '\0')
  1035.    {
  1036.       appendToString(&moveText, " %s", result);
  1037.    }
  1038.  
  1039.    trim(moveText.buffer);
  1040.    breakLines(moveText.buffer, 79);
  1041.  
  1042.    return moveText.buffer;
  1043. }
  1044.  
  1045. static char *generateRoasterLine(const char *tag, const char *tagValue,
  1046.                                  char buffer[PGN_ROASTERLINE_SIZE])
  1047. {
  1048.    if (tagValue[0] != '\0')
  1049.    {
  1050.       sprintf(buffer, "[%s \"%s\"]\n", tag, tagValue);
  1051.    }
  1052.    else
  1053.    {
  1054.       buffer[0] = '\0';
  1055.    }
  1056.  
  1057.    return buffer;
  1058. }
  1059.  
  1060. static char *generateRoaster(const PGNGame * pgngame)
  1061. {
  1062.    char tagbuffer[PGN_ROASTERLINE_SIZE];
  1063.    String string = getEmptyString();
  1064.  
  1065.    appendToString(&string,
  1066.                   generateRoasterLine("Event", pgngame->event, tagbuffer));
  1067.    appendToString(&string,
  1068.                   generateRoasterLine("Site", pgngame->site, tagbuffer));
  1069.    appendToString(&string,
  1070.                   generateRoasterLine("Date", pgngame->date, tagbuffer));
  1071.    appendToString(&string,
  1072.                   generateRoasterLine("Round", pgngame->round, tagbuffer));
  1073.    appendToString(&string,
  1074.                   generateRoasterLine("White", pgngame->white, tagbuffer));
  1075.    appendToString(&string,
  1076.                   generateRoasterLine("Black", pgngame->black, tagbuffer));
  1077.    appendToString(&string,
  1078.                   generateRoasterLine("Result", pgngame->result, tagbuffer));
  1079.    appendToString(&string,
  1080.                   generateRoasterLine("SetUp", pgngame->setup, tagbuffer));
  1081.    appendToString(&string,
  1082.                   generateRoasterLine("FEN", pgngame->fen, tagbuffer));
  1083.    appendToString(&string,
  1084.                   generateRoasterLine("WhiteTitle", pgngame->whiteTitle,
  1085.                                       tagbuffer));
  1086.    appendToString(&string,
  1087.                   generateRoasterLine("BlackTitle", pgngame->blackTitle,
  1088.                                       tagbuffer));
  1089.    appendToString(&string,
  1090.                   generateRoasterLine("WhiteElo", pgngame->whiteElo,
  1091.                                       tagbuffer));
  1092.    appendToString(&string,
  1093.                   generateRoasterLine("BlackElo", pgngame->blackElo,
  1094.                                       tagbuffer));
  1095.    appendToString(&string,
  1096.                   generateRoasterLine("ECO", pgngame->eco, tagbuffer));
  1097.    appendToString(&string,
  1098.                   generateRoasterLine("NIC", pgngame->nic, tagbuffer));
  1099.    appendToString(&string,
  1100.                   generateRoasterLine("TimeControl", pgngame->timeControl,
  1101.                                       tagbuffer));
  1102.    appendToString(&string,
  1103.                   generateRoasterLine("Termination", pgngame->termination,
  1104.                                       tagbuffer));
  1105.  
  1106.    return string.buffer;
  1107. }
  1108.  
  1109. char *generatePgn(PGNGame * game)
  1110. {
  1111.    String string = getEmptyString();
  1112.    char *roaster = generateRoaster(game);
  1113.    char *moveText = generateMoveSection(game->firstMove, game->result);
  1114.  
  1115.    appendToString(&string, "%s\n%s", roaster, moveText);
  1116.    free(roaster);
  1117.    free(moveText);
  1118.  
  1119.    return string.buffer;
  1120. }
  1121.  
  1122. void initializeVariationFromGame(Variation * variation, PGNGame * game)
  1123. {
  1124.    if (game->lastMove != 0)
  1125.    {
  1126.       Gamemove *currentMove = game->lastMove;
  1127.       int i = POSITION_HISTORY_OFFSET - 1;
  1128.  
  1129.       setBasePosition(variation, &(game->lastMove->position));
  1130.       makeMove(variation, gameMove2Move(game->lastMove));
  1131.  
  1132.       if (variation->singlePosition.activeColor == WHITE)
  1133.       {
  1134.          variation->singlePosition.moveNumber++;
  1135.       }
  1136.  
  1137.       setBasePosition(variation, &variation->singlePosition);
  1138.       prepareSearch(variation);
  1139.  
  1140.       while (i >= 0 && currentMove != 0)
  1141.       {
  1142.          variation->positionHistory[i--] = currentMove->position.hashKey;
  1143.          currentMove = currentMove->previousMove;
  1144.       }
  1145.    }
  1146.    else
  1147.    {
  1148.       if (strcmp(game->setup, "") != 0)
  1149.       {
  1150.          initializeVariation(variation, game->fen);
  1151.       }
  1152.       else
  1153.       {
  1154.          initializeVariation(variation, FEN_GAMESTART);
  1155.       }
  1156.    }
  1157. }
  1158.  
  1159. void initializeGameFromVariation(const Variation * variation, PGNGame * game,
  1160.                                  bool copyPv)
  1161. {
  1162.    const PrincipalVariation *pv = &variation->pv[0];
  1163.  
  1164.    resetPGNGame(game);
  1165.    getFen(&variation->startPosition, game->fen);
  1166.    strcpy(game->setup, "1");
  1167.  
  1168.    if (copyPv != FALSE)
  1169.    {
  1170.       int i = 0;
  1171.  
  1172.       for (i = 0; i < min(8, pv->length); i++)
  1173.       {
  1174.          Move move = (Move) pv->move[i];
  1175.  
  1176.          if (appendMove(game, move) != 0)
  1177.          {
  1178.             break;
  1179.          }
  1180.       }
  1181.    }
  1182. }
  1183.  
  1184. char *getPrincipalVariation(const Variation * variation)
  1185. {
  1186.    PGNGame game;
  1187.    char *pv;
  1188.  
  1189.    initializePGNGame(&game);
  1190.    initializeGameFromVariation(variation, &game, TRUE);
  1191.    pv = generateMoveSection(game.firstMove, "");
  1192.    trim(pv);
  1193.    resetPGNGame(&game);
  1194.  
  1195.    return pv;
  1196. }
  1197.  
  1198. /*
  1199.  ******************************************************************************
  1200.  *
  1201.  *   Module initialization and testing
  1202.  *
  1203.  ******************************************************************************
  1204.  */
  1205.  
  1206. int initializeModulePgn()
  1207. {
  1208.    pieceName[WHITE_KING] = 'K';
  1209.    pieceName[WHITE_QUEEN] = 'Q';
  1210.    pieceName[WHITE_ROOK] = 'R';
  1211.    pieceName[WHITE_BISHOP] = 'B';
  1212.    pieceName[WHITE_KNIGHT] = 'N';
  1213.    pieceName[WHITE_PAWN] = 'P';
  1214.    pieceName[BLACK_KING] = 'K';
  1215.    pieceName[BLACK_QUEEN] = 'Q';
  1216.    pieceName[BLACK_ROOK] = 'R';
  1217.    pieceName[BLACK_BISHOP] = 'B';
  1218.    pieceName[BLACK_KNIGHT] = 'N';
  1219.    pieceName[BLACK_PAWN] = 'P';
  1220.  
  1221.    return 0;
  1222. }
  1223.  
  1224. static int testGeneration()
  1225. {
  1226.    char *p1 = "r2qkb1r/ppp2ppp/3p4/5b2/3PN3/n4N2/PPP1QPPP/R3K2R w KQkq - 0 1";
  1227.    char *p2 = "r3k2r/n3nppp/3n2P1/8/1q1q4/2B5/1q1pRPPP/5RK1 b kq - 0 1";
  1228.    Variation variation;
  1229.    char pgnMove[64];
  1230.  
  1231.    initializeVariation(&variation, p1);
  1232.  
  1233.    generateMoveText(&variation, getPackedMove(D4, D5, NO_PIECE), pgnMove);
  1234.    assert(strcmp("d5", pgnMove) == 0);
  1235.  
  1236.    generateMoveText(&variation, getPackedMove(E1, G1, NO_PIECE), pgnMove);
  1237.    assert(strcmp("O-O", pgnMove) == 0);
  1238.  
  1239.    generateMoveText(&variation, getPackedMove(E1, C1, NO_PIECE), pgnMove);
  1240.    assert(strcmp("O-O-O", pgnMove) == 0);
  1241.  
  1242.    generateMoveText(&variation, getPackedMove(E4, D6, NO_PIECE), pgnMove);
  1243.    assert(strcmp("Nxd6+", pgnMove) == 0);
  1244.  
  1245.    generateMoveText(&variation, getPackedMove(E4, F6, NO_PIECE), pgnMove);
  1246.    assert(strcmp("Nf6#", pgnMove) == 0);
  1247.  
  1248.    generateMoveText(&variation, getPackedMove(F3, G5, NO_PIECE), pgnMove);
  1249.    assert(strcmp("Nfg5", pgnMove) == 0);
  1250.  
  1251.    generateMoveText(&variation, getPackedMove(E4, G5, NO_PIECE), pgnMove);
  1252.    assert(strcmp("Neg5+", pgnMove) == 0);
  1253.  
  1254.    generateMoveText(&variation, getPackedMove(B2, A3, NO_PIECE), pgnMove);
  1255.    assert(strcmp("bxa3", pgnMove) == 0);
  1256.  
  1257.    initializeVariation(&variation, p2);
  1258.  
  1259.    generateMoveText(&variation, getPackedMove(E7, F5, NO_PIECE), pgnMove);
  1260.    assert(strcmp("(illegal move: e7-f5)", pgnMove) == 0);
  1261.  
  1262.    generateMoveText(&variation, getPackedMove(D2, D1, WHITE_BISHOP), pgnMove);
  1263.    assert(strcmp("d1=B", pgnMove) == 0);
  1264.  
  1265.    generateMoveText(&variation, getPackedMove(B4, C3, NO_PIECE), pgnMove);
  1266.    assert(strcmp("Qb4xc3", pgnMove) == 0);
  1267.  
  1268.    generateMoveText(&variation, getPackedMove(B2, C3, NO_PIECE), pgnMove);
  1269.    assert(strcmp("Q2xc3", pgnMove) == 0);
  1270.  
  1271.    generateMoveText(&variation, getPackedMove(D4, C3, NO_PIECE), pgnMove);
  1272.    assert(strcmp("Qdxc3", pgnMove) == 0);
  1273.  
  1274.    generateMoveText(&variation, getPackedMove(D6, F5, NO_PIECE), pgnMove);
  1275.    assert(strcmp("Nf5", pgnMove) == 0);
  1276.  
  1277.    generateMoveText(&variation, getPackedMove(D6, C8, NO_PIECE), pgnMove);
  1278.    assert(strcmp("Ndc8", pgnMove) == 0);
  1279.  
  1280.    return 0;
  1281. }
  1282.  
  1283. static int testParsing()
  1284. {
  1285.    char *p1 = "r2qkb1r/ppp2ppp/3p4/5b2/3PN3/n4N2/PPP1QPPP/R3K2R w KQkq - 0 1";
  1286.    char *p2 = "r3k2r/n3nppp/3n2P1/8/1q1q4/2B5/1q1pRPPP/5RK1 b kq - 0 1";
  1287.    Variation variation;
  1288.  
  1289.    initializeVariation(&variation, p1);
  1290.  
  1291.    assert(parsePGNMove("d5", &variation.singlePosition) ==
  1292.           getPackedMove(D4, D5, NO_PIECE));
  1293.  
  1294.    assert(parsePGNMove("b4", &variation.singlePosition) ==
  1295.           getPackedMove(B2, B4, NO_PIECE));
  1296.  
  1297.    assert(parsePGNMove("bxa3", &variation.singlePosition) ==
  1298.           getPackedMove(B2, A3, NO_PIECE));
  1299.  
  1300.    assert(parsePGNMove("O-O", &variation.singlePosition) ==
  1301.           getPackedMove(E1, G1, NO_PIECE));
  1302.  
  1303.    assert(parsePGNMove("O-O-O", &variation.singlePosition) ==
  1304.           getPackedMove(E1, C1, NO_PIECE));
  1305.  
  1306.    assert(parsePGNMove("Ng3+", &variation.singlePosition) ==
  1307.           getPackedMove(E4, G3, NO_PIECE));
  1308.  
  1309.    assert(parsePGNMove("Nf6#", &variation.singlePosition) ==
  1310.           getPackedMove(E4, F6, NO_PIECE));
  1311.  
  1312.    assert(parsePGNMove("Nfg5", &variation.singlePosition) ==
  1313.           getPackedMove(F3, G5, NO_PIECE));
  1314.  
  1315.    initializeVariation(&variation, p2);
  1316.  
  1317.    assert(parsePGNMove("h6", &variation.singlePosition) ==
  1318.           getPackedMove(H7, H6, NO_PIECE));
  1319.  
  1320.    assert(parsePGNMove("f5", &variation.singlePosition) ==
  1321.           getPackedMove(F7, F5, NO_PIECE));
  1322.  
  1323.    assert(parsePGNMove("fxg6", &variation.singlePosition) ==
  1324.           getPackedMove(F7, G6, NO_PIECE));
  1325.  
  1326.    assert(parsePGNMove("d1=B", &variation.singlePosition) ==
  1327.           getPackedMove(D2, D1, BISHOP));
  1328.  
  1329.    assert(parsePGNMove("O-O", &variation.singlePosition) ==
  1330.           getPackedMove(E8, G8, NO_PIECE));
  1331.  
  1332.    assert(parsePGNMove("O-O-O", &variation.singlePosition) ==
  1333.           getPackedMove(E8, C8, NO_PIECE));
  1334.  
  1335.    assert(parsePGNMove("Nf5", &variation.singlePosition) ==
  1336.           getPackedMove(D6, F5, NO_PIECE));
  1337.  
  1338.    assert(parsePGNMove("Q2xc3", &variation.singlePosition) ==
  1339.           getPackedMove(B2, C3, NO_PIECE));
  1340.  
  1341.    assert(parsePGNMove("Qdxc3", &variation.singlePosition) ==
  1342.           getPackedMove(D4, C3, NO_PIECE));
  1343.  
  1344.    assert(parsePGNMove("Qb4xc3", &variation.singlePosition) ==
  1345.           getPackedMove(B4, C3, NO_PIECE));
  1346.  
  1347.    return 0;
  1348. }
  1349.  
  1350. static int testLoading()
  1351. {
  1352.    PGNFile pgnfile;
  1353.    PGNGame *game, game2;
  1354.    char *gameText, *generatedGameText;
  1355.  
  1356.    pgnfile.numGames = 0;
  1357.    pgnfile.index = 0;
  1358.    pgnfile.file = 0;
  1359.  
  1360.    assert(openPGNFile(&pgnfile, "test.pgn") == 0);
  1361.    game = getGame(&pgnfile, 1);
  1362.    assert(game != 0);
  1363.  
  1364.    gameText = generatePgn(game);
  1365.    initializePGNGame(&game2);
  1366.    parseRoasterValues(gameText, &game2);
  1367.    generatedGameText = generatePgn(&game2);
  1368.    /* logDebug("\n>%s<\n", generatedGameText); */
  1369.    assert(strcmp(gameText, generatedGameText) == 0);
  1370.  
  1371.    free(generatedGameText);
  1372.    freePgnGame(game);
  1373.    resetPGNGame(&game2);
  1374.    free(gameText);
  1375.    closePGNFile(&pgnfile);
  1376.  
  1377.    return 0;
  1378. }
  1379.  
  1380. int testModulePgn()
  1381. {
  1382.    int result;
  1383.  
  1384.    if ((result = testLoading()) != 0)
  1385.    {
  1386.       return result;
  1387.    }
  1388.  
  1389.    if ((result = testGeneration()) != 0)
  1390.    {
  1391.       return result;
  1392.    }
  1393.  
  1394.    if ((result = testParsing()) != 0)
  1395.    {
  1396.       return result;
  1397.    }
  1398.  
  1399.    return 0;
  1400. }
  1401.