/*
Protector -- a UCI chess engine
Copyright (C) 2009-2010 Raimund Heid (Raimund_Heid@yahoo.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "io.h"
#include "pgn.h"
#include "bitboard.h"
#include "position.h"
#include "movegeneration.h"
#include "fen.h"
#define BUFSIZE 8192
#define INCREMENT 1024
#define STATE_INIT 0
#define STATE_1 1
#define STATE_2 2
#define STATE_IGNORE 3
static char pieceName[16];
/*
******************************************************************************
*
* File operations
*
******************************************************************************
*/
static int scanForIndex(PGNFile * pgnfile, int state, char buffer[],
size_t bufsize, size_t offset)
{
size_t i = 0;
while (i < bufsize)
{
switch (buffer[i++])
{
case '\n':
if (state < STATE_IGNORE)
{
state++;
}
break;
case '\r':
case ' ':
continue;
case '{':
if (state < STATE_IGNORE)
{
state = STATE_IGNORE;
}
else
{
state++;
}
break;
case '}':
if (state > STATE_IGNORE)
{
state--;
}
else
{
state = 0;
}
break;
case '[':
if (state == 2)
{
if (pgnfile->numGames + 1 == pgnfile->indexSize)
{
pgnfile->indexSize += INCREMENT;
pgnfile
->index
= (long *) realloc(pgnfile
->index
,
pgnfile->indexSize *
sizeof(long));
}
pgnfile->index[pgnfile->numGames++] = (long) (i + offset - 1L);
assert(pgnfile
->numGames
< pgnfile
->indexSize
);
}
break;
default:
if (state != STATE_IGNORE)
{
state = 0;
}
}
}
return state;
}
static void buildIndex(PGNFile * pgnfile)
{
char buffer[BUFSIZE];
int state = STATE_2;
size_t numRead;
do
{
long pos
= ftell(pgnfile
->file
);
numRead
= fread(buffer
, 1, BUFSIZE
, pgnfile
->file
);
state = scanForIndex(pgnfile, state, buffer, numRead, pos);
}
while (numRead == BUFSIZE);
fseek(pgnfile
->file
, 0, SEEK_END
);
pgnfile
->index
[pgnfile
->numGames
] = ftell(pgnfile
->file
) + 1;
}
int openPGNFile(PGNFile * pgnfile, const char *filename)
{
pgnfile
->index
= (long *) malloc(INCREMENT
* sizeof(long));
pgnfile->indexSize = INCREMENT;
pgnfile->numGames = 0;
pgnfile
->file
= fopen(filename
, "rb");
if (pgnfile->index != 0 && pgnfile->file != 0)
{
buildIndex(pgnfile);
return 0;
}
else
{
return -1;
}
}
void closePGNFile(PGNFile * pgnfile)
{
if (pgnfile->index != 0)
{
}
pgnfile->indexSize = 0;
pgnfile->numGames = 0;
if (pgnfile->file != 0)
{
pgnfile->file = 0;
}
}
static char *getGameText(PGNFile * pgnfile, int number)
{
long start, end, length;
char *buffer;
if (number < 1 || number > pgnfile->numGames)
{
return 0;
}
start = pgnfile->index[number - 1];
end = pgnfile->index[number] - 1;
length = (int) (end - start);
if ((buffer
= malloc(length
+ 1)) == NULL
)
{
return 0;
}
fseek(pgnfile
->file
, start
, SEEK_SET
);
fread(buffer
, 1, length
, pgnfile
->file
);
buffer[length] = '\0';
trim(buffer);
return buffer;
}
/*
******************************************************************************
*
* Object generation and destruction
*
******************************************************************************
*/
static Gamemove *getGamemove(const Position * position,
Gamemove * previousMove, PGNGame * game)
{
Gamemove *new = &(game->moveHeap[game->nextMoveFromHeap++]);
new->from = NO_SQUARE;
new->to = NO_SQUARE;
new->newPiece = NO_PIECE;
new->position = *position;
new->previousMove = previousMove;
new->nextMove = new->alternativeMove = 0;
new->comment = new->glyphs = 0;
return new;
}
Move gameMove2Move(const Gamemove * gamemove)
{
return getPackedMove(gamemove->from, gamemove->to, gamemove->newPiece);
}
void initializePGNGame(PGNGame * game)
{
game->setup[0] = game->fen[0] = '\0';
game->moveText = 0;
game->firstMove = game->lastMove = 0;
game->nextMoveFromHeap = 0;
}
void resetPGNGame(PGNGame * game)
{
if (game->moveText != 0)
{
}
initializePGNGame(game);
}
void freePgnGame(PGNGame * game)
{
resetPGNGame(game);
}
/*
******************************************************************************
*
* Parser operations
*
******************************************************************************
*/
static Move parsePawnMove(const Position * position, const char *moveText)
{
Square from = NO_SQUARE, to = NO_SQUARE;
Piece newPiece = NO_PIECE;
int pawnStep = (position->activeColor == WHITE ? 8 : -8);
size_t textLength
= strlen(moveText
);
if (textLength < 2)
{
return NO_MOVE;
}
/*
* capture or push?
*/
if (textLength >= 4 && moveText[1] == 'x')
{
to = getSquare(moveText[2] - 'a', moveText[3] - '1');
from = (Square)
(getSquare(moveText[0] - 'a', moveText[3] - '1') - pawnStep);
}
else
{
to = getSquare(moveText[0] - 'a', moveText[1] - '1');
from = (Square) (to - pawnStep);
if (position->piece[from] == NO_PIECE)
{
from = (Square) (from - pawnStep);
}
}
/*
* promotion?
*/
if (textLength >= 4 && moveText[textLength - 2] == '=')
{
switch (moveText[textLength - 1])
{
case 'R':
newPiece = WHITE_ROOK;
break;
case 'B':
newPiece = WHITE_BISHOP;
break;
case 'N':
newPiece = WHITE_KNIGHT;
break;
default:
newPiece = WHITE_QUEEN;
}
}
return getPackedMove(from, to, newPiece);
}
static Move parseCastlingMove(const Position * position, const char *moveText)
{
const Rank rank = (position->activeColor == WHITE ? RANK_1 : RANK_8);
const File file
= (strcmp(moveText
, "O-O") == 0 ? FILE_G
: FILE_C
);
return getPackedMove(getSquare(FILE_E, rank), getSquare(file, rank),
NO_PIECE);
}
static Move parsePieceMove(const Position * position,
const char *moveText, const PieceType pieceType)
{
Square from = NO_SQUARE, to = NO_SQUARE;
Piece newPiece = NO_PIECE;
size_t textLength
= strlen(moveText
);
int fromFile = -1, fromRank = -1;
size_t pointer = 1;
char currentChar;
Bitboard candidates;
if (textLength < 3)
{
return NO_MOVE;
}
to = getSquare(moveText[textLength - 2] - 'a',
moveText[textLength - 1] - '1');
while (pointer < textLength - 2)
{
currentChar = moveText[pointer++];
if (currentChar >= 'a' && currentChar <= 'h')
{
fromFile = currentChar - 'a';
}
if (currentChar >= '1' && currentChar <= '8')
{
fromRank = currentChar - '1';
}
}
if (fromFile != -1 && fromRank != -1)
{
from = getSquare(fromFile, fromRank);
}
else
{
candidates =
getDirectAttackers(position, to, position->activeColor,
position->allPieces) &
position->piecesOfType[pieceType | position->activeColor];
if (fromFile != -1)
{
candidates &= getSquaresOfFile((File) fromFile);
}
else if (fromRank != -1)
{
candidates &= getSquaresOfRank((Rank) fromRank);
}
if (candidates == EMPTY_BITBOARD)
{
return NO_MOVE;
}
from = getLastSquare(&candidates);
while (candidates != EMPTY_BITBOARD)
{
const Move move = getPackedMove(from, to, newPiece);
if (moveIsLegal(position, move))
{
return move;
}
from = getLastSquare(&candidates);
}
}
return getPackedMove(from, to, newPiece);
}
static Move parsePGNMove(const char *moveText, Position * position)
{
char moveTextBuffer[16];
int i = 0;
while (i < 15 && moveText[i] != 0 && moveText[i] != '+' &&
moveText[i] != '#' && moveText[i] != '!' &&
moveText[i] != '?' && moveText[i] != ')' &&
{
moveTextBuffer[i] = moveText[i];
i++;
}
moveTextBuffer[i] = 0;
switch (moveTextBuffer[0])
{
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
return parsePawnMove(position, moveTextBuffer);
break;
case 'K':
return parsePieceMove(position, moveTextBuffer, KING);
break;
case 'Q':
return parsePieceMove(position, moveTextBuffer, QUEEN);
break;
case 'R':
return parsePieceMove(position, moveTextBuffer, ROOK);
break;
case 'B':
return parsePieceMove(position, moveTextBuffer, BISHOP);
break;
case 'N':
return parsePieceMove(position, moveTextBuffer, KNIGHT);
break;
case 'O':
return parseCastlingMove(position, moveTextBuffer);
break;
default:
return NO_MOVE;
}
}
Move interpretPGNMove(const char *moveText, PGNGame * game)
{
Variation variation;
initializeVariationFromGame(&variation, game);
return parsePGNMove(moveText, &variation.singlePosition);
}
int appendMove(PGNGame * game, const Move move)
{
Variation variation;
initializeVariationFromGame(&variation, game);
if (moveIsLegal(&variation.singlePosition, move))
{
Gamemove *newMove =
getGamemove(&variation.singlePosition, game->lastMove, game);
newMove->from = getFromSquare(move);
newMove->to = getToSquare(move);
newMove->newPiece = getNewPiece(move);
if (game->lastMove == 0)
{
game->firstMove = game->lastMove = newMove;
}
else
{
game->lastMove->nextMove = newMove;
game->lastMove = newMove;
}
return 0;
}
else
{
return -1;
}
}
void takebackLastMove(PGNGame * game)
{
if (game->lastMove != 0)
{
if (game->lastMove == game->firstMove)
{
game->lastMove = 0;
}
else
{
game->lastMove = game->lastMove->previousMove;
}
}
}
static void addAlternativeMove(Gamemove * move, Gamemove * alternative)
{
if (move->alternativeMove != 0)
{
addAlternativeMove(move->alternativeMove, alternative);
}
else
{
move->alternativeMove = alternative;
alternative->previousMove = move->previousMove;
}
}
static Gamemove *parseMoveText(const char *pgnMoveText,
size_t * offset, const Position * position,
PGNGame * game)
{
bool variationTerminated = FALSE;
Gamemove *baseMove = 0, *lastMove = 0;
Variation variation;
char currentChar;
const char *token;
const bool debugOutput = FALSE;
setBasePosition(&variation, position);
prepareSearch(&variation);
if (debugOutput)
{
logDebug("Starting pgn parsing at: >%s<\n", &pgnMoveText[*offset]);
}
while (variationTerminated == FALSE &&
(currentChar = pgnMoveText[(*offset)++]) != '\0')
{
if (strchr(" \r\n0123456789.", currentChar
) != 0)
{
continue;
}
token = &(pgnMoveText[*offset - 1]);
if (debugOutput)
{
logDebug("\nCurrent token: >%s<\n", token);
}
if (currentChar == '{')
{
char *comment = getToken(token + 1, "}");
if (debugOutput)
{
logDebug("Starting annotation: >%s<\n", comment);
}
if (lastMove != 0)
{
lastMove->comment = comment;
}
else
{
}
continue;
}
if (currentChar == '(')
{
if (debugOutput)
{
logDebug("Starting subvariation.\n");
}
if (lastMove != 0)
{
addAlternativeMove(lastMove,
parseMoveText(pgnMoveText, offset,
&lastMove->position, game));
}
}
if (currentChar == ')')
{
variationTerminated = TRUE;
if (debugOutput)
{
logDebug("Subvariation terminated.\n");
}
}
if (currentChar == '$')
{
char *glyph = getToken(token, " )\r\n");
if (lastMove != 0)
{
if (lastMove->glyphs != 0)
{
if (debugOutput)
{
logDebug("Existing glyphs: >%s<\n", lastMove->glyphs);
}
lastMove
->glyphs
= realloc(lastMove
->glyphs
,
strcat(lastMove
->glyphs
, " ");
strcat(lastMove
->glyphs
, glyph
);
}
else
{
if (debugOutput)
{
logDebug("Initializing glyphs...\n");
}
lastMove->glyphs = glyph;
}
if (debugOutput)
{
logDebug("glyphs: >%s<\n", lastMove->glyphs);
}
}
else
{
}
}
if (strchr("KQRBNOabcdefgh", currentChar
) != 0)
{
Move move;
/*dumpPosition(variation->currentPosition); */
if (debugOutput)
{
logDebug("Move token: >%s<\n", token);
}
move = parsePGNMove(token, &variation.singlePosition);
if (moveIsLegal(&variation.singlePosition, move))
{
Gamemove *newMove =
getGamemove(&variation.singlePosition, lastMove, game);
newMove->from = getFromSquare(move);
newMove->to = getToSquare(move);
newMove->newPiece = getNewPiece(move);
if (lastMove == 0)
{
baseMove = newMove;
}
else
{
lastMove->nextMove = newMove;
}
lastMove = newMove;
makeMove(&variation, move);
setBasePosition(&variation, &variation.singlePosition);
}
else
{
logDebug("### Illegal move: %s\n", token);
dumpMove(move);
dumpPosition(&variation.singlePosition);
break;
}
while (isspace((int) pgnMoveText
[*offset
]) == FALSE
&&
pgnMoveText[*offset] != ')')
{
(*offset)++;
}
}
}
return baseMove;
}
static Gamemove *parseMoveSection(const char *pgnMoveText,
const Position * position, PGNGame * game)
{
size_t offset = 0;
return parseMoveText(pgnMoveText, &offset, position, game);
}
static void parseRoasterValue(const char *pgn, const char *name,
char value[PGN_ROASTERLINE_SIZE])
{
char *nameBegin, *valueBegin, *valueEnd;
char buffer[2 * PGN_ROASTERLINE_SIZE];
size_t valueLength;
if ((nameBegin
= strstr(pgn
, buffer
)) == 0)
{
return;
}
if ((valueBegin
= strstr(nameBegin
, "\"")) == 0)
{
return;
}
if ((valueEnd
= strstr(valueBegin
, "\"]")) == 0)
{
return;
}
valueLength = min(PGN_ROASTERLINE_SIZE, valueEnd - valueBegin - 1);
memcpy(value
, valueBegin
+ 1, valueLength
);
value[valueLength] = '\0';
}
static void parseRoasterValues(const char *pgn, PGNGame * pgngame)
{
char *moves;
Variation variation;
parseRoasterValue(pgn, "Event", pgngame->event);
parseRoasterValue(pgn, "Site", pgngame->site);
parseRoasterValue(pgn, "Date", pgngame->date);
parseRoasterValue(pgn, "Round", pgngame->round);
parseRoasterValue(pgn, "White", pgngame->white);
parseRoasterValue(pgn, "Black", pgngame->black);
parseRoasterValue(pgn, "Result", pgngame->result);
parseRoasterValue(pgn, "SetUp", pgngame->setup);
parseRoasterValue(pgn, "FEN", pgngame->fen);
parseRoasterValue(pgn, "WhiteTitle", pgngame->whiteTitle);
parseRoasterValue(pgn, "BlackTitle", pgngame->blackTitle);
parseRoasterValue(pgn, "WhiteElo", pgngame->whiteElo);
parseRoasterValue(pgn, "BlackElo", pgngame->blackElo);
parseRoasterValue(pgn, "ECO", pgngame->eco);
parseRoasterValue(pgn, "NIC", pgngame->nic);
parseRoasterValue(pgn, "TimeControl", pgngame->timeControl);
parseRoasterValue(pgn, "Termination", pgngame->termination);
pgngame->moveText = 0;
pgngame->firstMove = 0;
initializeVariationFromGame(&variation, pgngame);
if ((moves
= strstr(pgn
, "\n\n")) == 0)
{
moves
= strstr(pgn
, "\r\n\r\n");
}
if (moves != 0)
{
{
strcpy(pgngame
->moveText
, moves
);
trim(pgngame->moveText);
pgngame->firstMove =
parseMoveSection(pgngame->moveText, &variation.singlePosition,
pgngame);
}
}
}
PGNGame *getGame(PGNFile * pgnfile, int number)
{
PGNGame *pgngame;
char *gameText = getGameText(pgnfile, number);
if (gameText == 0)
{
return 0;
}
if ((pgngame
= (PGNGame
*) malloc(sizeof(PGNGame
))) == NULL
)
{
return 0;
}
initializePGNGame(pgngame);
parseRoasterValues(gameText, pgngame);
return pgngame;
}
/*
******************************************************************************
*
* Generator operations
*
******************************************************************************
*/
#define SAME_PIECE 1
#define SAME_FILE 2
#define SAME_RANK 4
static int examineAlternativeMoves(Variation * variation, const Move move)
{
const Square from = getFromSquare(move);
const Square to = getToSquare(move);
const Piece newPiece = getNewPiece(move);
int result = 0;
Position *position = &variation->singlePosition;
Piece piece = position->piece[from];
Square square;
Bitboard candidates =
getDirectAttackers(position, to, position->activeColor,
position->allPieces) & position->piecesOfType[piece];
clearSquare(candidates, from);
ITERATE_BITBOARD(&candidates, square)
{
const Move tmp = getPackedMove(square, to, newPiece);
if (moveIsLegal(position, tmp))
{
result |= SAME_PIECE;
if (file(square) == file(from))
{
result |= SAME_FILE;
}
if (rank(square) == rank(from))
{
result |= SAME_RANK;
}
}
}
return result;
}
void generateMoveText(Variation * variation, const Move move, char *pgnMove)
{
Position *position = &variation->singlePosition;
const char *castlings[] = { "O-O", "O-O-O" };
const Square from = getFromSquare(move);
const Square to = getToSquare(move);
const Piece newPiece = getNewPiece(move);
Piece movingPiece = position->piece[from];
char pieceSign[2], origin[3], destination[6], captureSign[2];
char checkSign[2], promotionSign[3];
int ambiguityCheckResult;
getSquareName(to, destination);
pieceSign[0] = origin[0] = captureSign[0] = checkSign[0] = 0;
promotionSign[0] = 0;
if (!moveIsLegal(position, move))
{
getSquareName(from, origin);
sprintf(pgnMove
, "(illegal move: %s-%s)", origin
, destination
);
return;
}
else
{
makeMove(variation, move);
if (activeKingIsSafe(&variation->singlePosition) == FALSE)
{
Movelist legalMoves;
getLegalMoves(variation, &legalMoves);
if (legalMoves.numberOfMoves == 0)
{
}
else
{
}
}
unmakeLastMove(variation);
}
if (pieceType(movingPiece) == PAWN)
{
if (file(from) != file(to))
{
sprintf(origin
, "%c", fileName
(file
(from
)));
}
if (newPiece != NO_PIECE)
{
sprintf(promotionSign
, "=%c", pieceName
[newPiece
]);
}
}
else
{
if (pieceType(movingPiece) == KING && _distance[from][to] > 1)
{
strcpy(destination
, castlings
[file
(to
) == FILE_G
? 0 : 1]);
}
else
{
sprintf(pieceSign
, "%c", pieceName
[pieceType
(movingPiece
)]);
if (position->piece[to] != NO_PIECE)
{
}
ambiguityCheckResult = examineAlternativeMoves(variation, move);
if (ambiguityCheckResult & SAME_PIECE)
{
switch (ambiguityCheckResult)
{
case SAME_PIECE | SAME_FILE | SAME_RANK:
getSquareName(from, origin);
break;
case SAME_PIECE | SAME_FILE:
sprintf(origin
, "%c", rankName
(rank
(from
)));
break;
default:
sprintf(origin
, "%c", fileName
(file
(from
)));
}
}
}
}
sprintf(pgnMove
, "%s%s%s%s%s%s", pieceSign
, origin
, captureSign
,
destination, promotionSign, checkSign);
}
static char *generateMoveSection(Gamemove * gamemove, const char *result)
{
String moveText = getEmptyString();
char moveBuffer[64], *temp;
Variation variation;
bool restart = TRUE;
initializeVariation(&variation, FEN_GAMESTART);
while (gamemove != 0)
{
if (restart == TRUE && gamemove->position.activeColor == BLACK)
{
appendToString(&moveText, " %d...", gamemove->position.moveNumber);
}
restart = FALSE;
setBasePosition(&variation, &gamemove->position);
generateMoveText(&variation,
getPackedMove(gamemove->from, gamemove->to,
gamemove->newPiece), moveBuffer);
if (gamemove->position.activeColor == WHITE)
{
appendToString(&moveText, " %d. %s", gamemove->position.moveNumber,
moveBuffer);
}
else
{
appendToString(&moveText, " %s", moveBuffer);
}
if (gamemove->glyphs != 0)
{
appendToString(&moveText, " %s", gamemove->glyphs);
}
if (gamemove->comment != 0)
{
appendToString(&moveText, " {%s}", gamemove->comment);
}
if (gamemove->alternativeMove != 0)
{
temp = generateMoveSection(gamemove->alternativeMove, "");
appendToString(&moveText, " (%s)", temp);
restart = TRUE;
}
gamemove = gamemove->nextMove;
}
if (result[0] != '\0')
{
appendToString(&moveText, " %s", result);
}
trim(moveText.buffer);
breakLines(moveText.buffer, 79);
return moveText.buffer;
}
static char *generateRoasterLine(const char *tag, const char *tagValue,
char buffer[PGN_ROASTERLINE_SIZE])
{
if (tagValue[0] != '\0')
{
sprintf(buffer
, "[%s \"%s\"]\n", tag
, tagValue
);
}
else
{
buffer[0] = '\0';
}
return buffer;
}
static char *generateRoaster(const PGNGame * pgngame)
{
char tagbuffer[PGN_ROASTERLINE_SIZE];
String string = getEmptyString();
appendToString(&string,
generateRoasterLine("Event", pgngame->event, tagbuffer));
appendToString(&string,
generateRoasterLine("Site", pgngame->site, tagbuffer));
appendToString(&string,
generateRoasterLine("Date", pgngame->date, tagbuffer));
appendToString(&string,
generateRoasterLine("Round", pgngame->round, tagbuffer));
appendToString(&string,
generateRoasterLine("White", pgngame->white, tagbuffer));
appendToString(&string,
generateRoasterLine("Black", pgngame->black, tagbuffer));
appendToString(&string,
generateRoasterLine("Result", pgngame->result, tagbuffer));
appendToString(&string,
generateRoasterLine("SetUp", pgngame->setup, tagbuffer));
appendToString(&string,
generateRoasterLine("FEN", pgngame->fen, tagbuffer));
appendToString(&string,
generateRoasterLine("WhiteTitle", pgngame->whiteTitle,
tagbuffer));
appendToString(&string,
generateRoasterLine("BlackTitle", pgngame->blackTitle,
tagbuffer));
appendToString(&string,
generateRoasterLine("WhiteElo", pgngame->whiteElo,
tagbuffer));
appendToString(&string,
generateRoasterLine("BlackElo", pgngame->blackElo,
tagbuffer));
appendToString(&string,
generateRoasterLine("ECO", pgngame->eco, tagbuffer));
appendToString(&string,
generateRoasterLine("NIC", pgngame->nic, tagbuffer));
appendToString(&string,
generateRoasterLine("TimeControl", pgngame->timeControl,
tagbuffer));
appendToString(&string,
generateRoasterLine("Termination", pgngame->termination,
tagbuffer));
return string.buffer;
}
char *generatePgn(PGNGame * game)
{
String string = getEmptyString();
char *roaster = generateRoaster(game);
char *moveText = generateMoveSection(game->firstMove, game->result);
appendToString(&string, "%s\n%s", roaster, moveText);
return string.buffer;
}
void initializeVariationFromGame(Variation * variation, PGNGame * game)
{
if (game->lastMove != 0)
{
Gamemove *currentMove = game->lastMove;
int i = POSITION_HISTORY_OFFSET - 1;
setBasePosition(variation, &(game->lastMove->position));
makeMove(variation, gameMove2Move(game->lastMove));
if (variation->singlePosition.activeColor == WHITE)
{
variation->singlePosition.moveNumber++;
}
setBasePosition(variation, &variation->singlePosition);
prepareSearch(variation);
while (i >= 0 && currentMove != 0)
{
variation->positionHistory[i--] = currentMove->position.hashKey;
currentMove = currentMove->previousMove;
}
}
else
{
if (strcmp(game
->setup
, "") != 0)
{
initializeVariation(variation, game->fen);
}
else
{
initializeVariation(variation, FEN_GAMESTART);
}
}
}
void initializeGameFromVariation(const Variation * variation, PGNGame * game,
bool copyPv)
{
const PrincipalVariation *pv = &variation->pv[0];
resetPGNGame(game);
getFen(&variation->startPosition, game->fen);
if (copyPv != FALSE)
{
int i = 0;
for (i = 0; i < min(8, pv->length); i++)
{
Move move = (Move) pv->move[i];
if (appendMove(game, move) != 0)
{
break;
}
}
}
}
char *getPrincipalVariation(const Variation * variation)
{
PGNGame game;
char *pv;
initializePGNGame(&game);
initializeGameFromVariation(variation, &game, TRUE);
pv = generateMoveSection(game.firstMove, "");
trim(pv);
resetPGNGame(&game);
return pv;
}
/*
******************************************************************************
*
* Module initialization and testing
*
******************************************************************************
*/
int initializeModulePgn()
{
pieceName[WHITE_KING] = 'K';
pieceName[WHITE_QUEEN] = 'Q';
pieceName[WHITE_ROOK] = 'R';
pieceName[WHITE_BISHOP] = 'B';
pieceName[WHITE_KNIGHT] = 'N';
pieceName[WHITE_PAWN] = 'P';
pieceName[BLACK_KING] = 'K';
pieceName[BLACK_QUEEN] = 'Q';
pieceName[BLACK_ROOK] = 'R';
pieceName[BLACK_BISHOP] = 'B';
pieceName[BLACK_KNIGHT] = 'N';
pieceName[BLACK_PAWN] = 'P';
return 0;
}
static int testGeneration()
{
char *p1 = "r2qkb1r/ppp2ppp/3p4/5b2/3PN3/n4N2/PPP1QPPP/R3K2R w KQkq - 0 1";
char *p2 = "r3k2r/n3nppp/3n2P1/8/1q1q4/2B5/1q1pRPPP/5RK1 b kq - 0 1";
Variation variation;
char pgnMove[64];
initializeVariation(&variation, p1);
generateMoveText(&variation, getPackedMove(D4, D5, NO_PIECE), pgnMove);
generateMoveText(&variation, getPackedMove(E1, G1, NO_PIECE), pgnMove);
generateMoveText(&variation, getPackedMove(E1, C1, NO_PIECE), pgnMove);
generateMoveText(&variation, getPackedMove(E4, D6, NO_PIECE), pgnMove);
generateMoveText(&variation, getPackedMove(E4, F6, NO_PIECE), pgnMove);
generateMoveText(&variation, getPackedMove(F3, G5, NO_PIECE), pgnMove);
generateMoveText(&variation, getPackedMove(E4, G5, NO_PIECE), pgnMove);
generateMoveText(&variation, getPackedMove(B2, A3, NO_PIECE), pgnMove);
initializeVariation(&variation, p2);
generateMoveText(&variation, getPackedMove(E7, F5, NO_PIECE), pgnMove);
generateMoveText(&variation, getPackedMove(D2, D1, WHITE_BISHOP), pgnMove);
generateMoveText(&variation, getPackedMove(B4, C3, NO_PIECE), pgnMove);
generateMoveText(&variation, getPackedMove(B2, C3, NO_PIECE), pgnMove);
generateMoveText(&variation, getPackedMove(D4, C3, NO_PIECE), pgnMove);
generateMoveText(&variation, getPackedMove(D6, F5, NO_PIECE), pgnMove);
generateMoveText(&variation, getPackedMove(D6, C8, NO_PIECE), pgnMove);
return 0;
}
static int testParsing()
{
char *p1 = "r2qkb1r/ppp2ppp/3p4/5b2/3PN3/n4N2/PPP1QPPP/R3K2R w KQkq - 0 1";
char *p2 = "r3k2r/n3nppp/3n2P1/8/1q1q4/2B5/1q1pRPPP/5RK1 b kq - 0 1";
Variation variation;
initializeVariation(&variation, p1);
assert(parsePGNMove
("d5", &variation.
singlePosition) ==
getPackedMove(D4, D5, NO_PIECE));
assert(parsePGNMove
("b4", &variation.
singlePosition) ==
getPackedMove(B2, B4, NO_PIECE));
assert(parsePGNMove
("bxa3", &variation.
singlePosition) ==
getPackedMove(B2, A3, NO_PIECE));
assert(parsePGNMove
("O-O", &variation.
singlePosition) ==
getPackedMove(E1, G1, NO_PIECE));
assert(parsePGNMove
("O-O-O", &variation.
singlePosition) ==
getPackedMove(E1, C1, NO_PIECE));
assert(parsePGNMove
("Ng3+", &variation.
singlePosition) ==
getPackedMove(E4, G3, NO_PIECE));
assert(parsePGNMove
("Nf6#", &variation.
singlePosition) ==
getPackedMove(E4, F6, NO_PIECE));
assert(parsePGNMove
("Nfg5", &variation.
singlePosition) ==
getPackedMove(F3, G5, NO_PIECE));
initializeVariation(&variation, p2);
assert(parsePGNMove
("h6", &variation.
singlePosition) ==
getPackedMove(H7, H6, NO_PIECE));
assert(parsePGNMove
("f5", &variation.
singlePosition) ==
getPackedMove(F7, F5, NO_PIECE));
assert(parsePGNMove
("fxg6", &variation.
singlePosition) ==
getPackedMove(F7, G6, NO_PIECE));
assert(parsePGNMove
("d1=B", &variation.
singlePosition) ==
getPackedMove(D2, D1, BISHOP));
assert(parsePGNMove
("O-O", &variation.
singlePosition) ==
getPackedMove(E8, G8, NO_PIECE));
assert(parsePGNMove
("O-O-O", &variation.
singlePosition) ==
getPackedMove(E8, C8, NO_PIECE));
assert(parsePGNMove
("Nf5", &variation.
singlePosition) ==
getPackedMove(D6, F5, NO_PIECE));
assert(parsePGNMove
("Q2xc3", &variation.
singlePosition) ==
getPackedMove(B2, C3, NO_PIECE));
assert(parsePGNMove
("Qdxc3", &variation.
singlePosition) ==
getPackedMove(D4, C3, NO_PIECE));
assert(parsePGNMove
("Qb4xc3", &variation.
singlePosition) ==
getPackedMove(B4, C3, NO_PIECE));
return 0;
}
static int testLoading()
{
PGNFile pgnfile;
PGNGame *game, game2;
char *gameText, *generatedGameText;
pgnfile.numGames = 0;
pgnfile.index = 0;
pgnfile.file = 0;
assert(openPGNFile
(&pgnfile
, "test.pgn") == 0);
game = getGame(&pgnfile, 1);
gameText = generatePgn(game);
initializePGNGame(&game2);
parseRoasterValues(gameText, &game2);
generatedGameText = generatePgn(&game2);
/* logDebug("\n>%s<\n", generatedGameText); */
freePgnGame(game);
resetPGNGame(&game2);
closePGNFile(&pgnfile);
return 0;
}
int testModulePgn()
{
int result;
if ((result = testLoading()) != 0)
{
return result;
}
if ((result = testGeneration()) != 0)
{
return result;
}
if ((result = testParsing()) != 0)
{
return result;
}
return 0;
}