mirror of
git://git.savannah.nongnu.org/eliot.git
synced 2025-01-18 10:26:15 +01:00
- The main game modifications (playing a move, setting a new rack, ...) are now done using the Command design pattern, which alows undoing these changes easily.
- Added support for navigation in the game history to the text interface (not unit-tested yet)
This commit is contained in:
parent
4b766d2622
commit
bce4ce4604
29 changed files with 1037 additions and 348 deletions
|
@ -21,30 +21,35 @@ noinst_LIBRARIES = libgame.a
|
|||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/dic -I../intl -I$(top_srcdir)/intl @LIBCONFIG_CFLAGS@
|
||||
|
||||
libgame_a_SOURCES= \
|
||||
libgame_a_SOURCES= \
|
||||
game_exception.cpp game_exception.h \
|
||||
rack.cpp rack.h \
|
||||
pldrack.cpp pldrack.h \
|
||||
round.cpp round.h \
|
||||
move.cpp move.h \
|
||||
results.cpp results.h \
|
||||
bag.cpp bag.h \
|
||||
player.cpp player.h \
|
||||
ai_player.h \
|
||||
ai_percent.cpp ai_percent.h \
|
||||
coord.cpp coord.h \
|
||||
cross.cpp cross.h \
|
||||
board.cpp board.h \
|
||||
board_cross.cpp \
|
||||
board_search.cpp \
|
||||
settings.cpp settings.h \
|
||||
turn.cpp turn.h \
|
||||
history.cpp history.h \
|
||||
game.cpp game.h \
|
||||
game_io.cpp \
|
||||
duplicate.cpp duplicate.h \
|
||||
freegame.cpp freegame.h \
|
||||
training.cpp training.h \
|
||||
command.h command.cpp \
|
||||
rack.cpp rack.h \
|
||||
pldrack.cpp pldrack.h \
|
||||
round.cpp round.h \
|
||||
move.cpp move.h \
|
||||
results.cpp results.h \
|
||||
bag.cpp bag.h \
|
||||
turn.cpp turn.h \
|
||||
history.cpp history.h \
|
||||
player.cpp player.h \
|
||||
player_move_cmd.cpp player_move_cmd.h \
|
||||
player_rack_cmd.cpp player_rack_cmd.h \
|
||||
ai_player.h \
|
||||
ai_percent.cpp ai_percent.h \
|
||||
coord.cpp coord.h \
|
||||
cross.cpp cross.h \
|
||||
board.cpp board.h \
|
||||
board_cross.cpp \
|
||||
board_search.cpp \
|
||||
settings.cpp settings.h \
|
||||
game.cpp game.h \
|
||||
game_move_cmd.h game_move_cmd.cpp \
|
||||
turn_cmd.cpp turn_cmd.h \
|
||||
duplicate.cpp duplicate.h \
|
||||
freegame.cpp freegame.h \
|
||||
training.cpp training.h \
|
||||
game_factory.cpp game_factory.h \
|
||||
game_io.cpp \
|
||||
debug.h
|
||||
|
||||
|
|
45
game/command.cpp
Normal file
45
game/command.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*****************************************************************************
|
||||
* 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 "command.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
Command::Command()
|
||||
: m_executed(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void Command::execute()
|
||||
{
|
||||
ASSERT(!m_executed, "Command already executed!");
|
||||
doExecute();
|
||||
m_executed = true;
|
||||
}
|
||||
|
||||
|
||||
void Command::undo()
|
||||
{
|
||||
ASSERT(m_executed, "Command already undone!");
|
||||
doUndo();
|
||||
m_executed = false;
|
||||
}
|
||||
|
66
game/command.h
Normal file
66
game/command.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*****************************************************************************
|
||||
* 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 _COMMAND_H
|
||||
#define _COMMAND_H
|
||||
|
||||
|
||||
/**
|
||||
* This abstract class is the parent of all classes implementing the Command
|
||||
* design pattern.
|
||||
*/
|
||||
class Command
|
||||
{
|
||||
public:
|
||||
Command();
|
||||
virtual ~Command() {}
|
||||
|
||||
/**
|
||||
* Execute the command. This can later be undone using the undo()
|
||||
* method.
|
||||
*
|
||||
* You are not allowed to call this method twice before calling
|
||||
* undo() first.
|
||||
*/
|
||||
void execute();
|
||||
|
||||
/**
|
||||
* Undo the previous call to execute().
|
||||
*
|
||||
* You are not allowed to undo a command which is not executed yet.
|
||||
*/
|
||||
void undo();
|
||||
|
||||
/**
|
||||
* Return true if the command has been executed (i.e. if it is
|
||||
* allowed to call undo()), false otherwise.
|
||||
*/
|
||||
bool isExecuted() const { return m_executed; }
|
||||
|
||||
protected:
|
||||
virtual void doExecute() = 0;
|
||||
virtual void doUndo() = 0;
|
||||
|
||||
private:
|
||||
bool m_executed;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière
|
||||
* Copyright (C) 2005-2008 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -28,8 +28,12 @@
|
|||
#include "pldrack.h"
|
||||
#include "results.h"
|
||||
#include "player.h"
|
||||
#include "player_move_cmd.h"
|
||||
#include "player_rack_cmd.h"
|
||||
#include "game_move_cmd.h"
|
||||
#include "ai_player.h"
|
||||
#include "settings.h"
|
||||
#include "turn_cmd.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
|
@ -51,15 +55,16 @@ int Duplicate::play(const wstring &iCoord, const wstring &iWord)
|
|||
|
||||
// If we reach this point, either the move is valid and we can use the
|
||||
// "round" variable, or it is invalid but played nevertheless
|
||||
Player &currPlayer = *m_players[m_currPlayer];
|
||||
if (res == 0)
|
||||
{
|
||||
// Everything is OK, we can play the word
|
||||
recordPlayerMove(Move(round), m_currPlayer);
|
||||
recordPlayerMove(Move(round), currPlayer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Record the invalid move of the player
|
||||
recordPlayerMove(Move(iWord, iCoord), m_currPlayer);
|
||||
recordPlayerMove(Move(iWord, iCoord), currPlayer);
|
||||
}
|
||||
|
||||
// Little hack to handle duplicate games with only AI players.
|
||||
|
@ -78,7 +83,7 @@ void Duplicate::playAI(unsigned int p)
|
|||
ASSERT(player != NULL, "AI requested for a human player");
|
||||
|
||||
player->compute(getDic(), getBoard(), getHistory().beforeFirstRound());
|
||||
const Move move = player->getMove();
|
||||
const Move &move = player->getMove();
|
||||
if (move.getType() == Move::CHANGE_LETTERS ||
|
||||
move.getType() == Move::PASS)
|
||||
{
|
||||
|
@ -86,7 +91,7 @@ void Duplicate::playAI(unsigned int p)
|
|||
ASSERT(false, "AI tried to cheat!");
|
||||
}
|
||||
|
||||
recordPlayerMove(move, p);
|
||||
recordPlayerMove(move, *player);
|
||||
}
|
||||
|
||||
|
||||
|
@ -102,7 +107,14 @@ int Duplicate::start()
|
|||
{
|
||||
const PlayedRack &newRack =
|
||||
helperSetRackRandom(getCurrentPlayer().getCurrentRack(), true, RACK_NEW);
|
||||
m_players[m_currPlayer]->setCurrentRack(newRack);
|
||||
// All the players have the same rack
|
||||
for (unsigned int i = 0; i < getNPlayers(); i++)
|
||||
{
|
||||
Command *pCmd = new PlayerRackCmd(*m_players[i], newRack);
|
||||
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
|
||||
// Nobody has played yet in this round
|
||||
m_hasPlayed[i] = false;
|
||||
}
|
||||
}
|
||||
catch (EndGameException &e)
|
||||
{
|
||||
|
@ -110,18 +122,6 @@ int Duplicate::start()
|
|||
return 1;
|
||||
}
|
||||
|
||||
const PlayedRack& pld = m_players[m_currPlayer]->getCurrentRack();
|
||||
// All the players have the same rack
|
||||
for (unsigned int i = 0; i < getNPlayers(); i++)
|
||||
{
|
||||
if (i != m_currPlayer)
|
||||
{
|
||||
m_players[i]->setCurrentRack(pld);
|
||||
}
|
||||
// Nobody has played yet in this round
|
||||
m_hasPlayed[i] = false;
|
||||
}
|
||||
|
||||
// Little hack to handle duplicate games with only AI players.
|
||||
// This will have no effect when there is at least one human player
|
||||
tryEndTurn();
|
||||
|
@ -158,20 +158,12 @@ void Duplicate::tryEndTurn()
|
|||
}
|
||||
|
||||
|
||||
void Duplicate::recordPlayerMove(const Move &iMove, unsigned int p)
|
||||
void Duplicate::recordPlayerMove(const Move &iMove, Player &ioPlayer)
|
||||
{
|
||||
ASSERT(p < getNPlayers(), "Wrong player number");
|
||||
Command *pCmd = new PlayerMoveCmd(ioPlayer, iMove);
|
||||
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
|
||||
|
||||
// Get what was the rack for the current turn
|
||||
Rack oldRack;
|
||||
m_players[p]->getCurrentRack().getRack(oldRack);
|
||||
// Compute the new rack
|
||||
const Rack &newRack = helperComputeRackForMove(oldRack, iMove);
|
||||
|
||||
// Update the rack and the score of the playing player
|
||||
m_players[p]->endTurn(iMove, getHistory().getSize(), newRack);
|
||||
|
||||
m_hasPlayed[p] = true;
|
||||
m_hasPlayed[ioPlayer.getId()] = true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -222,7 +214,9 @@ void Duplicate::endTurn()
|
|||
}
|
||||
|
||||
// Play the best word on the board
|
||||
helperPlayMove(imax, bestMove);
|
||||
Command *pCmd = new GameMoveCmd(*this, bestMove,
|
||||
getPlayer(imax).getLastRack(), imax);
|
||||
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
|
||||
|
||||
// Leave the same reliquate to all players
|
||||
// This is required by the start() method which will be called to
|
||||
|
@ -232,10 +226,13 @@ void Duplicate::endTurn()
|
|||
{
|
||||
if (i != imax)
|
||||
{
|
||||
m_players[i]->setCurrentRack(pld);
|
||||
Command *pCmd = new PlayerRackCmd(*m_players[i], pld);
|
||||
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
|
||||
}
|
||||
}
|
||||
|
||||
newTurn();
|
||||
|
||||
// Start next turn...
|
||||
start();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière
|
||||
* Copyright (C) 2005-2008 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -23,6 +23,8 @@
|
|||
|
||||
#include "game.h"
|
||||
|
||||
class Player;
|
||||
|
||||
using std::string;
|
||||
using std::wstring;
|
||||
|
||||
|
@ -99,7 +101,7 @@ private:
|
|||
Duplicate(const Dictionary &iDic);
|
||||
|
||||
/// Record a player move
|
||||
void recordPlayerMove(const Move &iMove, unsigned int p);
|
||||
void recordPlayerMove(const Move &iMove, Player &ioPlayer);
|
||||
|
||||
/// Make the AI player whose ID is p play its turn
|
||||
void playAI(unsigned int p);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière
|
||||
* Copyright (C) 2005-2008 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -33,9 +33,13 @@
|
|||
#include "pldrack.h"
|
||||
#include "results.h"
|
||||
#include "player.h"
|
||||
#include "player_move_cmd.h"
|
||||
#include "player_rack_cmd.h"
|
||||
#include "game_move_cmd.h"
|
||||
#include "ai_player.h"
|
||||
#include "settings.h"
|
||||
#include "turn.h"
|
||||
#include "turn_cmd.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
|
@ -63,14 +67,14 @@ int FreeGame::play(const wstring &iCoord, const wstring &iWord)
|
|||
Move move(round);
|
||||
|
||||
// Update the rack and the score of the current player
|
||||
recordPlayerMove(move, m_currPlayer);
|
||||
recordPlayerMove(move, *m_players[m_currPlayer]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Move move(iWord, iCoord);
|
||||
|
||||
// Record the invalid move of the player
|
||||
recordPlayerMove(move, m_currPlayer);
|
||||
recordPlayerMove(move, *m_players[m_currPlayer]);
|
||||
}
|
||||
|
||||
// Next turn
|
||||
|
@ -88,32 +92,25 @@ void FreeGame::playAI(unsigned int p)
|
|||
AIPlayer *player = static_cast<AIPlayer*>(m_players[p]);
|
||||
|
||||
player->compute(getDic(), getBoard(), getHistory().beforeFirstRound());
|
||||
const Move move = player->getMove();
|
||||
const Move &move = player->getMove();
|
||||
if (move.getType() == Move::CHANGE_LETTERS ||
|
||||
move.getType() == Move::PASS)
|
||||
{
|
||||
ASSERT(checkPass(move.getChangedLetters(), p) == 0, "AI tried to cheat!");
|
||||
ASSERT(checkPass(*player, move.getChangedLetters()) == 0,
|
||||
"AI tried to cheat!");
|
||||
}
|
||||
|
||||
// Update the rack and the score of the current player
|
||||
recordPlayerMove(move, p);
|
||||
recordPlayerMove(move, *player);
|
||||
|
||||
endTurn();
|
||||
}
|
||||
|
||||
|
||||
void FreeGame::recordPlayerMove(const Move &iMove, unsigned int p)
|
||||
void FreeGame::recordPlayerMove(const Move &iMove, Player &ioPlayer)
|
||||
{
|
||||
ASSERT(p < getNPlayers(), "Wrong player number");
|
||||
|
||||
// Get what was the rack for the current turn
|
||||
Rack oldRack;
|
||||
m_players[p]->getCurrentRack().getRack(oldRack);
|
||||
// Compute the new rack
|
||||
const Rack &newRack = helperComputeRackForMove(oldRack, iMove);
|
||||
|
||||
// Record the invalid move of the player
|
||||
m_players[p]->endTurn(iMove, getHistory().getSize(), newRack);
|
||||
Command *pCmd = new PlayerMoveCmd(ioPlayer, iMove);
|
||||
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
|
||||
}
|
||||
|
||||
|
||||
|
@ -126,7 +123,8 @@ int FreeGame::start()
|
|||
{
|
||||
const PlayedRack &newRack =
|
||||
helperSetRackRandom(getPlayer(i).getCurrentRack(), false, RACK_NEW);
|
||||
m_players[i]->setCurrentRack(newRack);
|
||||
Command *pCmd = new PlayerRackCmd(*m_players[i], newRack);
|
||||
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
|
||||
}
|
||||
|
||||
m_currPlayer = 0;
|
||||
|
@ -143,9 +141,12 @@ int FreeGame::start()
|
|||
|
||||
int FreeGame::endTurn()
|
||||
{
|
||||
const Move &move = m_players[m_currPlayer]->getLastMove();
|
||||
const Move &move = getCurrentPlayer().getLastMove();
|
||||
// Update the game
|
||||
helperPlayMove(m_currPlayer, move);
|
||||
Command *pCmd = new GameMoveCmd(*this, move,
|
||||
getCurrentPlayer().getLastRack(),
|
||||
m_currPlayer);
|
||||
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
|
||||
|
||||
// Complete the rack for the player that just played
|
||||
if (move.getType() == Move::VALID_ROUND ||
|
||||
|
@ -155,7 +156,9 @@ int FreeGame::endTurn()
|
|||
{
|
||||
const PlayedRack &newRack =
|
||||
helperSetRackRandom(getCurrentPlayer().getCurrentRack(), false, RACK_NEW);
|
||||
m_players[m_currPlayer]->setCurrentRack(newRack);
|
||||
Command *pCmd = new PlayerRackCmd(*m_players[m_currPlayer],
|
||||
newRack);
|
||||
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
|
||||
}
|
||||
catch (EndGameException &e)
|
||||
{
|
||||
|
@ -168,8 +171,10 @@ int FreeGame::endTurn()
|
|||
// Next player
|
||||
nextPlayer();
|
||||
|
||||
newTurn();
|
||||
|
||||
// If this player is an AI, make it play now
|
||||
if (!m_players[m_currPlayer]->isHuman())
|
||||
if (!getCurrentPlayer().isHuman())
|
||||
{
|
||||
playAI(m_currPlayer);
|
||||
}
|
||||
|
@ -216,10 +221,9 @@ void FreeGame::endGame()
|
|||
}
|
||||
|
||||
|
||||
int FreeGame::checkPass(const wstring &iToChange, unsigned int p) const
|
||||
int FreeGame::checkPass(const Player &iPlayer,
|
||||
const wstring &iToChange) const
|
||||
{
|
||||
ASSERT(p < getNPlayers(), "Wrong player number");
|
||||
|
||||
// Check that the game is not finished
|
||||
if (m_finished)
|
||||
return 3;
|
||||
|
@ -243,8 +247,7 @@ int FreeGame::checkPass(const wstring &iToChange, unsigned int p) const
|
|||
}
|
||||
|
||||
// Check that the letters are all present in the player's rack
|
||||
Player *player = m_players[p];
|
||||
PlayedRack pld = player->getCurrentRack();
|
||||
const PlayedRack &pld = iPlayer.getCurrentRack();
|
||||
Rack rack;
|
||||
pld.getRack(rack);
|
||||
BOOST_FOREACH(wchar_t wch, iToChange)
|
||||
|
@ -270,13 +273,14 @@ int FreeGame::checkPass(const wstring &iToChange, unsigned int p) const
|
|||
|
||||
int FreeGame::pass(const wstring &iToChange)
|
||||
{
|
||||
int res = checkPass(iToChange, m_currPlayer);
|
||||
Player &player = *m_players[m_currPlayer];
|
||||
int res = checkPass(player, iToChange);
|
||||
if (res != 0)
|
||||
return res;
|
||||
|
||||
Move move(iToChange);
|
||||
// End the player's turn
|
||||
recordPlayerMove(move, m_currPlayer);
|
||||
recordPlayerMove(move, player);
|
||||
|
||||
// Next game turn
|
||||
endTurn();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière
|
||||
* Copyright (C) 2005-2008 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -24,6 +24,8 @@
|
|||
#include "game.h"
|
||||
#include "tile.h"
|
||||
|
||||
class Player;
|
||||
|
||||
using std::string;
|
||||
using std::wstring;
|
||||
using std::vector;
|
||||
|
@ -87,7 +89,7 @@ private:
|
|||
void playAI(unsigned int p);
|
||||
|
||||
/// Record a player move
|
||||
void recordPlayerMove(const Move &iMove, unsigned int p);
|
||||
void recordPlayerMove(const Move &iMove, Player &ioPlayer);
|
||||
|
||||
/// Finish the current turn
|
||||
int endTurn();
|
||||
|
@ -99,7 +101,7 @@ private:
|
|||
* Check whether it is legal to change the letters of iToChange.
|
||||
* The return codes are the same as the ones on the pass() method
|
||||
*/
|
||||
int checkPass(const wstring &iToChange, unsigned int p) const;
|
||||
int checkPass(const Player &iPlayer, const wstring &iToChange) const;
|
||||
};
|
||||
|
||||
#endif /* _FREEGAME_H_ */
|
||||
|
|
217
game/game.cpp
217
game/game.cpp
|
@ -1,6 +1,6 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Copyright (C) 1999-2008 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
|
@ -41,6 +41,7 @@
|
|||
#include "turn.h"
|
||||
#include "encoding.h"
|
||||
#include "game_exception.h"
|
||||
#include "turn_cmd.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
|
@ -54,6 +55,8 @@ Game::Game(const Dictionary &iDic):
|
|||
m_points = 0;
|
||||
m_currPlayer = 0;
|
||||
m_finished = false;
|
||||
m_currTurn = -1;
|
||||
newTurn();
|
||||
}
|
||||
|
||||
|
||||
|
@ -63,6 +66,10 @@ Game::~Game()
|
|||
{
|
||||
delete p;
|
||||
}
|
||||
BOOST_FOREACH(Command *c, m_turnCommands)
|
||||
{
|
||||
delete c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -73,166 +80,6 @@ const Player& Game::getPlayer(unsigned int iNum) const
|
|||
}
|
||||
|
||||
|
||||
Rack Game::helperComputeRackForMove(const Rack &iRack, const Move &iMove)
|
||||
{
|
||||
// Start from the given rack
|
||||
Rack newRack = iRack;
|
||||
|
||||
if (iMove.getType() == Move::VALID_ROUND)
|
||||
{
|
||||
// Remove the played tiles from the rack
|
||||
const Round &round = iMove.getRound();
|
||||
for (unsigned int i = 0; i < round.getWordLen(); i++)
|
||||
{
|
||||
if (round.isPlayedFromRack(i))
|
||||
{
|
||||
if (round.isJoker(i))
|
||||
newRack.remove(Tile::Joker());
|
||||
else
|
||||
newRack.remove(round.getTile(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (iMove.getType() == Move::CHANGE_LETTERS)
|
||||
{
|
||||
// Remove the changed tiles from the rack
|
||||
const wstring & changed = iMove.getChangedLetters();
|
||||
BOOST_FOREACH(wchar_t ch, changed)
|
||||
{
|
||||
newRack.remove(Tile(ch));
|
||||
}
|
||||
}
|
||||
|
||||
return newRack;
|
||||
}
|
||||
|
||||
|
||||
void Game::helperPlayMove(unsigned int iPlayerId, const Move &iMove)
|
||||
{
|
||||
// Get the original rack from the player history
|
||||
const PlayedRack &oldPldRack = getPlayer(iPlayerId).getLastRack();
|
||||
Rack oldRack;
|
||||
oldPldRack.getRack(oldRack);
|
||||
const Rack &newRack = helperComputeRackForMove(oldRack, iMove);
|
||||
|
||||
// History of the game
|
||||
m_history.setCurrentRack(oldPldRack);
|
||||
m_history.playMove(iPlayerId, m_history.getSize(), iMove, newRack);
|
||||
|
||||
// Points
|
||||
m_points += iMove.getScore();
|
||||
|
||||
// For moves corresponding to a valid round, we have much more
|
||||
// work to do...
|
||||
if (iMove.getType() == Move::VALID_ROUND)
|
||||
{
|
||||
helperPlayRound(iPlayerId, iMove.getRound());
|
||||
}
|
||||
#ifdef REAL_BAG_MODE
|
||||
else if (iMove.getType() == Move::CHANGE_LETTERS)
|
||||
{
|
||||
// Put the changed letters back into the bag
|
||||
BOOST_FOREACH(wchar_t ch, iMove.getChangedLetters())
|
||||
{
|
||||
m_bag.replaceTile(Tile(ch));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Game::helperPlayRound(unsigned int iPlayerId, const Round &iRound)
|
||||
{
|
||||
// Copy the round, because we may need to modify it (case of
|
||||
// the joker games).
|
||||
Round round = iRound;
|
||||
|
||||
// Before updating the bag and the board, if we are playing a "joker game",
|
||||
// we replace in the round the joker by the letter it represents
|
||||
// This is currently done by a succession of ugly hacks :-/
|
||||
if (m_variant == kJOKER)
|
||||
{
|
||||
for (unsigned int i = 0; i < round.getWordLen(); i++)
|
||||
{
|
||||
if (round.isPlayedFromRack(i) && round.isJoker(i))
|
||||
{
|
||||
// Is the represented letter still available in the bag?
|
||||
// XXX: this way to get the represented letter sucks...
|
||||
Tile t(towupper(round.getTile(i).toChar()));
|
||||
#ifdef REAL_BAG_MODE
|
||||
Bag &bag = m_bag;
|
||||
#else
|
||||
Bag bag(m_dic);
|
||||
realBag(bag);
|
||||
// FIXME: realBag() does not give us a real bag in this
|
||||
// particular case! This is because Player::endTurn() is called
|
||||
// before Game::helperPlayRound(), which means that the rack
|
||||
// of the player is updated, while the word is not actually
|
||||
// played on the board yet. Since realBag() relies on
|
||||
// Player::getCurrentRack(), it doesn't remove the letters of
|
||||
// the current player, which are in fact available through
|
||||
// Player::getLastRack().
|
||||
// That's why we have to replace the letters of the current
|
||||
// rack and remove the ones from the previous rack...
|
||||
// There is a big design problem here, but i am unsure what is
|
||||
// the best way to fix it.
|
||||
vector<Tile> tiles;
|
||||
getPlayer(iPlayerId).getCurrentRack().getAllTiles(tiles);
|
||||
BOOST_FOREACH(const Tile &tile, tiles)
|
||||
{
|
||||
bag.replaceTile(tile);
|
||||
}
|
||||
getPlayer(iPlayerId).getLastRack().getAllTiles(tiles);
|
||||
BOOST_FOREACH(const Tile &tile, tiles)
|
||||
{
|
||||
bag.takeTile(tile);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bag.in(t))
|
||||
{
|
||||
round.setTile(i, t);
|
||||
// FIXME: This shouldn't be necessary, this is only
|
||||
// needed because of the stupid way of handling jokers in
|
||||
// rounds
|
||||
round.setJoker(i, false);
|
||||
}
|
||||
|
||||
// In a joker game we should have only 1 joker in the rack
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef REAL_BAG_MODE
|
||||
#else
|
||||
// Update the bag
|
||||
// We remove tiles from the bag only when they are played
|
||||
// on the board. When going back in the game, we must only
|
||||
// replace played tiles.
|
||||
// We test a rack when it is set but tiles are left in the bag.
|
||||
for (unsigned int i = 0; i < round.getWordLen(); i++)
|
||||
{
|
||||
if (round.isPlayedFromRack(i))
|
||||
{
|
||||
if (round.isJoker(i))
|
||||
{
|
||||
m_bag.takeTile(Tile::Joker());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bag.takeTile(round.getTile(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update the board
|
||||
m_board.addRound(m_dic, round);
|
||||
}
|
||||
|
||||
|
||||
int Game::back(unsigned int n)
|
||||
{
|
||||
if (m_history.getSize() < n)
|
||||
|
@ -771,3 +618,51 @@ 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;
|
||||
}
|
||||
|
||||
|
|
74
game/game.h
74
game/game.h
|
@ -1,6 +1,6 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Copyright (C) 1999-2008 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
|
@ -35,6 +35,7 @@ class PlayedRack;
|
|||
class Round;
|
||||
class Rack;
|
||||
class Turn;
|
||||
class TurnCmd;
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -97,14 +98,28 @@ public:
|
|||
|
||||
/// Get the board
|
||||
const Board& getBoard() const { return m_board; }
|
||||
Board & accessBoard() { return m_board; }
|
||||
/// Get the bag
|
||||
#ifdef REAL_BAG_MODE
|
||||
Bag getBag() const;
|
||||
#else
|
||||
const Bag& getBag() const { return m_bag; }
|
||||
Bag & accessBag() { return m_bag; }
|
||||
/**
|
||||
* The realBag is the current bag minus all the racks
|
||||
* present in the game. It represents the actual
|
||||
* letters that are left in the bag.
|
||||
* FIXME: in Duplicate mode, this method uses m_currPlayer to find the
|
||||
* rack of the player. Since not all the players played the same word,
|
||||
* it is important to set m_currPlayer accurately before!
|
||||
*/
|
||||
void realBag(Bag &iBag) const;
|
||||
#endif
|
||||
|
||||
|
||||
/// Get the history of the game */
|
||||
const History& getHistory() const { return m_history; }
|
||||
History & accessHistory() { return m_history; }
|
||||
|
||||
/***************
|
||||
* Methods to access players.
|
||||
|
@ -205,16 +220,14 @@ public:
|
|||
|
||||
enum set_rack_mode {RACK_ALL, RACK_NEW, RACK_MANUAL};
|
||||
|
||||
void addPoints(int iPoints) { m_points += iPoints; }
|
||||
|
||||
protected:
|
||||
/// All the players, indexed by their ID
|
||||
vector<Player*> m_players;
|
||||
/// ID of the "current" player
|
||||
unsigned int m_currPlayer;
|
||||
void prevTurn();
|
||||
void nextTurn();
|
||||
void firstTurn();
|
||||
void lastTurn();
|
||||
|
||||
// TODO: check what should be private and what should be protected
|
||||
private:
|
||||
|
||||
/// Variant
|
||||
GameVariant m_variant;
|
||||
|
||||
|
@ -228,28 +241,30 @@ private:
|
|||
|
||||
int m_points;
|
||||
|
||||
|
||||
// TODO: check what should be private and what should be protected
|
||||
protected:
|
||||
/// All the players, indexed by their ID
|
||||
vector<Player*> m_players;
|
||||
/// ID of the "current" player
|
||||
unsigned int m_currPlayer;
|
||||
|
||||
/// Board
|
||||
Board m_board;
|
||||
|
||||
/// Bag
|
||||
Bag m_bag;
|
||||
|
||||
vector<TurnCmd *> m_turnCommands;
|
||||
unsigned int m_currTurn;
|
||||
|
||||
bool m_finished;
|
||||
|
||||
/*********************************************************
|
||||
* Helper functions
|
||||
*********************************************************/
|
||||
|
||||
/**
|
||||
* Return the rack obtained from the given one, after playing the
|
||||
* given move.
|
||||
* The move is supposed to be possible for the given rack.
|
||||
*/
|
||||
static Rack helperComputeRackForMove(const Rack &iOldRack, const Move &iMove);
|
||||
|
||||
/** Play a Move for the given player, updating game history */
|
||||
void helperPlayMove(unsigned int iPlayerId, const Move &iMove);
|
||||
void newTurn();
|
||||
|
||||
/**
|
||||
* Complete the given rack randomly.
|
||||
|
@ -299,27 +314,14 @@ protected:
|
|||
*/
|
||||
bool rackInBag(const Rack &iRack, const Bag &iBag) const;
|
||||
|
||||
#ifdef REAL_BAG_MODE
|
||||
#else
|
||||
/**
|
||||
* The realBag is the current bag minus all the racks
|
||||
* present in the game. It represents the actual
|
||||
* letters that are left in the bag.
|
||||
* FIXME: in Duplicate mode, this method uses m_currPlayer to find the
|
||||
* rack of the player. Since not all the players played the same word,
|
||||
* it is important to set m_currPlayer accurately before!
|
||||
*/
|
||||
void realBag(Bag &iBag) const;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This function checks whether it is legal to play the given word at the
|
||||
* given coordinates. If so, the function fills a Round object, also given
|
||||
* as a parameter.
|
||||
* Possible return values: same as the play() method
|
||||
*/
|
||||
int checkPlayedWord(const wstring &iCoord,
|
||||
const wstring &iWord, Round &oRound) const;
|
||||
int checkPlayedWord(const wstring &iCoord,
|
||||
const wstring &iWord, Round &oRound) const;
|
||||
|
||||
/**
|
||||
* load games from File using the first format.
|
||||
|
@ -343,14 +345,6 @@ protected:
|
|||
*/
|
||||
void gameSaveFormat_15(ostream &out) const;
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Play a round on the board.
|
||||
* This should only be called by helperPlayMove().
|
||||
*/
|
||||
void helperPlayRound(unsigned int iPlayerId, const Round &iRound);
|
||||
|
||||
};
|
||||
|
||||
#endif /* _GAME_H_ */
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "duplicate.h"
|
||||
#include "encoding.h"
|
||||
#include "game_exception.h"
|
||||
#include "game_move_cmd.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -389,7 +390,10 @@ Game* Game::gameLoadFormat_15(FILE *fin, const Dictionary& iDic)
|
|||
// pGame->m_players[player]->endTurn(round,num - 1);
|
||||
|
||||
// Play the round
|
||||
pGame->helperPlayRound(pGame->m_currPlayer, round);
|
||||
GameMoveCmd cmd(*pGame, Move(round),
|
||||
pGame->getCurrentPlayer().getLastRack(),
|
||||
pGame->m_currPlayer);
|
||||
cmd.execute();
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
|
|
182
game/game_move_cmd.cpp
Normal file
182
game/game_move_cmd.cpp
Normal file
|
@ -0,0 +1,182 @@
|
|||
/*******************************************************************
|
||||
* 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 "game_move_cmd.h"
|
||||
#include "player.h"
|
||||
#include "game.h"
|
||||
#include "rack.h"
|
||||
|
||||
|
||||
GameMoveCmd::GameMoveCmd(Game &ioGame, const Move &iMove,
|
||||
const PlayedRack &iMoveRack, unsigned int iPlayerId)
|
||||
: m_game(ioGame), m_move(iMove), m_moveRack(iMoveRack),
|
||||
m_playerId(iPlayerId)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void GameMoveCmd::doExecute()
|
||||
{
|
||||
// Get the original rack from the player history
|
||||
const Rack &newRack = Move::ComputeRackForMove(m_moveRack, m_move);
|
||||
|
||||
// History of the game
|
||||
History &history = m_game.accessHistory();
|
||||
history.setCurrentRack(m_moveRack);
|
||||
history.playMove(m_playerId, m_move, newRack);
|
||||
|
||||
// Points
|
||||
m_game.addPoints(m_move.getScore());
|
||||
|
||||
// For moves corresponding to a valid round, we have much more
|
||||
// work to do...
|
||||
if (m_move.getType() == Move::VALID_ROUND)
|
||||
{
|
||||
playRound();
|
||||
}
|
||||
#ifdef REAL_BAG_MODE
|
||||
else if (m_move.getType() == Move::CHANGE_LETTERS)
|
||||
{
|
||||
// Put the changed letters back into the bag
|
||||
BOOST_FOREACH(wchar_t ch, m_move.getChangedLetters())
|
||||
{
|
||||
m_bag.replaceTile(Tile(ch));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void GameMoveCmd::doUndo()
|
||||
{
|
||||
// Undo playing the round on the board
|
||||
if (m_move.getType() == Move::VALID_ROUND)
|
||||
{
|
||||
unplayRound();
|
||||
}
|
||||
|
||||
// Points
|
||||
m_game.addPoints(- m_move.getScore());
|
||||
|
||||
// History
|
||||
m_game.accessHistory().removeLastTurn();
|
||||
}
|
||||
|
||||
|
||||
void GameMoveCmd::playRound()
|
||||
{
|
||||
// Copy the round, because we may need to modify it (case of
|
||||
// the joker games). It will also be convenient for the unplayRound()
|
||||
// method.
|
||||
m_round = m_move.getRound();
|
||||
|
||||
#ifdef REAL_BAG_MODE
|
||||
#else
|
||||
// Update the bag
|
||||
// We remove tiles from the bag only when they are played
|
||||
// on the board. When going back in the game, we must only
|
||||
// replace played tiles.
|
||||
// We test a rack when it is set but tiles are left in the bag.
|
||||
Bag & bag = m_game.accessBag();
|
||||
for (unsigned int i = 0; i < m_round.getWordLen(); i++)
|
||||
{
|
||||
if (m_round.isPlayedFromRack(i))
|
||||
{
|
||||
if (m_round.isJoker(i))
|
||||
{
|
||||
bag.takeTile(Tile::Joker());
|
||||
}
|
||||
else
|
||||
{
|
||||
bag.takeTile(m_round.getTile(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_game.getVariant() == Game::kJOKER)
|
||||
{
|
||||
for (unsigned int i = 0; i < m_round.getWordLen(); i++)
|
||||
{
|
||||
if (m_round.isPlayedFromRack(i) && m_round.isJoker(i))
|
||||
{
|
||||
// Get the real bag
|
||||
#ifdef REAL_BAG_MODE
|
||||
Bag &bag = m_game.accessBag();
|
||||
#else
|
||||
Bag bag(m_game.getDic());
|
||||
m_game.realBag(bag);
|
||||
#endif
|
||||
|
||||
// Is the represented letter still available in the bag?
|
||||
// XXX: this way to get the represented letter sucks...
|
||||
Tile t(towupper(m_round.getTile(i).toChar()));
|
||||
if (bag.in(t))
|
||||
{
|
||||
bag.replaceTile(Tile::Joker());
|
||||
bag.takeTile(t);
|
||||
m_round.setTile(i, t);
|
||||
// FIXME: This shouldn't be necessary, this is only
|
||||
// needed because of the stupid way of handling jokers in
|
||||
// rounds
|
||||
m_round.setJoker(i, false);
|
||||
}
|
||||
|
||||
// In a joker game we should have only 1 joker in the rack
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the board
|
||||
m_game.accessBoard().addRound(m_game.getDic(), m_round);
|
||||
}
|
||||
|
||||
|
||||
void GameMoveCmd::unplayRound()
|
||||
{
|
||||
// Update the board
|
||||
m_game.accessBoard().removeRound(m_game.getDic(), m_round);
|
||||
|
||||
#ifdef REAL_BAG_MODE
|
||||
#else
|
||||
// Update the bag
|
||||
// We remove tiles from the bag only when they are played
|
||||
// on the board. When going back in the game, we must only
|
||||
// replace played tiles.
|
||||
// We test a rack when it is set but tiles are left in the bag.
|
||||
Bag & bag = m_game.accessBag();
|
||||
for (unsigned int i = 0; i < m_round.getWordLen(); i++)
|
||||
{
|
||||
if (m_round.isPlayedFromRack(i))
|
||||
{
|
||||
if (m_round.isJoker(i))
|
||||
{
|
||||
bag.replaceTile(Tile::Joker());
|
||||
}
|
||||
else
|
||||
{
|
||||
bag.replaceTile(m_round.getTile(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
66
game/game_move_cmd.h
Normal file
66
game/game_move_cmd.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*******************************************************************
|
||||
* 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 _GAME_MOVE_CMD_H
|
||||
#define _GAME_MOVE_CMD_H
|
||||
|
||||
#include "command.h"
|
||||
#include "move.h"
|
||||
#include "pldrack.h"
|
||||
#include "round.h"
|
||||
|
||||
class Game;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements the Command design pattern.
|
||||
* It encapsulates the logic to update the game state when a move is
|
||||
* played:
|
||||
* - game score update
|
||||
* - game history update
|
||||
* - bag update
|
||||
* - board update
|
||||
*
|
||||
* The Move validation must have been done before calling execute().
|
||||
*/
|
||||
class GameMoveCmd: public Command
|
||||
{
|
||||
public:
|
||||
GameMoveCmd(Game &ioGame, const Move &iMove,
|
||||
const PlayedRack &iMoveRack,
|
||||
unsigned int iPlayerId);
|
||||
|
||||
protected:
|
||||
virtual void doExecute();
|
||||
virtual void doUndo();
|
||||
|
||||
private:
|
||||
Game &m_game;
|
||||
Move m_move;
|
||||
Round m_round;
|
||||
PlayedRack m_moveRack;
|
||||
unsigned int m_playerId;
|
||||
|
||||
void playRound();
|
||||
void unplayRound();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -93,20 +93,20 @@ bool History::beforeFirstRound() const
|
|||
}
|
||||
|
||||
|
||||
void History::playMove(unsigned int iPlayer, unsigned int iTurn,
|
||||
const Move &iMove, const Rack &iNewRack)
|
||||
void History::playMove(unsigned int iPlayer,
|
||||
const Move &iMove,
|
||||
const Rack &iNewRack)
|
||||
{
|
||||
Turn * current_turn = m_history.back();
|
||||
|
||||
// Set the number and the round
|
||||
current_turn->setNum(iTurn);
|
||||
current_turn->setPlayer(iPlayer);
|
||||
current_turn->setMove(iMove);
|
||||
|
||||
// Create a new turn
|
||||
Turn * next_turn = new Turn();
|
||||
PlayedRack pldrack;
|
||||
pldrack.setOld(iNewRack);
|
||||
Turn * next_turn = new Turn();
|
||||
next_turn->setPlayedRack(pldrack);
|
||||
m_history.push_back(next_turn);
|
||||
}
|
||||
|
@ -126,7 +126,6 @@ void History::removeLastTurn()
|
|||
|
||||
// Now we have the previous played round in back()
|
||||
Turn *t = m_history.back();
|
||||
t->setNum(0);
|
||||
t->setPlayer(0);
|
||||
//t->setRound(Round());
|
||||
#ifdef BACK_REMOVE_RACK_NEW_PART
|
||||
|
|
|
@ -84,8 +84,8 @@ class History
|
|||
* A new turn is created with the unplayed letters in the rack
|
||||
* 03 sept 2000: We have to sort the tiles according to the new rules
|
||||
*/
|
||||
void playMove(unsigned int player, unsigned int turn,
|
||||
const Move &iMove, const Rack &iNewRack);
|
||||
void playMove(unsigned int player, const Move &iMove,
|
||||
const Rack &iNewRack);
|
||||
|
||||
/// Remove last turn
|
||||
void removeLastTurn();
|
||||
|
|
|
@ -18,11 +18,15 @@
|
|||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*****************************************************************************/
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <wctype.h>
|
||||
#include <sstream>
|
||||
|
||||
#include "move.h"
|
||||
#include "rack.h"
|
||||
#include "pldrack.h"
|
||||
|
||||
|
||||
Move::Move(const Round &iRound)
|
||||
|
@ -100,6 +104,41 @@ const wstring & Move::getChangedLetters() const
|
|||
}
|
||||
|
||||
|
||||
Rack Move::ComputeRackForMove(const PlayedRack &iOldRack, const Move &iMove)
|
||||
{
|
||||
// Start from the given rack
|
||||
Rack newRack;
|
||||
iOldRack.getRack(newRack);
|
||||
|
||||
if (iMove.getType() == Move::VALID_ROUND)
|
||||
{
|
||||
// Remove the played tiles from the rack
|
||||
const Round &round = iMove.getRound();
|
||||
for (unsigned int i = 0; i < round.getWordLen(); i++)
|
||||
{
|
||||
if (round.isPlayedFromRack(i))
|
||||
{
|
||||
if (round.isJoker(i))
|
||||
newRack.remove(Tile::Joker());
|
||||
else
|
||||
newRack.remove(round.getTile(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (iMove.getType() == Move::CHANGE_LETTERS)
|
||||
{
|
||||
// Remove the changed tiles from the rack
|
||||
const wstring & changed = iMove.getChangedLetters();
|
||||
BOOST_FOREACH(wchar_t ch, changed)
|
||||
{
|
||||
newRack.remove(Tile(ch));
|
||||
}
|
||||
}
|
||||
|
||||
return newRack;
|
||||
}
|
||||
|
||||
|
||||
wstring Move::toString() const
|
||||
{
|
||||
wstringstream wss;
|
||||
|
|
10
game/move.h
10
game/move.h
|
@ -25,6 +25,8 @@
|
|||
|
||||
#include "round.h"
|
||||
|
||||
class Rack;
|
||||
class PlayedRack;
|
||||
using std::wstring;
|
||||
|
||||
|
||||
|
@ -107,6 +109,14 @@ class Move
|
|||
*/
|
||||
const wstring & getChangedLetters() const;
|
||||
|
||||
/**
|
||||
* Return the rack obtained from the given one, after playing the
|
||||
* given move.
|
||||
* The move is supposed to be possible for the given rack.
|
||||
*/
|
||||
static Rack ComputeRackForMove(const PlayedRack &iOldRack,
|
||||
const Move &iMove);
|
||||
|
||||
/// To help debugging
|
||||
wstring toString() const;
|
||||
|
||||
|
|
|
@ -63,12 +63,6 @@ const Move & Player::getLastMove() const
|
|||
}
|
||||
|
||||
|
||||
void Player::endTurn(const Move &iMove, unsigned int iTurn, const Rack &iNewRack)
|
||||
{
|
||||
addPoints(iMove.getScore());
|
||||
m_history.playMove(m_id, iTurn, iMove, iNewRack);
|
||||
}
|
||||
|
||||
void Player::removeLastTurn()
|
||||
{
|
||||
// Remove points of the last turn
|
||||
|
@ -78,13 +72,14 @@ void Player::removeLastTurn()
|
|||
|
||||
wstring Player::toString() const
|
||||
{
|
||||
wstring res;
|
||||
wstring res = L"Player ";
|
||||
|
||||
wchar_t buff[6];
|
||||
_swprintf(buff, 5, L"Player %d\n", m_id);
|
||||
res = wstring(buff);
|
||||
_swprintf(buff, 5, L"%d\n", m_id);
|
||||
res += wstring(buff);
|
||||
res += m_history.toString() + L"\n";
|
||||
_swprintf(buff, 5, L"score %d\n", m_score);
|
||||
res += L"score ";
|
||||
_swprintf(buff, 5, L"%d\n", m_score);
|
||||
res += wstring(buff);
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,8 @@ public:
|
|||
/// Set the name of the player
|
||||
void setName(const wstring &iName) { m_name = iName; }
|
||||
|
||||
/// Set the ID
|
||||
/// ID handling
|
||||
unsigned int getId() const { return m_id; }
|
||||
void setId(unsigned int iId) { m_id = iId; }
|
||||
|
||||
/**************************
|
||||
|
@ -67,6 +68,7 @@ public:
|
|||
void setCurrentRack(const PlayedRack &iPld);
|
||||
|
||||
const History& getHistory() const { return m_history; }
|
||||
History & accessHistory() { return m_history; }
|
||||
|
||||
/// Remove last turn
|
||||
void removeLastTurn();
|
||||
|
@ -79,14 +81,6 @@ public:
|
|||
void addPoints(int iPoints) { m_score += iPoints; }
|
||||
int getPoints() const { return m_score; }
|
||||
|
||||
/**
|
||||
* Update the player "history", with the given move.
|
||||
* A new rack is created with the remaining letters.
|
||||
* The score of the player is updated with the one of the move, if it is
|
||||
* meaningful.
|
||||
*/
|
||||
void endTurn(const Move &iMove, unsigned int iTurn, const Rack &iNewRack);
|
||||
|
||||
wstring toString() const;
|
||||
|
||||
private:
|
||||
|
|
55
game/player_move_cmd.cpp
Normal file
55
game/player_move_cmd.cpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*******************************************************************
|
||||
* 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 "player_move_cmd.h"
|
||||
#include "player.h"
|
||||
#include "rack.h"
|
||||
|
||||
|
||||
PlayerMoveCmd::PlayerMoveCmd(Player &ioPlayer, const Move &iMove)
|
||||
: m_player(ioPlayer), m_move(iMove)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void PlayerMoveCmd::doExecute()
|
||||
{
|
||||
// Get what was the rack for the current turn
|
||||
m_originalRack = m_player.getCurrentRack();
|
||||
|
||||
// Compute the new rack
|
||||
const Rack &newRack = Move::ComputeRackForMove(m_originalRack, m_move);
|
||||
|
||||
// Update the score of the player
|
||||
m_player.addPoints(m_move.getScore());
|
||||
// Update the history and rack of the player
|
||||
m_player.accessHistory().playMove(m_player.getId(), m_move, newRack);
|
||||
}
|
||||
|
||||
|
||||
void PlayerMoveCmd::doUndo()
|
||||
{
|
||||
// Remove the last history item
|
||||
m_player.accessHistory().removeLastTurn();
|
||||
// Restore the score
|
||||
m_player.addPoints(- m_move.getScore());
|
||||
// TODO: restore rack?
|
||||
}
|
||||
|
58
game/player_move_cmd.h
Normal file
58
game/player_move_cmd.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*******************************************************************
|
||||
* 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 _PLAYER_MOVE_CMD_H
|
||||
#define _PLAYER_MOVE_CMD_H
|
||||
|
||||
#include "command.h"
|
||||
#include "move.h"
|
||||
#include "pldrack.h"
|
||||
|
||||
class Player;
|
||||
class Rack;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements the Command design pattern.
|
||||
* It encapsulates the logic to update the player state when a player
|
||||
* plays a move:
|
||||
* - score update
|
||||
* - rack update
|
||||
* - player history update
|
||||
*
|
||||
* The Move validation must have been done before calling execute().
|
||||
*/
|
||||
class PlayerMoveCmd: public Command
|
||||
{
|
||||
public:
|
||||
PlayerMoveCmd(Player &ioPlayer, const Move &iMove);
|
||||
|
||||
protected:
|
||||
virtual void doExecute();
|
||||
virtual void doUndo();
|
||||
|
||||
private:
|
||||
Player &m_player;
|
||||
Move m_move;
|
||||
PlayedRack m_originalRack;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
45
game/player_rack_cmd.cpp
Normal file
45
game/player_rack_cmd.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*******************************************************************
|
||||
* 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 "player_rack_cmd.h"
|
||||
#include "player.h"
|
||||
|
||||
|
||||
PlayerRackCmd::PlayerRackCmd(Player &ioPlayer, const PlayedRack &iNewRack)
|
||||
: m_player(ioPlayer), m_newRack(iNewRack)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void PlayerRackCmd::doExecute()
|
||||
{
|
||||
// Get what was the rack for the current turn
|
||||
m_oldRack = m_player.getCurrentRack();
|
||||
// Update the rack of the player
|
||||
m_player.setCurrentRack(m_newRack);
|
||||
}
|
||||
|
||||
|
||||
void PlayerRackCmd::doUndo()
|
||||
{
|
||||
// Restore the rack of the player
|
||||
m_player.setCurrentRack(m_oldRack);
|
||||
}
|
||||
|
50
game/player_rack_cmd.h
Normal file
50
game/player_rack_cmd.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*******************************************************************
|
||||
* 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 _PLAYER_RACK_CMD_H
|
||||
#define _PLAYER_RACK_CMD_H
|
||||
|
||||
#include "command.h"
|
||||
#include "pldrack.h"
|
||||
|
||||
class Player;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements the Command design pattern.
|
||||
* It encapsulates the logic to update the player rack, usually to complete it.
|
||||
*/
|
||||
class PlayerRackCmd: public Command
|
||||
{
|
||||
public:
|
||||
PlayerRackCmd(Player &ioPlayer, const PlayedRack &iNewRack);
|
||||
|
||||
protected:
|
||||
virtual void doExecute();
|
||||
virtual void doUndo();
|
||||
|
||||
private:
|
||||
Player &m_player;
|
||||
PlayedRack m_oldRack;
|
||||
PlayedRack m_newRack;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Copyright (C) 1999-2008 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
|
@ -35,8 +35,12 @@
|
|||
#include "move.h"
|
||||
#include "pldrack.h"
|
||||
#include "player.h"
|
||||
#include "player_move_cmd.h"
|
||||
#include "player_rack_cmd.h"
|
||||
#include "game_move_cmd.h"
|
||||
#include "training.h"
|
||||
#include "encoding.h"
|
||||
#include "turn_cmd.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
|
@ -55,7 +59,8 @@ void Training::setRackRandom(bool iCheck, set_rack_mode mode)
|
|||
m_results.clear();
|
||||
const PlayedRack &newRack =
|
||||
helperSetRackRandom(getCurrentPlayer().getCurrentRack(), iCheck, mode);
|
||||
m_players[m_currPlayer]->setCurrentRack(newRack);
|
||||
Command *pCmd = new PlayerRackCmd(*m_players[m_currPlayer], newRack);
|
||||
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
|
||||
}
|
||||
|
||||
|
||||
|
@ -110,11 +115,7 @@ int Training::play(const wstring &iCoord, const wstring &iWord)
|
|||
}
|
||||
|
||||
Move move(round);
|
||||
// Update the rack and the score of the current player
|
||||
// Player::endTurn() must be called before Game::helperPlayMove()
|
||||
// (called here in endTurn()).
|
||||
// See the big comment in game.cpp, line 96
|
||||
recordPlayerMove(move, m_currPlayer);
|
||||
recordPlayerMove(move, *m_players[m_currPlayer]);
|
||||
|
||||
// Next turn
|
||||
endTurn();
|
||||
|
@ -123,18 +124,14 @@ int Training::play(const wstring &iCoord, const wstring &iWord)
|
|||
}
|
||||
|
||||
|
||||
void Training::recordPlayerMove(const Move &iMove, unsigned int p)
|
||||
void Training::recordPlayerMove(const Move &iMove, Player &ioPlayer)
|
||||
{
|
||||
ASSERT(p < getNPlayers(), "Wrong player number");
|
||||
|
||||
// Get what was the rack for the current turn
|
||||
Rack oldRack;
|
||||
m_players[p]->getCurrentRack().getRack(oldRack);
|
||||
// Compute the new rack
|
||||
const Rack &newRack = helperComputeRackForMove(oldRack, iMove);
|
||||
|
||||
// Record the invalid move of the player
|
||||
m_players[p]->endTurn(iMove, getHistory().getSize(), newRack);
|
||||
// Update the rack and the score of the current player
|
||||
// PlayerMoveCmd::execute() must be called before Game::helperPlayMove()
|
||||
// (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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -154,7 +151,11 @@ void Training::endTurn()
|
|||
|
||||
// Play the word on the board
|
||||
const Move &move = m_players[m_currPlayer]->getLastMove();
|
||||
helperPlayMove(m_currPlayer, move);
|
||||
Command *pCmd = new GameMoveCmd(*this, move,
|
||||
getCurrentPlayer().getLastRack(),
|
||||
m_currPlayer);
|
||||
m_turnCommands[m_currTurn]->addAndExecute(pCmd);
|
||||
newTurn();
|
||||
}
|
||||
|
||||
|
||||
|
@ -174,7 +175,7 @@ int Training::playResult(unsigned int n)
|
|||
|
||||
Move move(m_results.get(n));
|
||||
// Update the rack and the score of the current player
|
||||
recordPlayerMove(move, m_currPlayer);
|
||||
recordPlayerMove(move, *m_players[m_currPlayer]);
|
||||
|
||||
// Next turn
|
||||
endTurn();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Copyright (C) 1999-2008 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
|
@ -28,6 +28,8 @@
|
|||
#include "round.h"
|
||||
#include "results.h"
|
||||
|
||||
class Player;
|
||||
|
||||
using std::string;
|
||||
using std::wstring;
|
||||
|
||||
|
@ -96,7 +98,7 @@ private:
|
|||
Training(const Dictionary &iDic);
|
||||
|
||||
/// Record a player move
|
||||
void recordPlayerMove(const Move &iMove, unsigned int p);
|
||||
void recordPlayerMove(const Move &iMove, Player &ioPlayer);
|
||||
|
||||
void endTurn();
|
||||
|
||||
|
|
|
@ -25,14 +25,14 @@
|
|||
// FIXME: move set to an invalid value. It would be better to get rid of this
|
||||
// constructor completely
|
||||
Turn::Turn()
|
||||
: m_num(0), m_playerId(0), m_move(L"", L"")
|
||||
: m_playerId(0), m_move(L"", L"")
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Turn::Turn(unsigned int iNum, unsigned int iPlayerId,
|
||||
const PlayedRack& iPldRack, const Move& iMove)
|
||||
: m_num(iNum), m_playerId(iPlayerId), m_pldrack(iPldRack), m_move(iMove)
|
||||
Turn::Turn(unsigned int iPlayerId, const PlayedRack& iPldRack,
|
||||
const Move& iMove)
|
||||
: m_playerId(iPlayerId), m_pldrack(iPldRack), m_move(iMove)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@ using std::wstring;
|
|||
/**
|
||||
* A Turn is the information about one 'move' done by a player.
|
||||
* It consists of the player who played, the rack, and the actual move.
|
||||
* A turn also has an id (XXX: currently never read)
|
||||
*
|
||||
* This class has no logic, it is merely there to aggregate corresponding
|
||||
* data.
|
||||
|
@ -41,15 +40,13 @@ class Turn
|
|||
{
|
||||
public:
|
||||
Turn();
|
||||
Turn(unsigned int iNum, unsigned int iPlayerId,
|
||||
Turn(unsigned int iPlayerId,
|
||||
const PlayedRack& iPldRack, const Move& iMove);
|
||||
|
||||
void setNum(unsigned int iNum) { m_num = iNum; }
|
||||
void setPlayer(unsigned int iPlayerId) { m_playerId = iPlayerId; }
|
||||
void setPlayedRack(const PlayedRack& iPldRack) { m_pldrack = iPldRack; }
|
||||
void setMove(const Move& iMove) { m_move = iMove; }
|
||||
|
||||
unsigned int getNum() const { return m_num; }
|
||||
unsigned int getPlayer() const { return m_playerId; }
|
||||
const PlayedRack& getPlayedRack() const { return m_pldrack; }
|
||||
const Move& getMove() const { return m_move; }
|
||||
|
@ -57,7 +54,6 @@ public:
|
|||
wstring toString(bool iShowExtraSigns = false) const;
|
||||
|
||||
private:
|
||||
unsigned int m_num;
|
||||
unsigned int m_playerId;
|
||||
PlayedRack m_pldrack;
|
||||
Move m_move;
|
||||
|
|
62
game/turn_cmd.cpp
Normal file
62
game/turn_cmd.cpp
Normal 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
|
||||
*****************************************************************************/
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "turn_cmd.h"
|
||||
#include "player.h"
|
||||
|
||||
|
||||
TurnCmd::~TurnCmd()
|
||||
{
|
||||
BOOST_FOREACH(Command *cmd, m_commands)
|
||||
{
|
||||
delete cmd;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TurnCmd::addAndExecute(Command *iCmd)
|
||||
{
|
||||
m_commands.push_back(iCmd);
|
||||
iCmd->execute();
|
||||
}
|
||||
|
||||
|
||||
void TurnCmd::doExecute()
|
||||
{
|
||||
BOOST_FOREACH(Command *cmd, m_commands)
|
||||
{
|
||||
if (!cmd->isExecuted())
|
||||
cmd->execute();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TurnCmd::doUndo()
|
||||
{
|
||||
// Undo commands in the reverse order of execution
|
||||
vector<Command*>::reverse_iterator it;
|
||||
for (it = m_commands.rbegin(); it != m_commands.rend(); ++it)
|
||||
{
|
||||
(*it)->undo();
|
||||
}
|
||||
}
|
||||
|
55
game/turn_cmd.h
Normal file
55
game/turn_cmd.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*******************************************************************
|
||||
* 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 _TURN_CMD_H
|
||||
#define _TURN_CMD_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "command.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements both the Command and Composite design patterns.
|
||||
* It encapsulates commands, while still behaving like one.
|
||||
*/
|
||||
class TurnCmd: public Command
|
||||
{
|
||||
public:
|
||||
virtual ~TurnCmd();
|
||||
|
||||
/**
|
||||
* Add the given command and execute it.
|
||||
* The TurnCmd object takes ownership of the given Command.
|
||||
*/
|
||||
void addAndExecute(Command *iCmd);
|
||||
|
||||
protected:
|
||||
virtual void doExecute();
|
||||
virtual void doUndo();
|
||||
|
||||
private:
|
||||
vector<Command *> m_commands;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -233,6 +233,7 @@ void help_training()
|
|||
printf(" n [] : jouer le résultat numéro []\n");
|
||||
printf(" r : rechercher les meilleurs résultats\n");
|
||||
printf(" s [] : sauver la partie en cours dans le fichier []\n");
|
||||
printf(" h [p|n|f|l] : naviguer dans l'historique (prev, next, first, last\n");
|
||||
printf(" q : quitter le mode entraînement\n");
|
||||
}
|
||||
|
||||
|
@ -257,6 +258,7 @@ void help_freegame()
|
|||
printf(" j [] {} : jouer le mot [] aux coordonnées {}\n");
|
||||
printf(" p [] : passer son tour en changeant les lettres []\n");
|
||||
printf(" s [] : sauver la partie en cours dans le fichier []\n");
|
||||
printf(" h [p|n|f|l] : naviguer dans l'historique (prev, next, first, last\n");
|
||||
printf(" q : quitter le mode partie libre\n");
|
||||
}
|
||||
|
||||
|
@ -280,6 +282,7 @@ void help_duplicate()
|
|||
printf(" j [] {} : jouer le mot [] aux coordonnées {}\n");
|
||||
printf(" n [] : passer au joueur n°[]\n");
|
||||
printf(" s [] : sauver la partie en cours dans le fichier []\n");
|
||||
printf(" h [p|n|f|l] : naviguer dans l'historique (prev, next, first, last\n");
|
||||
printf(" q : quitter le mode duplicate\n");
|
||||
}
|
||||
|
||||
|
@ -566,6 +569,27 @@ void loop_training(Training &iGame)
|
|||
fout.close();
|
||||
}
|
||||
break;
|
||||
case L'h':
|
||||
token = next_token_alpha(NULL, delim, &state);
|
||||
if (token != NULL)
|
||||
{
|
||||
switch (token[0])
|
||||
{
|
||||
case L'p':
|
||||
iGame.prevTurn();
|
||||
break;
|
||||
case L'n':
|
||||
iGame.nextTurn();
|
||||
break;
|
||||
case L'f':
|
||||
iGame.firstTurn();
|
||||
break;
|
||||
case L'l':
|
||||
iGame.lastTurn();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case L'q':
|
||||
quit = 1;
|
||||
break;
|
||||
|
@ -674,6 +698,27 @@ void loop_freegame(FreeGame &iGame)
|
|||
fout.close();
|
||||
}
|
||||
break;
|
||||
case L'h':
|
||||
token = next_token_alpha(NULL, delim, &state);
|
||||
if (token != NULL)
|
||||
{
|
||||
switch (token[0])
|
||||
{
|
||||
case L'p':
|
||||
iGame.prevTurn();
|
||||
break;
|
||||
case L'n':
|
||||
iGame.nextTurn();
|
||||
break;
|
||||
case L'f':
|
||||
iGame.firstTurn();
|
||||
break;
|
||||
case L'l':
|
||||
iGame.lastTurn();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case L'q':
|
||||
quit = 1;
|
||||
break;
|
||||
|
@ -790,6 +835,27 @@ void loop_duplicate(Duplicate &iGame)
|
|||
fout.close();
|
||||
}
|
||||
break;
|
||||
case L'h':
|
||||
token = next_token_alpha(NULL, delim, &state);
|
||||
if (token != NULL)
|
||||
{
|
||||
switch (token[0])
|
||||
{
|
||||
case L'p':
|
||||
iGame.prevTurn();
|
||||
break;
|
||||
case L'n':
|
||||
iGame.nextTurn();
|
||||
break;
|
||||
case L'f':
|
||||
iGame.firstTurn();
|
||||
break;
|
||||
case L'l':
|
||||
iGame.lastTurn();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case L'q':
|
||||
quit = 1;
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue