mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-12-20 13:24:33 -05:00
542 lines
12 KiB
C++
542 lines
12 KiB
C++
|
/***************************************************************************
|
||
|
* Copyright (C) 2002-2003 Andi Peredri *
|
||
|
* andi@ukr.net *
|
||
|
* Copyright (C) 2004-2005 Artur Wiebe *
|
||
|
* wibix@gmx.de *
|
||
|
* *
|
||
|
* 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 2 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, write to the *
|
||
|
* Free Software Foundation, Inc., *
|
||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||
|
***************************************************************************/
|
||
|
#include <QFile>
|
||
|
#include <QTextStream>
|
||
|
#include <QProgressDialog>
|
||
|
#include <QDebug>
|
||
|
|
||
|
#include "checkers.h"
|
||
|
#include "pdn.h"
|
||
|
|
||
|
|
||
|
#define PLAYER true
|
||
|
#define COMPUTER false
|
||
|
|
||
|
#define END_OF_MOVELINE "@."
|
||
|
|
||
|
|
||
|
Pdn::Pdn()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
Pdn::~Pdn()
|
||
|
{
|
||
|
qDeleteAll(m_database);
|
||
|
m_database.clear();
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Pdn::open(const QString& filename, QWidget* parent,
|
||
|
const QString& label, QString& text_to_log)
|
||
|
{
|
||
|
qDeleteAll(m_database);
|
||
|
m_database.clear();
|
||
|
|
||
|
QFile file(filename);
|
||
|
if(!file.open(QFile::ReadOnly)) return false;
|
||
|
|
||
|
QTextStream ts(&file);
|
||
|
|
||
|
QString str1, str2;
|
||
|
|
||
|
QProgressDialog progress(parent);
|
||
|
progress.setModal(true);
|
||
|
progress.setLabelText(label);
|
||
|
progress.setRange(0, file.size());
|
||
|
progress.setMinimumDuration(0);
|
||
|
|
||
|
unsigned int line_nr = 1;
|
||
|
unsigned int game_started = 1;
|
||
|
bool in_tags = false;
|
||
|
while(!ts.atEnd()) {
|
||
|
str1 = ts.readLine().trimmed();
|
||
|
if(ts.atEnd())
|
||
|
str2 += str1;
|
||
|
|
||
|
if((str1.length() && str1[0]=='[') || ts.atEnd()) {
|
||
|
if(!in_tags) {
|
||
|
// tags begin again, so a game is ended.
|
||
|
if(str2.length()) {
|
||
|
if((m_database.count()%10)==0)
|
||
|
progress.setValue(file.pos());
|
||
|
|
||
|
QString log_txt;
|
||
|
PdnGame* game = new PdnGame(str2, log_txt);
|
||
|
m_database.append(game);
|
||
|
|
||
|
if(log_txt.length()) {
|
||
|
text_to_log
|
||
|
+= QString("%1. game begins at line %2:\n")
|
||
|
.arg(m_database.count())
|
||
|
.arg(game_started);
|
||
|
text_to_log += log_txt;
|
||
|
}
|
||
|
|
||
|
game_started = line_nr;
|
||
|
str2="";
|
||
|
}
|
||
|
|
||
|
in_tags = true;
|
||
|
}
|
||
|
} else {
|
||
|
if(in_tags)
|
||
|
in_tags = false;
|
||
|
}
|
||
|
|
||
|
str2.append(str1+"\n");
|
||
|
|
||
|
if(progress.wasCanceled())
|
||
|
break;
|
||
|
|
||
|
line_nr++;
|
||
|
}
|
||
|
|
||
|
file.close();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Pdn::save(const QString& filename)
|
||
|
{
|
||
|
QFile file(filename);
|
||
|
if(!file.open(QFile::WriteOnly))
|
||
|
return false;
|
||
|
|
||
|
QTextStream ts(&file);
|
||
|
|
||
|
foreach(PdnGame* game, m_database) {
|
||
|
ts << game->toString() << endl << endl;
|
||
|
}
|
||
|
|
||
|
file.close();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
PdnGame* Pdn::newGame()
|
||
|
{
|
||
|
QString log_txt; // here: ignore TODO
|
||
|
PdnGame* game = new PdnGame("", log_txt);
|
||
|
m_database.append(game);
|
||
|
return game;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
* *
|
||
|
* *
|
||
|
***************************************************************************/
|
||
|
PdnMove::PdnMove(QString line)
|
||
|
{
|
||
|
if(line[0]=='{') {
|
||
|
qDebug("a move must not begin with a comment.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// first move.
|
||
|
m_first = line.section(' ', 0, 0);
|
||
|
line = line.mid(m_first.length()).trimmed();
|
||
|
|
||
|
// check for a first comment.
|
||
|
if(line[0]=='{') {
|
||
|
int end = line.indexOf('}', 1);
|
||
|
if(end>=0) {
|
||
|
m_comfirst = line.mid(1, end-1);
|
||
|
line.remove(0, end+1);
|
||
|
line = line.trimmed();
|
||
|
} else
|
||
|
qDebug("no comment ending of the first comment.");
|
||
|
}
|
||
|
|
||
|
// second move.
|
||
|
m_second = line.section(' ', 0, 0);
|
||
|
line = line.mid(m_second.length()).trimmed();
|
||
|
|
||
|
// check for a second comment.
|
||
|
if(line[0]=='{') {
|
||
|
int end = line.indexOf('}', 1);
|
||
|
if(end>=0)
|
||
|
m_comsecond = line.mid(1, end-1);
|
||
|
else
|
||
|
qDebug("no comment ending of the second comment.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
* *
|
||
|
* *
|
||
|
***************************************************************************/
|
||
|
PdnGame::PdnGame(const QString& game_string, QString& log_txt)
|
||
|
{
|
||
|
white = PLAYER;
|
||
|
for(int i=0; i<12; i++)
|
||
|
board[i]=MAN2;
|
||
|
for(int i=20; i<32; i++)
|
||
|
board[i]=MAN1;
|
||
|
|
||
|
if(!parse(game_string, log_txt)) {
|
||
|
qDebug(" errors occured while processing game."); // TODO
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
PdnGame::~PdnGame()
|
||
|
{
|
||
|
qDeleteAll(m_moves);
|
||
|
m_moves.clear();
|
||
|
}
|
||
|
|
||
|
|
||
|
QString PdnGame::get(Tag tag) const
|
||
|
{
|
||
|
switch(tag) {
|
||
|
case Date: return pdnDate;
|
||
|
case Site: return pdnSite;
|
||
|
case Type: return pdnType;
|
||
|
case Event: return pdnEvent;
|
||
|
case Round: return pdnRound;
|
||
|
case White: return pdnWhite;
|
||
|
case Black: return pdnBlack;
|
||
|
default: return pdnResult;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void PdnGame::set(Tag tag, const QString& string)
|
||
|
{
|
||
|
switch(tag) {
|
||
|
case Date: pdnDate=string; break;
|
||
|
case Site: pdnSite=string; break;
|
||
|
case Type: pdnType=string; break;
|
||
|
case Event: pdnEvent=string;break;
|
||
|
case Round: pdnRound=string;break;
|
||
|
case White: pdnWhite=string;break;
|
||
|
case Black: pdnBlack=string;break;
|
||
|
default: pdnResult=string;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
bool PdnGame::parse_moves(const QString& line)
|
||
|
{
|
||
|
qDeleteAll(m_moves);
|
||
|
m_moves.clear();
|
||
|
|
||
|
QStringList list = line.split(' ');
|
||
|
|
||
|
QString current_move;
|
||
|
int move_num = 0;
|
||
|
bool in_comment = false;
|
||
|
foreach(QString str, list) {
|
||
|
if(str.startsWith("{"))
|
||
|
in_comment = true;
|
||
|
if(str.endsWith("}"))
|
||
|
in_comment = false;
|
||
|
|
||
|
if(str.endsWith(".") && !in_comment) {
|
||
|
if(str!=END_OF_MOVELINE) {
|
||
|
if((move_num+1) != str.mid(0, str.length()-1).toInt()) {
|
||
|
qDebug() << "Move num expected:" << move_num+1
|
||
|
<< "received:" << str;
|
||
|
return false;
|
||
|
}
|
||
|
move_num++;
|
||
|
}
|
||
|
|
||
|
current_move = current_move.trimmed();
|
||
|
if(current_move.length()) {
|
||
|
m_moves.append(new PdnMove(current_move));
|
||
|
current_move = "";
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if(str.isEmpty())
|
||
|
current_move += " ";
|
||
|
else
|
||
|
current_move += str + " ";
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool PdnGame::parse(const QString& pdngame, QString& log_txt)
|
||
|
{
|
||
|
QString fen;
|
||
|
QString moves;
|
||
|
int num = pdngame.count("\n"); // Number of lines
|
||
|
|
||
|
for(int i=0; i<=num; i++) {
|
||
|
QString line = pdngame.section('\n',i ,i);
|
||
|
if(!line.length())
|
||
|
continue;
|
||
|
|
||
|
if(line.startsWith("[")) {
|
||
|
line.remove(0, 1);
|
||
|
line = line.trimmed();
|
||
|
|
||
|
if(line.startsWith("GameType")) pdnType=line.section('"',1,1);
|
||
|
else if(line.startsWith("FEN")) fen=line.section('"',1,1);
|
||
|
else if(line.startsWith("Date")) pdnDate=line.section('"',1,1);
|
||
|
else if(line.startsWith("Site")) pdnSite=line.section('"',1,1);
|
||
|
else if(line.startsWith("Event")) pdnEvent=line.section('"',1,1);
|
||
|
else if(line.startsWith("Round")) pdnRound=line.section('"',1,1);
|
||
|
else if(line.startsWith("White")) pdnWhite=line.section('"',1,1);
|
||
|
else if(line.startsWith("Black")) pdnBlack=line.section('"',1,1);
|
||
|
else if(line.startsWith("Result")) pdnResult=line.section('"',1,1);
|
||
|
else ; // Skip other unsupported tags
|
||
|
|
||
|
} else {
|
||
|
moves += " " + line;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// parse move section.
|
||
|
if(moves.endsWith(pdnResult))
|
||
|
moves.truncate(moves.length()-pdnResult.length());
|
||
|
else {
|
||
|
log_txt += " +Different result at the end of the movelist:\n"
|
||
|
+ QString(" \"%1\" expected, got \"%2\"\n")
|
||
|
.arg(pdnResult)
|
||
|
.arg(moves.right(pdnResult.length()));
|
||
|
|
||
|
// need to remove the incorrect result.
|
||
|
if(moves.endsWith(" *")) {
|
||
|
log_txt += " => Ignoring \" *\" from the end.\n";
|
||
|
moves.truncate(moves.length()-2);
|
||
|
} else {
|
||
|
int pos = moves.lastIndexOf('-') - 1;
|
||
|
bool skip_ws = true;
|
||
|
for(int i=pos; i>=0; i--) {
|
||
|
if(moves[i]==' ') {
|
||
|
if(!skip_ws) {
|
||
|
log_txt += " => Ignoring \""
|
||
|
+ moves.right(moves.length()-i-1)
|
||
|
+ "\" from the end.\n",
|
||
|
moves.truncate(i+1);
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
skip_ws = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(!parse_moves(moves+" "END_OF_MOVELINE)) { // :)
|
||
|
log_txt += "\n +parsing moves failed.";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Translation of the GameType tag
|
||
|
switch(pdnType.toInt()) {
|
||
|
case ENGLISH:
|
||
|
case RUSSIAN:
|
||
|
break;
|
||
|
default:
|
||
|
// log_txt += "\n +setting game type to english.";
|
||
|
pdnType.setNum(ENGLISH);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Parsing of the Forsyth-Edwards Notation (FEN) tag
|
||
|
if(fen.isNull())
|
||
|
return true;
|
||
|
|
||
|
fen=fen.trimmed();
|
||
|
|
||
|
for(int i=fen.indexOf(" "); i!=-1; i=fen.indexOf(" "))
|
||
|
fen=fen.remove(i,1);
|
||
|
|
||
|
if(fen.startsWith("W:W"))
|
||
|
white=PLAYER;
|
||
|
else if(fen.startsWith("B:W"))
|
||
|
white=COMPUTER;
|
||
|
else
|
||
|
return false;
|
||
|
|
||
|
QString string = fen.mid(3).section(":B",0,0);
|
||
|
if(!parse(string, white))
|
||
|
return false;
|
||
|
|
||
|
string=fen.section(":B",1,1);
|
||
|
if(string.endsWith("."))
|
||
|
string.truncate(string.length()-1);
|
||
|
if(!parse(string, !white))
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool PdnGame::parse(const QString& str, bool side)
|
||
|
{
|
||
|
QString notation;
|
||
|
|
||
|
if(pdnType.toInt() == ENGLISH)
|
||
|
notation=QString(ENOTATION);
|
||
|
else
|
||
|
notation=QString(RNOTATION);
|
||
|
|
||
|
QStringList sections = str.split(",");
|
||
|
foreach(QString pos, sections) {
|
||
|
bool king=false;
|
||
|
|
||
|
if(pos.startsWith("K")) {
|
||
|
pos=pos.remove(0,1);
|
||
|
king=true;
|
||
|
}
|
||
|
if(pos.length()==1)
|
||
|
pos.append(' ');
|
||
|
if(pos.length()!=2)
|
||
|
return false;
|
||
|
|
||
|
int index = notation.indexOf(pos);
|
||
|
if(index%2)
|
||
|
index=notation.indexOf(pos,index+1);
|
||
|
if(index == -1)
|
||
|
return false;
|
||
|
|
||
|
if(white==COMPUTER)
|
||
|
index=62-index;
|
||
|
|
||
|
if(side==PLAYER)
|
||
|
board[index/2]=(king ? KING1 : MAN1);
|
||
|
else
|
||
|
board[index/2]=(king ? KING2 : MAN2);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
PdnMove* PdnGame::getMove(int i)
|
||
|
{
|
||
|
if(i<m_moves.count()) {
|
||
|
return m_moves.at(i);
|
||
|
}
|
||
|
|
||
|
// TODO - do we need this?
|
||
|
if(i>m_moves.count())
|
||
|
qDebug("PdnGame::getMove(%u) m_moves.count()=%u",
|
||
|
i, m_moves.count());
|
||
|
|
||
|
PdnMove* m = new PdnMove("");
|
||
|
m_moves.append(m);
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
|
||
|
QString PdnGame::toString()
|
||
|
{
|
||
|
QString fen;
|
||
|
QString moves;
|
||
|
|
||
|
/*
|
||
|
* fen
|
||
|
*/
|
||
|
if(!movesCount()) {
|
||
|
qDebug("FEN tag with lots of errors.");
|
||
|
QString string1;
|
||
|
QString string2;
|
||
|
QString notation;
|
||
|
|
||
|
if(pdnType.toInt() == ENGLISH)
|
||
|
notation=QString(ENOTATION);
|
||
|
else
|
||
|
notation=QString(RNOTATION);
|
||
|
|
||
|
for(int i=0; i<32; i++) {
|
||
|
int index=i*2;
|
||
|
if(white==COMPUTER) index=62-index;
|
||
|
|
||
|
QString pos;
|
||
|
|
||
|
switch(board[i]) {
|
||
|
case KING1:
|
||
|
pos.append('K');
|
||
|
case MAN1:
|
||
|
pos.append(notation.mid(index,2).trimmed());
|
||
|
if(string1.length()) string1.append(',');
|
||
|
string1.append(pos);
|
||
|
break;
|
||
|
case KING2:
|
||
|
pos.append('K');
|
||
|
case MAN2:
|
||
|
pos.append(notation.mid(index,2).trimmed());
|
||
|
if(string2.length()) string2.append(',');
|
||
|
string2.append(pos);
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if(white==PLAYER)
|
||
|
fen.append("W:W"+string1+":B"+string2+".");
|
||
|
else
|
||
|
fen.append("B:W"+string2+":B"+string1+".");
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* moves
|
||
|
*/
|
||
|
unsigned int count = 1;
|
||
|
foreach(PdnMove* move, m_moves) {
|
||
|
moves += QString("%1. %2 %3%4%5\n")
|
||
|
.arg(count)
|
||
|
.arg(move->m_first)
|
||
|
.arg(move->m_comfirst.length() ? "{"+move->m_comfirst+"} " : "")
|
||
|
.arg(move->m_second)
|
||
|
.arg(move->m_comsecond.length() ? " {"+move->m_comsecond+"}" : "");
|
||
|
count++;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* create format and write tags+fen+moves.
|
||
|
*/
|
||
|
QString str;
|
||
|
|
||
|
if(pdnEvent.length()) str.append("[Event \""+pdnEvent+"\"]\n");
|
||
|
if(pdnSite.length()) str.append("[Site \"" +pdnSite +"\"]\n");
|
||
|
if(pdnDate.length()) str.append("[Date \"" +pdnDate +"\"]\n");
|
||
|
if(pdnRound.length()) str.append("[Round \""+pdnRound+"\"]\n");
|
||
|
if(pdnWhite.length()) str.append("[White \""+pdnWhite+"\"]\n");
|
||
|
if(pdnBlack.length()) str.append("[Black \""+pdnBlack+"\"]\n");
|
||
|
|
||
|
if(fen.length()) {
|
||
|
str.append("[SetUp \"1\"]\n");
|
||
|
str.append("[FEN \""+fen+"\"]\n\n");
|
||
|
}
|
||
|
|
||
|
str.append("[Result \""+pdnResult+"\"]\n");
|
||
|
str.append("[GameType \""+pdnType+"\"]\n");
|
||
|
str.append(moves);
|
||
|
str.append(pdnResult+"\n");
|
||
|
|
||
|
return str;
|
||
|
}
|
||
|
|