/*************************************************************************** * 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 #include #include #include #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(im_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; }