Files
secondo/Algebras/Chess/pgnparser.h
2026-01-23 17:03:45 +08:00

919 lines
25 KiB
C++

/*
----
This file is part of SECONDO.
Copyright (C) 2004-2007, University in Hagen, Faculty of Mathematics and Computer Science,
Database Systems for New Applications.
SECONDO 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 2 of the License, or
(at your option) any later version.
SECONDO 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 SECONDO; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
----
//paragraph [1] title: [{\Large \bf ] [}]
[1] Datatypes for ChessAlgebra (implementation)
*/
#ifndef PGNPARSER_H
#define PGNPARSER_H
#include <iostream>
#include <string>
#include <fstream>
#include "chessTypes.h"
namespace ChessAlgebra {
/*
The flag below may help to find bugs in the parser ot the game
representation.
*/
bool trace = false;
/*
player constants - used in pgn-parser
*/
enum player
{
WHITE, BLACK
};
void traceMove( const string& pgn, char agent, char startfile,
int startrow, char endfile, int endrow, bool captures )
{
if (trace)
{
cout << "pgn: " << pgn
<< " recognized: " << agent
<< startfile << startrow << endfile << endrow
<< " captures: " << captures << endl;
}
}
bool
parseMove( char &startFile, int &startRow,
char &endFile, int &endRow,
int &playerNo, string &pgn,
Position* position )
{
char agent = ' ';
int distance = 0;
bool captures = false;
bool agentSet = false;
bool doall = false;
startFile = ' ';
endFile = ' ';
startRow = 0;
endRow = 0;
for ( int i = 0; i < min( 5, ( int ) pgn.length() ); i++ )
{
switch ( pgn[ i ] )
{
case 'K':
case 'Q':
case 'R':
case 'B':
case 'N':
if ( agentSet )
{
cerr << "unexpected Capital Letter in '" +
pgn + "'";
return false;
}
agent = ( playerNo == WHITE ) ? toupper( pgn[ i ] ) : tolower( pgn[ i ] );
agentSet = true;
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
if ( not agentSet )
{
agent = ( playerNo == WHITE ) ? 'P' : 'p';
agentSet = true;
}
if ( endFile != ' ' ) {
startFile = endFile;
}
endFile = pgn[ i ];
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
if ( endRow != 0 ) {
startRow = endRow;
}
endRow = atoi( pgn.substr( i, 1 ).c_str() );
break;
case 'x':
captures = true;
break;
case 'O':
if ( pgn.substr( 0, 5 ) == "O-O-O" )
{
endFile = 'c';
}
else if ( pgn.substr( 0, 3 ) == "O-O" )
{
endFile = 'g';
}
else
{
cerr << "unexpected continuation of 'O'\n" << endl;
return false;
}
agent = ( playerNo == WHITE ) ? 'K' : 'k';
startFile = 'e';
startRow = endRow = ( playerNo == WHITE ) ? 1 : 8;
traceMove(pgn, agent, startFile, startRow, endFile, endRow, captures);
return true;
default:
i = 6; //?
}
}
if ( endRow == 0 || endFile == ' ' ) { // what is this good for?
traceMove(pgn, agent, startFile, startRow, endFile, endRow, captures);
return true;
}
if ( startRow == 0 || startFile == ' ' )
{
// Calculate startRow and startFile
doall = ( startRow == 0 && startFile == ' ' );
char origStartFile = ' ';
int origStartRow = 0;
bool agentFound = false;
string tmpAgent = "";
bool filepos = true, fileneg = true;
bool rowpos = true, rowneg = true;
bool diag1 = true, diag2 = true, diag3 = true, diag4 = true;
switch ( agent )
{ // Pawns may move only forward so black and white
case 'P':
// have to be handled separately
if ( captures )
{
startRow = endRow - 1;
if ( startFile != ' ' ) break;
startFile = endFile - 1;
if ( startFile >= 'a' &&
position->TestField( "P", startFile, startRow ) ) break;
startFile = endFile + 1;
if ( startFile <= 'h' &&
position->TestField( "P", startFile, startRow ) ) break;
}
else
{ // doesn't capture
startFile = endFile;
startRow = endRow - 1;
if ( position->TestField( "P", startFile, startRow ) ) break;
startRow = endRow - 2;
if ( startRow == 2 &&
position->TestField( "P", startFile, startRow ) ) break;
}
cerr << "Didn't find white pawn to make move\n" << endl;
return false;
break;
case 'p':
if ( captures )
{
startRow = endRow + 1;
if ( startFile != ' ' ) break;
startFile = endFile - 1;
if ( startFile >= 'a' &&
position->TestField( "p", startFile, startRow ) ) break;
startFile = endFile + 1;
if ( startFile <= 'h' &&
position->TestField( "p", startFile, startRow ) ) break;
}
else
{ // doesn't capture
startFile = endFile;
startRow = endRow + 1;
if ( position->TestField( "p", startFile, startRow ) ) break;
startRow = endRow + 2;
if ( startRow == 7 &&
position->TestField( "p", startFile, startRow ) ) break;
}
cerr << "Didn't find black pawn to make move\n" << endl;
return false;
break;
case 'N':
case 'n':
if ( startFile != ' ' )
{
distance = 3 - abs( startFile - endFile );
startRow = endRow - distance;
if ( startRow >= 1 &&
( agentFound = position->TestField( string( 1, agent ),
startFile, startRow ) ) ) break;
startRow = endRow + distance;
if ( startRow <= 8 &&
( agentFound = position->TestField( string( 1, agent ),
startFile, startRow ) ) ) break;
cerr << "Didn't find Knight to make move\n" << endl;
return false;
break;
}
else if ( startRow != 0 )
{
distance = 3 - abs( startRow - endRow );
startFile = endFile - distance;
if ( startFile >= 'a' &&
( agentFound = position->TestField( string( 1, agent ),
startFile, startRow ) ) ) break;
startFile = endFile + distance;
if ( startFile <= 'h' &&
( agentFound = position->TestField( string( 1, agent ),
startFile, startRow ) ) ) break;
cerr << "Didn't find Knight to make move\n" << endl;
return false;
break;
}
else
{
startFile = endFile - 2;
startRow = endRow - 1;
if ( startRow >= 1 && startFile >= 'a' &&
( agentFound = position->TestField( string( 1, agent ),
startFile, startRow ) ) )
break;
startFile = endFile - 2;
startRow = endRow + 1;
if ( startRow <= 8 && startFile >= 'a' &&
( agentFound = position->TestField( string( 1, agent ),
startFile, startRow ) ) )
break;
startFile = endFile - 1;
startRow = endRow - 2;
if ( startRow >= 1 && startFile >= 'a' &&
( agentFound = position->TestField( string( 1, agent ),
startFile, startRow ) ) )
break;
startFile = endFile - 1;
startRow = endRow + 2;
if ( startRow <= 8 && startFile >= 'a' &&
( agentFound = position->TestField( string( 1, agent ),
startFile, startRow ) ) )
break;
startFile = endFile + 1;
startRow = endRow - 2;
if ( startRow >= 1 && startFile <= 'h' &&
( agentFound = position->TestField( string( 1, agent ),
startFile, startRow ) ) )
break;
startFile = endFile + 1;
startRow = endRow + 2;
if ( startRow <= 8 && startFile <= 'h' &&
( agentFound = position->TestField( string( 1, agent ),
startFile, startRow ) ) )
break;
startFile = endFile + 2;
startRow = endRow - 1;
if ( startRow >= 1 && startFile <= 'h' &&
( agentFound = position->TestField( string( 1, agent ),
startFile, startRow ) ) )
break;
startFile = endFile + 2;
startRow = endRow + 1;
if ( startRow <= 8 && startFile <= 'h' &&
( agentFound = position->TestField( string( 1, agent ),
startFile, startRow ) ) )
break;
}
cerr << "Didn't find Knight to make move\n" << endl;
return false;
break;
case 'B':
case 'b':
origStartRow = startRow;
origStartFile = startFile;
for ( int i = -7; i <= 7; i++ )
{
startFile = endFile + i;
startRow = endRow + i;
if ( startRow >= 1 && startFile >= 'a' &&
startRow <= 8 && startFile <= 'h' &&
( doall || origStartRow == startRow ||
origStartFile == startFile ) &&
( agentFound = position->TestField( string( 1, agent ),
startFile, startRow ) ) )
break;
}
if ( agentFound ) break;
for ( int i = -7; i <= 7; i++ )
{
startFile = endFile + i;
startRow = endRow - i;
if ( startRow >= 1 && startFile >= 'a' &&
startRow <= 8 && startFile <= 'h' &&
( doall || origStartRow == startRow ||
origStartFile == startFile ) &&
( agentFound = position->TestField( string( 1, agent ),
startFile, startRow ) ) )
break;
}
if ( agentFound ) break;
cerr << "Didn't find Bishop to make move\n" << endl;
return false;
break;
case 'R':
case 'r':
if ( startFile == endFile )
{ // need to search only this file
filepos = fileneg = false;
}
else if ( startFile != ' ' )
{ // startFile != endFile => startRow == endRow
startRow = endRow;
break;
}
else if ( startRow == endRow )
{ // need to search only this row
rowpos = rowneg = false;
}
else if ( startRow != 0 )
{ // startRow != endRow => startFile == endFile
startFile = endFile;
break;
}
for ( int i = 1; i <= 7; i++ )
{
if ( filepos )
{
if ( (endFile + i) > 'h' )
{
filepos = false;
}
else
{
tmpAgent = position->GetAgentShort( endFile + i , endRow );
if ( tmpAgent[ 0 ] == agent )
{
startRow = endRow;
startFile = endFile + i;
break;
}
else if ( tmpAgent != "-" && tmpAgent != "x" )
{
filepos = false;
}
}
}
if ( fileneg )
{
if ( (endFile - i) < 'a' )
{
fileneg = false;
}
else
{
tmpAgent = position->GetAgentShort( endFile - i, endRow );
if ( tmpAgent[ 0 ] == agent )
{
startRow = endRow;
startFile = endFile - i;
break;
}
else if ( (tmpAgent != "-") && (tmpAgent != "x") )
{
fileneg = false;
}
}
}
if ( rowpos )
{
if ( (endRow + i) > 8 )
{
rowpos = false;
}
else
{
tmpAgent = position->GetAgentShort( endFile, endRow + i );
if ( tmpAgent[ 0 ] == agent )
{
startRow = endRow + i;
startFile = endFile;
break;
}
else if ( tmpAgent != "-" && tmpAgent != "x" )
{
rowpos = false;
}
}
}
if ( rowneg )
{
if ( (endRow - i) < 1 )
{
rowneg = false;
}
else
{
tmpAgent = position->GetAgentShort( endFile, endRow - i );
if ( tmpAgent[ 0 ] == agent )
{
startRow = endRow - i;
startFile = endFile;
break;
}
else if ( (tmpAgent != "-") && (tmpAgent != "x") )
{
rowneg = false;
}
}
}
}
if ( startFile == ' ' || startRow == 0 )
{
cerr << "Didn't find Rook to make move\n";
}
break;
case 'Q':
case 'q':
if ( startFile == endFile )
{ // need to search only this file
filepos = fileneg = diag1 = diag2 = diag3 = diag4 = false;
}
else if ( startFile != ' ' )
{
if ( position->TestField( string( 1, agent ), startFile, endRow ) )
{
startRow = endRow;
break;
}
else if ( ( startRow = endRow + abs( endFile - startFile ) ) <= 8 &&
position->TestField( string( 1, agent ),
startFile, startRow ) )
{
break;
}
else if ( ( startRow = endRow - abs( endFile - startFile ) ) >= 1 &&
position->TestField( string( 1, agent ),
startFile, startRow ) )
{
break;
}
}
else if ( startRow == endRow )
{ // need to search only this row
rowpos = rowneg = diag1 = diag2 = diag3 = diag4 = false;
}
else if ( startRow != 0 )
{
if ( position->TestField( string( 1, agent ), endFile, startRow ) )
{
startFile = endFile;
break;
}
else if ( ( startFile = endFile + abs( endRow - startRow ) ) <= 'h' &&
position->TestField( string( 1, agent ),
startFile, startRow ) )
{
break;
}
else if ( ( startFile = endFile - abs( endRow - startRow ) ) >= 'a' &&
position->TestField( string( 1, agent ),
startFile, startRow ) )
{
break;
}
}
for ( int i = 1; i <= 7; i++ )
{
if ( filepos )
{
if ( (endFile + i) > 'h' )
{
filepos = false;
}
else
{
tmpAgent = position->GetAgentShort( endFile + i, endRow );
if ( tmpAgent[ 0 ] == agent )
{
startRow = endRow;
startFile = endFile + i;
break;
}
else if ( tmpAgent != "-" && tmpAgent != "x" )
{
filepos = false;
}
}
}
if ( fileneg )
{
if ( (endFile - i) < 'a' )
{
fileneg = false;
}
else
{
tmpAgent = position->GetAgentShort( endFile - i, endRow );
if ( tmpAgent[ 0 ] == agent )
{
startRow = endRow;
startFile = endFile - i;
break;
}
else if ( (tmpAgent != "-") && (tmpAgent != "x") )
{
fileneg = false;
}
}
}
if ( rowpos )
{
if ( (endRow + i) > 8 )
{
rowpos = false;
}
else
{
tmpAgent = position->GetAgentShort( endFile, endRow + i );
if ( tmpAgent[ 0 ] == agent )
{
startRow = endRow + i;
startFile = endFile;
break;
}
else if ( (tmpAgent != "-") && (tmpAgent != "x") )
{
rowpos = false;
}
}
}
if ( rowneg )
{
if ( endRow - i < 1 )
{
rowneg = false;
}
else
{
tmpAgent = position->GetAgentShort( endFile, endRow - i );
if ( tmpAgent[ 0 ] == agent )
{
startRow = endRow - i;
startFile = endFile;
break;
}
else if ( (tmpAgent != "-") && (tmpAgent != "x") )
{
rowneg = false;
}
}
}
if ( diag1 )
{
if ( (endFile + i) > 'h' || (endRow + i) > 8 )
{
diag1 = false;
}
else
{
tmpAgent = position->GetAgentShort( endFile + i, endRow + i );
if ( tmpAgent[ 0 ] == agent )
{
startRow = endRow + i;
startFile = endFile + i;
break;
}
else if ( (tmpAgent != "-") && (tmpAgent != "x") )
{
diag1 = false;
}
}
}
if ( diag2 )
{
if ( (endFile + i) > 'h' || (endRow - i) < 1 )
{
diag2 = false;
}
else
{
tmpAgent = position->GetAgentShort( endFile + i, endRow - i );
if ( tmpAgent[ 0 ] == agent )
{
startRow = endRow - i;
startFile = endFile + i;
break;
}
else if ( (tmpAgent != "-") && (tmpAgent != "x") )
{
diag2 = false;
}
}
}
if ( diag3 )
{
if ( (endFile - i) < 'a' || (endRow + i) > 8 )
{
diag3 = false;
}
else
{
tmpAgent = position->GetAgentShort( endFile - i, endRow + i );
if ( tmpAgent[ 0 ] == agent )
{
startRow = endRow + i;
startFile = endFile - i;
break;
}
else if ( tmpAgent != "-" && tmpAgent != "x" )
{
diag3 = false;
}
}
}
if ( diag4 )
{
if ( (endFile - i) < 'a' || (endRow - i) < 1 )
{
diag4 = false;
}
else
{
tmpAgent = position->GetAgentShort( endFile - i, endRow - i );
if ( tmpAgent[ 0 ] == agent )
{
startRow = endRow - i;
startFile = endFile - i;
break;
}
else if ( tmpAgent != "-" && tmpAgent != "x" )
{
diag4 = false;
}
}
}
}
if ( startFile == ' ' || startRow == 0 )
{
cerr << "Didn't find Rook to make move\n" << endl;
return false;
}
break;
case 'K':
case 'k':
for ( int i = -1; i <= 1; i++ ) {
if (agentFound) // short exit
break;
for ( int j = -1; j <= 1; j++ ) {
if (agentFound) // short exit
break;
startFile = endFile + i;
startRow = endRow + j;
if ( startRow >= 1 && startFile >= 'a' &&
startRow <= 8 && startFile <= 'h' &&
!((i == 0) && (j == 0)) )
{
agentFound = position->TestField( string( 1, agent ),
startFile, startRow );
//cerr << "Test for " << agent << ": "
// << startFile << startRow << " " << agentFound << endl;
}
}
}
if (!agentFound) {
cmsg.error() << "Didn't find King to make move\n" << endl;
cmsg.send();
traceMove(pgn, agent, startFile, startRow, endFile, endRow, captures);
return false;
}
break;
default: {
cerr << "Internal Error: wrong agent"
" in move calculation\n" << endl;
return false; }
} // switch
} // calculate startRow and startFile
traceMove(pgn, agent, startFile, startRow, endFile, endRow, captures);
return true;
}
Chessgame* ParseFile( ifstream* file )
{
// this version only supports pgn format
Chessgame * result = new Chessgame( 0, 0 );
string line, key, value, pgn, pgntmp;
char inChar=' ', startFile=' ', endFile=' ';
int startRow=0, endRow=0, parenthesis=0;
string::size_type pos=0, pos2=0;
bool comment = false, nextcomment = false;
bool skipToNextMatch = false;
// read Tag pair section
bool foundTagPair = false;
while ( getline( *file, line ) && (line == "" || line[0] == 13 ));
while ( line.find( "]", 0 ) != string::npos )
{
if ( line[ 0 ] == '[' )
{
pos = line.find( " ", 1 );
key = line.substr( 1, pos - 1 );
int valuelen = line.find( "\"", pos + 2 ) - pos - 2;
value = line.substr( pos + 2, valuelen );
result->AddMetainfoEntry( key, value );
foundTagPair = true;
}
getline( *file, line );
}
result->SortMetainfos();
// should only happen, if empty line after last game was read
if ( !foundTagPair ) {
return 0;
}
STRING_T white;
STRING_T black;
result->GetMetainfoValue("White", &white);
result->GetMetainfoValue("Black", &black);
cerr << "Parsing game notation for "
<< white << " vs. " << black << endl << endl;
// read Movetext section
int playerNo=0;
*file >> pgn;
while ( comment ||
(parenthesis != 0) ||
(( pgn != "1-0" ) && ( pgn != "0-1" ) &&
( pgn != "1/2-1/2" ) && ( pgn != "*" )
&& !file->eof()) )
{
if ( comment )
{
if ( ( pos = pgn.find( "}", 0 ) ) != string::npos )
{
pgn = pgn.substr( pos + 1, pgn.length() - pos - 1 );
comment = false;
}
else
{
*file >> pgn;
if ( pgn == "1/2-1/2" ) break;
}
}
else
{
if ( nextcomment )
{
comment = true;
nextcomment = false;
}
if ( ( pos = pgn.find( "{", 0 ) ) != string::npos )
{
if ( ( pos2 = pgn.find( "}", pos ) ) == string::npos )
{
nextcomment = true;
pgn = pgn.substr( 0, pos );
}
else
{
pgn = pgn.substr( 0, pos ) + pgn.substr( pos2 + 1,
pgn.length() - pos2 - 1 );
}
}
else if ( ( pos = pgn.find( "(", 0 ) ) != string::npos )
{
// alternatives
parenthesis ++;
if ( parenthesis == 1 )
{
pgn = pgn.substr( 0, pos );
}
else
{
*file >> pgn;
}
}
else if ( parenthesis > 0 )
{
if ( ( pos = pgn.find( ")", 0 ) ) != string::npos )
{
parenthesis--;
if ( parenthesis <= 0 )
{
pgn = pgn.substr( pos + 1, pgn.length() - pos - 1 );
}
else
{
while ( ( pos = pgn.find( ")", pos + 1 ) ) != string::npos )
{
parenthesis--;
}
if ( parenthesis <= 0 && pos < pgn.length() - 1 )
{
pgn = pgn.substr( pos + 1, pgn.length() - pos - 1 );
}
else
{
*file >> pgn;
}
}
}
else
{
*file >> pgn;
if ( pgn == "1/2-1/2" ) break;
}
}
else if ( pgn.find( "...", 0 ) != string::npos )
{
playerNo = BLACK;
*file >> pgn;
}
else if ( pgn.find( ".", 0 ) != string::npos )
{
playerNo = WHITE;
pos = pgn.find( ".", 0 );
pgn = pgn.substr( pos + 1, pgn.size() );
}
else if ( pgn == "" )
{
*file >> pgn;
}
else
{
if (!skipToNextMatch) {
if (trace)
result->GetLastPosition()->ShowBoard(cerr);
bool ok = parseMove( startFile, startRow, endFile,
endRow, playerNo, pgn,
result->GetLastPosition() );
if (ok)
{
result->AddMove( startFile, startRow, endFile, endRow, pgn );
if ( playerNo == WHITE ) playerNo = BLACK;
} else {
result->AddMetainfoEntry( "Result", "Parse Error for " + pgn );
skipToNextMatch = true;
}
}
*file >> pgn;
}
}
}
// ignore rest of current line (usually only the '\n' char)
while ( file->get( inChar ) && inChar != '\n' ) cout << inChar;
return result;
}
} // namespace ChessAlgebra
#endif // PST_TYPES_H