Logo Search packages:      
Sourcecode: kcheckers version File versions  Download package

pdn.cc

/***************************************************************************
 *   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;
}


Generated by  Doxygen 1.6.0   Back to index