- Fixed compilation in debug mode

- Centralized navigation in the game history in the Navigation class
 - The Qt interface now has (very) basic controls to navigate in the history.
This commit is contained in:
Olivier Teulière 2008-11-23 16:55:28 +00:00
parent 9e330cf83b
commit 675b534e77
15 changed files with 374 additions and 120 deletions

View file

@ -43,6 +43,7 @@ libgame_a_SOURCES= \
board_cross.cpp \
board_search.cpp \
settings.cpp settings.h \
navigation.cpp navigation.h \
game.cpp game.h \
game_move_cmd.h game_move_cmd.cpp \
turn_cmd.cpp turn_cmd.h \

View file

@ -28,6 +28,11 @@
#ifdef DEBUG
# include <iostream>
# include <cstdlib>
using std::cerr;
using std::endl;
// Assertion macro: if the condition is not verified, print a message on stderr
// and stops execution, otherwise do nothing.
# define ASSERT(cond, msg) \

View file

@ -33,7 +33,6 @@
#include "game_move_cmd.h"
#include "ai_player.h"
#include "settings.h"
#include "turn_cmd.h"
#include "debug.h"
@ -111,7 +110,7 @@ int Duplicate::start()
for (unsigned int i = 0; i < getNPlayers(); i++)
{
Command *pCmd = new PlayerRackCmd(*m_players[i], newRack);
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
accessNavigation().addAndExecute(pCmd);
// Nobody has played yet in this round
m_hasPlayed[i] = false;
}
@ -161,7 +160,7 @@ void Duplicate::tryEndTurn()
void Duplicate::recordPlayerMove(const Move &iMove, Player &ioPlayer)
{
Command *pCmd = new PlayerMoveCmd(ioPlayer, iMove);
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
accessNavigation().addAndExecute(pCmd);
m_hasPlayed[ioPlayer.getId()] = true;
}
@ -216,7 +215,7 @@ void Duplicate::endTurn()
// Play the best word on the board
Command *pCmd = new GameMoveCmd(*this, bestMove,
getPlayer(imax).getLastRack(), imax);
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
accessNavigation().addAndExecute(pCmd);
// Leave the same reliquate to all players
// This is required by the start() method which will be called to
@ -227,11 +226,11 @@ void Duplicate::endTurn()
if (i != imax)
{
Command *pCmd = new PlayerRackCmd(*m_players[i], pld);
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
accessNavigation().addAndExecute(pCmd);
}
}
newTurn();
accessNavigation().newTurn();
// Start next turn...
start();

View file

@ -39,7 +39,6 @@
#include "ai_player.h"
#include "settings.h"
#include "turn.h"
#include "turn_cmd.h"
#include "debug.h"
@ -110,7 +109,7 @@ void FreeGame::playAI(unsigned int p)
void FreeGame::recordPlayerMove(const Move &iMove, Player &ioPlayer)
{
Command *pCmd = new PlayerMoveCmd(ioPlayer, iMove);
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
accessNavigation().addAndExecute(pCmd);
}
@ -124,7 +123,7 @@ int FreeGame::start()
const PlayedRack &newRack =
helperSetRackRandom(getPlayer(i).getCurrentRack(), false, RACK_NEW);
Command *pCmd = new PlayerRackCmd(*m_players[i], newRack);
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
accessNavigation().addAndExecute(pCmd);
}
m_currPlayer = 0;
@ -146,7 +145,7 @@ int FreeGame::endTurn()
Command *pCmd = new GameMoveCmd(*this, move,
getCurrentPlayer().getLastRack(),
m_currPlayer);
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
accessNavigation().addAndExecute(pCmd);
// Complete the rack for the player that just played
if (move.getType() == Move::VALID_ROUND ||
@ -158,7 +157,7 @@ int FreeGame::endTurn()
helperSetRackRandom(getCurrentPlayer().getCurrentRack(), false, RACK_NEW);
Command *pCmd = new PlayerRackCmd(*m_players[m_currPlayer],
newRack);
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
accessNavigation().addAndExecute(pCmd);
}
catch (EndGameException &e)
{
@ -171,7 +170,7 @@ int FreeGame::endTurn()
// Next player
nextPlayer();
newTurn();
accessNavigation().newTurn();
// If this player is an AI, make it play now
if (!getCurrentPlayer().isHuman())

View file

@ -55,8 +55,6 @@ Game::Game(const Dictionary &iDic):
m_points = 0;
m_currPlayer = 0;
m_finished = false;
m_currTurn = -1;
newTurn();
}
@ -66,10 +64,6 @@ Game::~Game()
{
delete p;
}
BOOST_FOREACH(Command *c, m_turnCommands)
{
delete c;
}
}
@ -85,6 +79,12 @@ int Game::back(unsigned int n)
if (m_history.getSize() < n)
throw GameException("Cannot go back that far");
for (unsigned int i = 0; i < n+1; ++i)
{
m_navigation.prevTurn();
}
m_navigation.clearFuture();
#if 0
for (unsigned int i = 0; i < n; i++)
{
prevPlayer();
@ -113,6 +113,7 @@ int Game::back(unsigned int n)
m_players[m_currPlayer]->removeLastTurn();
m_history.removeLastTurn();
}
#endif
return 0;
}
@ -198,7 +199,6 @@ void Game::realBag(Bag &ioBag) const
PlayedRack Game::helperSetRackRandom(const PlayedRack &iPld,
bool iCheck, set_rack_mode mode) const
{
ASSERT(p < getNPlayers(), "Wrong player number");
// FIXME: RACK_MANUAL shouldn't be in the enum
ASSERT(mode != RACK_MANUAL, "Invalid rack mode");
@ -618,51 +618,3 @@ int Game::checkPlayedWord(const wstring &iCoord,
return 0;
}
/*********************************************************
*********************************************************/
void Game::prevTurn()
{
if (m_currTurn > 0)
{
--m_currTurn;
m_turnCommands[m_currTurn]->undo();
}
}
void Game::nextTurn()
{
if (m_currTurn + 1 < m_turnCommands.size())
{
m_turnCommands[m_currTurn]->execute();
++m_currTurn;
}
}
void Game::firstTurn()
{
while (m_currTurn > 0)
{
prevTurn();
}
}
void Game::lastTurn()
{
while (m_currTurn + 1 < m_turnCommands.size())
{
nextTurn();
}
}
void Game::newTurn()
{
lastTurn();
m_turnCommands.push_back(new TurnCmd);
++m_currTurn;
}

View file

@ -28,6 +28,7 @@
#include "bag.h"
#include "board.h"
#include "history.h"
#include "navigation.h"
class Dictionary;
class Player;
@ -35,7 +36,6 @@ class PlayedRack;
class Round;
class Rack;
class Turn;
class TurnCmd;
using namespace std;
@ -222,10 +222,8 @@ public:
void addPoints(int iPoints) { m_points += iPoints; }
void prevTurn();
void nextTurn();
void firstTurn();
void lastTurn();
const Navigation & getNavigation() const { return m_navigation; }
Navigation & accessNavigation() { return m_navigation; }
private:
/// Variant
@ -239,8 +237,9 @@ private:
*/
History m_history;
int m_points;
Navigation m_navigation;
int m_points;
// TODO: check what should be private and what should be protected
protected:
@ -255,17 +254,12 @@ protected:
/// Bag
Bag m_bag;
vector<TurnCmd *> m_turnCommands;
unsigned int m_currTurn;
bool m_finished;
/*********************************************************
* Helper functions
*********************************************************/
void newTurn();
/**
* Complete the given rack randomly.
*

118
game/navigation.cpp Normal file
View file

@ -0,0 +1,118 @@
/*******************************************************************
* Eliot
* Copyright (C) 2008 Olivier Teulière
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*****************************************************************************/
#include <boost/foreach.hpp>
#include "navigation.h"
#include "turn_cmd.h"
#include "debug.h"
Navigation::Navigation()
: m_currTurn(0)
{
newTurn();
}
Navigation::~Navigation()
{
BOOST_FOREACH(Command *c, m_turnCommands)
{
delete c;
}
}
void Navigation::newTurn()
{
lastTurn();
m_turnCommands.push_back(new TurnCmd);
++m_currTurn;
}
void Navigation::addAndExecute(Command *iCmd)
{
ASSERT(isLastTurn(), "Trying to add a command to an old turn!");
ASSERT(m_currTurn >= 1, "Bug in the turns vector");
ASSERT(m_currTurn - 1 < m_turnCommands.size(), "Bug in the turns vector");
m_turnCommands[m_currTurn - 1]->addAndExecute(iCmd);
}
void Navigation::prevTurn()
{
if (m_currTurn > 0)
{
--m_currTurn;
m_turnCommands[m_currTurn]->undo();
}
}
void Navigation::nextTurn()
{
if (m_currTurn < m_turnCommands.size())
{
m_turnCommands[m_currTurn]->execute();
++m_currTurn;
}
}
void Navigation::firstTurn()
{
while (m_currTurn > 0)
{
prevTurn();
}
}
void Navigation::lastTurn()
{
while (m_currTurn < m_turnCommands.size())
{
nextTurn();
}
}
void Navigation::clearFuture()
{
// When there is no future, don't do anything
if (isLastTurn())
return;
for (unsigned int i = m_currTurn; i < m_turnCommands.size(); ++i)
{
delete m_turnCommands[i];
}
while (m_turnCommands.size() > m_currTurn)
{
m_turnCommands.pop_back();
}
newTurn();
// Sanity check
ASSERT(isLastTurn(),
"After removing the next turns, we should be at the last turn");
}

62
game/navigation.h Normal file
View file

@ -0,0 +1,62 @@
/*******************************************************************
* Eliot
* Copyright (C) 2008 Olivier Teulière
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*****************************************************************************/
#ifndef _NAVIGATION_H
#define _NAVIGATION_H
#include <vector>
class TurnCmd;
class Command;
using namespace std;
class Navigation
{
public:
Navigation();
~Navigation();
void newTurn();
void addAndExecute(Command *iCmd);
unsigned int getCurrTurn() const { return m_currTurn; }
bool isFirstTurn() const { return m_currTurn == 0; }
bool isLastTurn() const { return m_currTurn == m_turnCommands.size(); }
void firstTurn();
void prevTurn();
void nextTurn();
void lastTurn();
/**
* Get rid of the future turns of the game, the current turn
* becoming the last one.
*/
void clearFuture();
private:
vector<TurnCmd *> m_turnCommands;
unsigned int m_currTurn;
};
#endif

View file

@ -40,7 +40,6 @@
#include "game_move_cmd.h"
#include "training.h"
#include "encoding.h"
#include "turn_cmd.h"
#include "debug.h"
@ -60,7 +59,7 @@ void Training::setRackRandom(bool iCheck, set_rack_mode mode)
const PlayedRack &newRack =
helperSetRackRandom(getCurrentPlayer().getCurrentRack(), iCheck, mode);
Command *pCmd = new PlayerRackCmd(*m_players[m_currPlayer], newRack);
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
accessNavigation().addAndExecute(pCmd);
}
@ -131,7 +130,7 @@ void Training::recordPlayerMove(const Move &iMove, Player &ioPlayer)
// (called in this class in endTurn()).
// See the big comment in game.cpp, line 96
Command *pCmd = new PlayerMoveCmd(ioPlayer, iMove);
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
accessNavigation().addAndExecute(pCmd);
}
@ -154,8 +153,8 @@ void Training::endTurn()
Command *pCmd = new GameMoveCmd(*this, move,
getCurrentPlayer().getLastRack(),
m_currPlayer);
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
newTurn();
accessNavigation().addAndExecute(pCmd);
accessNavigation().newTurn();
}

View file

@ -24,6 +24,13 @@
#include "player.h"
TurnCmd::TurnCmd()
{
// Fake execution
execute();
}
TurnCmd::~TurnCmd()
{
BOOST_FOREACH(Command *cmd, m_commands)

View file

@ -35,6 +35,7 @@ using namespace std;
class TurnCmd: public Command
{
public:
TurnCmd();
virtual ~TurnCmd();
/**
@ -43,6 +44,8 @@ class TurnCmd: public Command
*/
void addAndExecute(Command *iCmd);
bool isEmpty() const { return m_commands.empty(); }
protected:
virtual void doExecute();
virtual void doUndo();

View file

@ -86,6 +86,9 @@ MainWindow::MainWindow(QWidget *iParent)
QObject::connect(this, SIGNAL(gameChanged(const Game*)),
this, SLOT(updateForGame(const Game*)));
QObject::connect(this, SIGNAL(gameUpdated()),
this, SLOT(refresh()));
refresh();
// Status bar
statusBar()->addWidget(new QLabel, 1);
@ -150,6 +153,15 @@ MainWindow::MainWindow(QWidget *iParent)
emit gameChangedNonConst(NULL);
emit gameChanged(NULL);
QObject::connect(m_ui.buttonFirst, SIGNAL(clicked()),
this, SLOT(onGameFirst()));
QObject::connect(m_ui.buttonPrev, SIGNAL(clicked()),
this, SLOT(onGamePrev()));
QObject::connect(m_ui.buttonNext, SIGNAL(clicked()),
this, SLOT(onGameNext()));
QObject::connect(m_ui.buttonLast, SIGNAL(clicked()),
this, SLOT(onGameLast()));
// Load dictionary
QSettings qs(ORGANIZATION, PACKAGE_NAME);
QString dicPath = qs.value(PrefsDialog::kINTF_DIC_PATH, "").toString();
@ -197,6 +209,31 @@ void MainWindow::destroyCurrentGame()
}
void MainWindow::refresh()
{
if (m_game == NULL)
{
m_ui.buttonFirst->setEnabled(false);
m_ui.buttonPrev->setEnabled(false);
m_ui.buttonNext->setEnabled(false);
m_ui.buttonLast->setEnabled(false);
// XXX: tmp
m_ui.labelTurnNb->setText("");
}
else
{
bool isFirstTurn = m_game->getNavigation().isFirstTurn();
bool isLastTurn = m_game->getNavigation().isLastTurn();
m_ui.buttonFirst->setEnabled(!isFirstTurn);
m_ui.buttonPrev->setEnabled(!isFirstTurn);
m_ui.buttonNext->setEnabled(!isLastTurn);
m_ui.buttonLast->setEnabled(!isLastTurn);
// XXX: tmp
m_ui.labelTurnNb->setText(QString("Turn: %1").arg(m_game->getNavigation().getCurrTurn()));
}
}
void MainWindow::updateForGame(const Game *iGame)
{
if (iGame == NULL)
@ -715,3 +752,43 @@ void MainWindow::onHelpAbout()
aboutBox->exec();
}
void MainWindow::onGameFirst()
{
if (m_game == NULL)
return;
m_game->accessNavigation().firstTurn();
emit gameUpdated();
}
void MainWindow::onGamePrev()
{
if (m_game == NULL)
return;
m_game->accessNavigation().prevTurn();
emit gameUpdated();
}
void MainWindow::onGameNext()
{
if (m_game == NULL)
return;
m_game->accessNavigation().nextTurn();
emit gameUpdated();
}
void MainWindow::onGameLast()
{
if (m_game == NULL)
return;
m_game->accessNavigation().lastTurn();
emit gameUpdated();
}

View file

@ -73,6 +73,14 @@ private slots:
void onWindowsDicTools();
void onHelpAbout();
void onGameFirst();
void onGamePrev();
void onGameNext();
void onGameLast();
/** Perform some updates when the game is updated */
void refresh();
/**
* Perform several updates when the game changes (title bar, status bar,
* grey out some menu items, ...)

View file

@ -17,15 +17,59 @@
<normaloff>:/images/eliot.xpm</normaloff>:/images/eliot.xpm</iconset>
</property>
<widget class="QWidget" name="centralwidget" >
<property name="geometry" >
<rect>
<x>0</x>
<y>26</y>
<width>747</width>
<height>568</height>
</rect>
</property>
<layout class="QVBoxLayout" >
<item>
<layout class="QHBoxLayout" name="horizontalLayout" >
<item>
<widget class="QPushButton" name="buttonFirst" >
<property name="text" >
<string>First</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonPrev" >
<property name="text" >
<string>Prev</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonNext" >
<property name="text" >
<string>Next</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonLast" >
<property name="text" >
<string>Last</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelTurnNb" >
<property name="text" >
<string>Turn: </string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer" >
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" >
<item>
@ -72,20 +116,11 @@
<x>0</x>
<y>0</y>
<width>747</width>
<height>26</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar" >
<property name="geometry" >
<rect>
<x>0</x>
<y>594</y>
<width>747</width>
<height>23</height>
<height>25</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar" />
<action name="action_WindowsBag" >
<property name="checkable" >
<bool>true</bool>
@ -146,4 +181,5 @@
<resources>
<include location="../eliot.qrc" />
</resources>
<connections/>
</ui>

View file

@ -576,16 +576,16 @@ void loop_training(Training &iGame)
switch (token[0])
{
case L'p':
iGame.prevTurn();
iGame.accessNavigation().prevTurn();
break;
case L'n':
iGame.nextTurn();
iGame.accessNavigation().nextTurn();
break;
case L'f':
iGame.firstTurn();
iGame.accessNavigation().firstTurn();
break;
case L'l':
iGame.lastTurn();
iGame.accessNavigation().lastTurn();
break;
}
}
@ -705,16 +705,16 @@ void loop_freegame(FreeGame &iGame)
switch (token[0])
{
case L'p':
iGame.prevTurn();
iGame.accessNavigation().prevTurn();
break;
case L'n':
iGame.nextTurn();
iGame.accessNavigation().nextTurn();
break;
case L'f':
iGame.firstTurn();
iGame.accessNavigation().firstTurn();
break;
case L'l':
iGame.lastTurn();
iGame.accessNavigation().lastTurn();
break;
}
}
@ -842,16 +842,16 @@ void loop_duplicate(Duplicate &iGame)
switch (token[0])
{
case L'p':
iGame.prevTurn();
iGame.accessNavigation().prevTurn();
break;
case L'n':
iGame.nextTurn();
iGame.accessNavigation().nextTurn();
break;
case L'f':
iGame.firstTurn();
iGame.accessNavigation().firstTurn();
break;
case L'l':
iGame.lastTurn();
iGame.accessNavigation().lastTurn();
break;
}
}
@ -1120,9 +1120,3 @@ int main(int argc, char *argv[])
return 0;
}
/// Local Variables:
/// mode: c++
/// mode: hs-minor
/// c-basic-offset: 4
/// indent-tabs-mode: nil
/// End: