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

view.cc

/***************************************************************************
 *   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 <QLayout>
#include <QDate>
#include <QDebug>
#include <QTimer>

#include "pdn.h"
#include "echeckers.h"
#include "rcheckers.h"
#include "view.h"
#include "common.h"
#include "history.h"
#include "newgamedlg.h"

#include "player.h"
#include "humanplayer.h"
#include "computerplayer.h"


#define MAX_CMD_LEN           80
#define MOVE_PAUSE            1000

// this class is used to note differencies between moves.
00042 class myDiff {
public:
      myDiff(int pos, int from, int to)
            : m_pos(pos), m_from(from), m_to(to) {}
      int m_pos;
      int m_from;
      int m_to;
};


myView::myView(QWidget* parent)
      : QFrame(parent)
{
      /*
       * board & info 
       */
      m_board = new myBoard(this);
      connect(m_board, SIGNAL(fieldClicked(int)),
                  this, SLOT(slot_click(int)));

      m_history = new myHistory(this);
      connect(m_history, SIGNAL(previewGame(int)),
                  this, SLOT(slot_preview_game(int)));
      connect(m_history, SIGNAL(applyMoves(const QString&)),
                  this, SLOT(slot_apply_moves(const QString&)));
      connect(m_history, SIGNAL(newMode(bool, bool)),
                  this, SLOT(slot_new_mode(bool, bool)));
      connect(this, SIGNAL(working(bool)),
                  m_history, SLOT(slotWorking(bool)));

      QHBoxLayout* hb = new QHBoxLayout(0);
      hb->addWidget(m_board);
      hb->addSpacing(5);
      hb->addWidget(m_history);


      /*
       *
       */
      m_log = new QTextEdit(this);
      m_log->setFixedHeight(100); //FIXME
      m_log->setReadOnly(true);


      /*
       * it's the final layout.
       */
      QVBoxLayout* vb = new QVBoxLayout(this);
      vb->addLayout(hb);
      vb->addWidget(m_log);
      vb->setSizeConstraint(QLayout::SetFixedSize);


      /*
       * game init
       */
      m_player = m_current = 0;
}


myView::~myView()
{
      if(m_player) {
            delete m_player;
            delete m_player->opponent();
      }
}


void myView::setEnabled(bool b)
{
      m_board->setEnabled(b);
      if(b)
            setCursor(Qt::ArrowCursor);   // should be m_board bound.
      else
            setCursor(Qt::WaitCursor);
}


void myView::setTheme(const QString& path)
{
      m_board->setTheme(path, m_player ? m_player->isWhite() : true);
      m_history->setFixedHeight(m_board->height());
}


void myView::newGame(int rules, bool freeplace,
            const QString& name, bool is_white,
            int opponent, const QString& opp_name, int skill)
{
      m_freeplace_from = -1;

      if(m_player) {
            delete m_player;
            delete m_player->opponent();
      }

      m_board->setColorWhite(is_white);

      // create players
      myPlayer* plr = new myHumanPlayer(name, is_white, false);
      myPlayer* opp = 0;
      if(opponent==HUMAN)
            opp = new myHumanPlayer(opp_name, !is_white, true);
      else
            opp = new myComputerPlayer(opp_name, !is_white, skill);

      emit working(true);


      /*
       * set up player stuff. slots/signals.
       */
      m_player = plr;

      plr->setOpponent(opp);
      opp->setOpponent(plr);

      plr->disconnect();
      opp->disconnect();

      connect(plr, SIGNAL(moveDone(const QString&)),
                  this, SLOT(slot_move_done(const QString&)));

      connect(opp, SIGNAL(moveDone(const QString&)),
                  this, SLOT(slot_move_done(const QString&)));


      /*
       * create game board.
       */
      m_board->setGame(rules);

      m_board->reset();
      m_history->clear();

      begin_game(1, freeplace);
}


void myView::begin_game(unsigned int round, bool freeplace)
{
      if(m_clear_log)
            m_log->clear();

      m_board->adjustNotation(m_player->isWhite());

      m_history->newPdn(APPNAME" Game", freeplace);
      m_history->setTag(PdnGame::Type, QString::number(m_board->type()));
      m_history->setTag(PdnGame::Date,
            QDate::currentDate().toString("yyyy.MM.dd"));
      m_history->setTag(PdnGame::Result, "*");
      m_history->setTag(PdnGame::Round, QString::number(round));


      /*
       * go!
       */
      myPlayer* last_player = get_first_player()->opponent();

      m_game_over = false;
      m_aborted = false;
      m_current = last_player;

      // setup names
      if(m_player->isWhite()) {
            m_history->setTag(PdnGame::White, m_player->name());
            m_history->setTag(PdnGame::Black, m_player->opponent()->name());
      } else {
            m_history->setTag(PdnGame::White, m_player->opponent()->name());
            m_history->setTag(PdnGame::Black, m_player->name());
      }

      if(m_history->isFreePlacement())
            emit working(false);
      else
            slot_move_done(m_board->game()->toString(false));
}


bool myView::check_game_over()
{
      if(m_game_over)         // no further checks
            return true;

      m_game_over = true;

      bool player_can = m_board->game()->checkMove1()
            || m_board->game()->checkCapture1();
      bool opp_can = m_board->game()->checkMove2()
            || m_board->game()->checkCapture2();

      // player cannot go but opponent can -> player lost.
      if(/*FIXME*/m_player==m_current && !player_can && opp_can) {
            you_won(false);
            return m_game_over;
      }
      // player can go but opponent cannot -> player won.
      if(/*FIXME*/m_player!=m_current && player_can && !opp_can) {
            you_won(true);
            return m_game_over;
      }
      // neither of the player can go -> draw.
      if(!player_can && !opp_can) {
            add_log(myView::System, tr("Drawn game."));
            m_history->setTag(PdnGame::Result, "1/2-1/2");
            return m_game_over;
      }

      m_game_over = false;
      return m_game_over;
}


void myView::slot_click(int field_num)
{
      if(m_game_over || m_aborted)
            return;

      if(m_history->isPaused()) {
            if(m_history->isFreePlacement()) {
                  // FIXME - hightlight fields
                  if(m_freeplace_from < 0) {
                        m_freeplace_from = field_num;
                        m_board->selectField(field_num, true);
                  } else {
                        m_board->selectField(m_freeplace_from, false);
                        m_board->doFreeMove(m_freeplace_from,
                                    field_num);
                        m_freeplace_from = -1;
                  }
            }
      } else {
            bool select = false;
            QString err_msg;

            if(!m_current->fieldClicked(field_num, &select, err_msg)) {
                  add_log(myView::Warning, m_current->name()+": "
                              + (err_msg.length()
                                    ? err_msg
                                    : tr("Invalid move.")));
            } else {
                  m_board->selectField(field_num, select);
            }
      }
}


void myView::slotNextRound()
{
      if(m_aborted)
            return;

      m_player->setWhite(!m_player->isWhite());
      m_player->opponent()->setWhite(!m_player->isWhite());

      m_board->setColorWhite(m_player->isWhite());
      m_board->reset();

      unsigned int round = m_history->getTag(PdnGame::Round).toUInt() + 1;
      begin_game(round, m_history->isFreePlacement());
}


void myView::slotStopGame()
{
      m_player->stop();
      m_player->opponent()->stop();
}


void myView::stop_game(const QString& msg)
{
      m_game_over = true;
      m_aborted = true;

      QString text(tr("Game aborted.")+(!msg.isEmpty() ? "\n"+msg : ""));
      add_log(myView::System, text);

      emit working(false);
}


void myView::slot_move_done(const QString& board_str)
{
      if(m_history->isPaused())     // FIXME - ???
            return;

      perform_jumps(m_board->game()->toString(false), board_str);

      // show who is next?
      m_current = m_current->opponent();
      m_history->setCurrent(m_current->name());

      if(!m_current->isHuman()) {
            emit working(true);
      } else {
            emit working(false);
      }

      if(m_current->opponent()->isHuman() && !m_current->isHuman())
            QTimer::singleShot(MOVE_PAUSE, this,
                        SLOT(slot_move_done_step_two()));
      else
            slot_move_done_step_two();
}

void myView::slot_move_done_step_two()
{
      //
      m_current->yourTurn(m_board->game());

      if(check_game_over())
            emit working(false);
}


void myView::you_won(bool yes)
{
      if(yes&&m_player->isWhite() || !yes&&!m_player->isWhite()) {
            m_history->setTag(PdnGame::Result, "1-0");      // white wins
            add_log(myView::System, tr("White wins!"));
      } else {
            m_history->setTag(PdnGame::Result, "0-1");      // black wins
            add_log(myView::System, tr("Black wins!"));
      }

      emit working(false);
}


bool myView::openPdn(const QString& fn)
{
      emit working(false);

      m_current->stop();

      QString log_text;
      if(!m_history->openPdn(fn, log_text)) {
            return false;
      }

      if(log_text.length()) {
            add_log(myView::System, tr("Opened:")+" "+fn);
            add_log(myView::Error, log_text.trimmed());
            add_log(myView::Warning, tr("Warning! Some errors occured."));
      }

      return true;
}


bool myView::savePdn(const QString& fn)
{
      if(!m_history->savePdn(fn)) {
            qDebug() << __PRETTY_FUNCTION__ << "failed.";
            return false;
      }
      add_log(myView::System, tr("Saved:")+" "+fn);
      return true;
}


void myView::slot_new_mode(bool paused, bool freeplace)
{
      if(paused) {
            if(freeplace)
                  m_board->setCursor(Qt::PointingHandCursor);
            else
                  m_board->setCursor(Qt::ForbiddenCursor);
      } else {
            m_board->setCursor(Qt::ArrowCursor);
      }

      // resume game: ask info for who is next, black or white.XXX FIXME TODO
      if(!paused) {
            myPlayer* next = 0;
            if(m_history->moveCount()%2==0)
                  next = get_first_player();
            else
                  next = get_first_player()->opponent();

            m_current = next->opponent();
            slot_move_done(m_board->game()->toString(false));
      }
}


void myView::slot_preview_game(int rules)
{
      if(rules!=RUSSIAN && rules!=ENGLISH) {
            qDebug() << __PRETTY_FUNCTION__ << rules << "Wrong game type.";
            return;
      }

      m_board->setGame(rules);

      if(m_player->isWhite() && rules==RUSSIAN) {
            m_player->setName(m_history->getTag(PdnGame::White));
            m_player->opponent()->setName(m_history->getTag(PdnGame::Black));
      } else {
            m_player->setName(m_history->getTag(PdnGame::Black));
            m_player->opponent()->setName(m_history->getTag(PdnGame::White));
      }

      // FIXME
      m_player->setWhite(rules==RUSSIAN);// FIXME TODO
      m_player->opponent()->setWhite(!m_player->isWhite());
      m_board->setColorWhite(m_player->isWhite());
      m_board->adjustNotation(m_player->isWhite());
}


void myView::slot_apply_moves(const QString& moves)
{
      QStringList move_list= moves.split(MOVE_SPLIT, QString::SkipEmptyParts);

      m_board->reset();

      bool white_player = get_first_player()->isWhite();
      foreach(QString move, move_list) {
            m_board->doMove(move, white_player);
            white_player = !white_player;
      }

      // set current player who pulls next. assume is white.
      m_current = (m_player->isWhite() ? m_player : m_player->opponent());
      if(!white_player)
            m_current = m_current->opponent();
      m_history->setCurrent(m_current->name());
}


void myView::add_log(enum LogType type, const QString& text)
{
      QString str = text;
      str = str.replace('<', "&lt;");
      str = str.replace('>', "&gt;");

      QString tag_b, tag_e;
      switch(type) {
      case Error: tag_b="<ul><pre>"; tag_e="</pre></ul>"; break;
      case Warning:     tag_b="<b>"; tag_e="</b>"; break;
      case System:      tag_b="<font color=\"blue\">"; tag_e="</font>"; break;
      default:    break;
      }

      m_log->append(tag_b + str + tag_e);

      m_log->ensureCursorVisible();
}


void myView::perform_jumps(const QString& from_board, const QString& to_board)
{
      if(from_board==to_board) {
            return;
      }

      QString new_to_board = to_board;

      //qDebug("F:%s\nT:%s", from_board.latin1(), new_to_board.latin1());

      // diff
      QList<myDiff*> diff_list;

      // collect information
      for(int i=0; i<32; i++) {
            if(from_board[2*i]!=new_to_board[2*i]
                        || from_board[2*i+1]!=new_to_board[2*i+1]) {
                  myDiff* diff = new myDiff(i,
                              from_board.mid(2*i, 2).toInt(),
                              new_to_board.mid(2*i, 2).toInt());
                  diff_list.append(diff);

                  //qDebug(">%d: %d->%d", diff->m_pos, diff->m_from, diff->m_to);
            }
      }

      int from_pos = -1;
      int to_pos = -1;
      bool captured = (diff_list.count()>2);

      int man = -1;
      // find the dest. first: so we have the man moved.
      foreach(myDiff* diff, diff_list) {
            if(diff->m_to!=FREE) {
                  man = diff->m_to;
                  to_pos = diff->m_pos;
                  break;
            }
      }

      int king = -1;
      switch(man) {
      case MAN1:  king=KING1; break;
      case KING1: king=MAN1; break;
      case MAN2:  king=KING2; break;
      case KING2: king=MAN2; break;
      }
      // find src.
      foreach(myDiff* diff, diff_list) {
            if(diff->m_to==FREE) {
                  if(diff->m_from==man || diff->m_from==king) {
                        from_pos = diff->m_pos;
                        break;
                  }
            }
      } 

      /*
        qDebug("  to_pos=%d with man/king=%d from=%d", to_pos, man,
        from_pos);
        */

      // finally - animate :)
      QString move = m_board->doMove(from_pos, to_pos, m_current->isWhite());
      m_history->appendMove(move.replace("?", captured ? "x" : "-" ), "");

      qDeleteAll(diff_list);
}
 
void myView::setNotation(bool enabled, bool show_above)
{
      // TODO - intermediate function - remove somehow!
      m_board->setNotation(enabled, show_above);
}


void myView::setNotationFont(const QFont& f)
{
      // TODO - intermediate function - remove somehow!
      m_board->setNotationFont(f);
}


myPlayer* myView::get_first_player() const
{
      bool white = m_board->type()==RUSSIAN ? true : false;
      // it is white.
      if((white && m_player->isWhite()) || (!white && !m_player->isWhite()))
            return m_player;
      return m_player->opponent();
}



Generated by  Doxygen 1.6.0   Back to index