[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Eliot-dev] eliot game/Makefile.am game/duplicate.cpp game/...
From: |
eliot-dev |
Subject: |
[Eliot-dev] eliot game/Makefile.am game/duplicate.cpp game/... |
Date: |
Sun, 23 Nov 2008 08:18:12 +0000 |
CVSROOT: /cvsroot/eliot
Module name: eliot
Changes by: Olivier Teulière <ipkiss> 08/11/23 08:18:12
Modified files:
game : Makefile.am duplicate.cpp duplicate.h
freegame.cpp freegame.h game.cpp game.h
game_io.cpp history.cpp history.h move.cpp
move.h player.cpp player.h training.cpp
training.h turn.cpp turn.h
utils : eliottxt.cpp
Added files:
game : command.cpp command.h game_move_cmd.cpp
game_move_cmd.h player_move_cmd.cpp
player_move_cmd.h player_rack_cmd.cpp
player_rack_cmd.h turn_cmd.cpp turn_cmd.h
Log message:
- 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)
CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/eliot/game/Makefile.am?cvsroot=eliot&r1=1.19&r2=1.20
http://cvs.savannah.gnu.org/viewcvs/eliot/game/duplicate.cpp?cvsroot=eliot&r1=1.21&r2=1.22
http://cvs.savannah.gnu.org/viewcvs/eliot/game/duplicate.h?cvsroot=eliot&r1=1.15&r2=1.16
http://cvs.savannah.gnu.org/viewcvs/eliot/game/freegame.cpp?cvsroot=eliot&r1=1.23&r2=1.24
http://cvs.savannah.gnu.org/viewcvs/eliot/game/freegame.h?cvsroot=eliot&r1=1.13&r2=1.14
http://cvs.savannah.gnu.org/viewcvs/eliot/game/game.cpp?cvsroot=eliot&r1=1.43&r2=1.44
http://cvs.savannah.gnu.org/viewcvs/eliot/game/game.h?cvsroot=eliot&r1=1.38&r2=1.39
http://cvs.savannah.gnu.org/viewcvs/eliot/game/game_io.cpp?cvsroot=eliot&r1=1.11&r2=1.12
http://cvs.savannah.gnu.org/viewcvs/eliot/game/history.cpp?cvsroot=eliot&r1=1.13&r2=1.14
http://cvs.savannah.gnu.org/viewcvs/eliot/game/history.h?cvsroot=eliot&r1=1.13&r2=1.14
http://cvs.savannah.gnu.org/viewcvs/eliot/game/move.cpp?cvsroot=eliot&r1=1.3&r2=1.4
http://cvs.savannah.gnu.org/viewcvs/eliot/game/move.h?cvsroot=eliot&r1=1.2&r2=1.3
http://cvs.savannah.gnu.org/viewcvs/eliot/game/player.cpp?cvsroot=eliot&r1=1.17&r2=1.18
http://cvs.savannah.gnu.org/viewcvs/eliot/game/player.h?cvsroot=eliot&r1=1.23&r2=1.24
http://cvs.savannah.gnu.org/viewcvs/eliot/game/training.cpp?cvsroot=eliot&r1=1.24&r2=1.25
http://cvs.savannah.gnu.org/viewcvs/eliot/game/training.h?cvsroot=eliot&r1=1.18&r2=1.19
http://cvs.savannah.gnu.org/viewcvs/eliot/game/turn.cpp?cvsroot=eliot&r1=1.13&r2=1.14
http://cvs.savannah.gnu.org/viewcvs/eliot/game/turn.h?cvsroot=eliot&r1=1.11&r2=1.12
http://cvs.savannah.gnu.org/viewcvs/eliot/game/command.cpp?cvsroot=eliot&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/eliot/game/command.h?cvsroot=eliot&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/eliot/game/game_move_cmd.cpp?cvsroot=eliot&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/eliot/game/game_move_cmd.h?cvsroot=eliot&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/eliot/game/player_move_cmd.cpp?cvsroot=eliot&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/eliot/game/player_move_cmd.h?cvsroot=eliot&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/eliot/game/player_rack_cmd.cpp?cvsroot=eliot&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/eliot/game/player_rack_cmd.h?cvsroot=eliot&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/eliot/game/turn_cmd.cpp?cvsroot=eliot&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/eliot/game/turn_cmd.h?cvsroot=eliot&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/eliot/utils/eliottxt.cpp?cvsroot=eliot&r1=1.25&r2=1.26
Patches:
Index: game/Makefile.am
===================================================================
RCS file: /cvsroot/eliot/eliot/game/Makefile.am,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -b -r1.19 -r1.20
--- game/Makefile.am 22 Nov 2008 13:09:29 -0000 1.19
+++ game/Makefile.am 23 Nov 2008 08:18:03 -0000 1.20
@@ -23,13 +23,18 @@
libgame_a_SOURCES= \
game_exception.cpp game_exception.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 \
@@ -38,13 +43,13 @@
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 \
+ 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
Index: game/duplicate.cpp
===================================================================
RCS file: /cvsroot/eliot/eliot/game/duplicate.cpp,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -b -r1.21 -r1.22
--- game/duplicate.cpp 22 Nov 2008 14:40:26 -0000 1.21
+++ game/duplicate.cpp 23 Nov 2008 08:18:05 -0000 1.22
@@ -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 @@
// 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 @@
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 @@
ASSERT(false, "AI tried to cheat!");
}
- recordPlayerMove(move, p);
+ recordPlayerMove(move, *player);
}
@@ -102,25 +107,20 @@
{
const PlayedRack &newRack =
helperSetRackRandom(getCurrentPlayer().getCurrentRack(), true,
RACK_NEW);
- m_players[m_currPlayer]->setCurrentRack(newRack);
- }
- catch (EndGameException &e)
- {
- endGame();
- 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);
- }
+ 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)
+ {
+ endGame();
+ return 1;
+ }
// Little hack to handle duplicate games with only AI players.
// This will have no effect when there is at least one human player
@@ -158,20 +158,12 @@
}
-void Duplicate::recordPlayerMove(const Move &iMove, unsigned int p)
+void Duplicate::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);
+ Command *pCmd = new PlayerMoveCmd(ioPlayer, iMove);
+ m_turnCommands[m_currTurn]->addAndExecute(pCmd);
- // 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 @@
}
// 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 @@
{
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();
}
Index: game/duplicate.h
===================================================================
RCS file: /cvsroot/eliot/eliot/game/duplicate.h,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -b -r1.15 -r1.16
--- game/duplicate.h 22 Nov 2008 13:09:30 -0000 1.15
+++ game/duplicate.h 23 Nov 2008 08:18:06 -0000 1.16
@@ -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 @@
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);
Index: game/freegame.cpp
===================================================================
RCS file: /cvsroot/eliot/eliot/game/freegame.cpp,v
retrieving revision 1.23
retrieving revision 1.24
diff -u -b -r1.23 -r1.24
--- game/freegame.cpp 22 Nov 2008 14:40:26 -0000 1.23
+++ game/freegame.cpp 23 Nov 2008 08:18:06 -0000 1.24
@@ -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 @@
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 @@
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 @@
{
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::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 @@
{
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 @@
// 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 @@
}
-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 @@
}
// 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::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();
Index: game/freegame.h
===================================================================
RCS file: /cvsroot/eliot/eliot/game/freegame.h,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -b -r1.13 -r1.14
--- game/freegame.h 22 Nov 2008 13:09:30 -0000 1.13
+++ game/freegame.h 23 Nov 2008 08:18:06 -0000 1.14
@@ -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 @@
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 @@
* 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_ */
Index: game/game.cpp
===================================================================
RCS file: /cvsroot/eliot/eliot/game/game.cpp,v
retrieving revision 1.43
retrieving revision 1.44
diff -u -b -r1.43 -r1.44
--- game/game.cpp 22 Nov 2008 13:09:30 -0000 1.43
+++ game/game.cpp 23 Nov 2008 08:18:06 -0000 1.44
@@ -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 @@
m_points = 0;
m_currPlayer = 0;
m_finished = false;
+ m_currTurn = -1;
+ newTurn();
}
@@ -63,6 +66,10 @@
{
delete p;
}
+ BOOST_FOREACH(Command *c, m_turnCommands)
+ {
+ delete c;
+ }
}
@@ -73,166 +80,6 @@
}
-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 @@
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;
+}
+
Index: game/game.h
===================================================================
RCS file: /cvsroot/eliot/eliot/game/game.h,v
retrieving revision 1.38
retrieving revision 1.39
diff -u -b -r1.38 -r1.39
--- game/game.h 22 Nov 2008 14:40:26 -0000 1.38
+++ game/game.h 23 Nov 2008 08:18:06 -0000 1.39
@@ -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 Round;
class Rack;
class Turn;
+class TurnCmd;
using namespace std;
@@ -97,14 +98,28 @@
/// 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 @@
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 @@
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,19 +314,6 @@
*/
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
@@ -343,14 +345,6 @@
*/
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_ */
Index: game/game_io.cpp
===================================================================
RCS file: /cvsroot/eliot/eliot/game/game_io.cpp,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -b -r1.11 -r1.12
--- game/game_io.cpp 22 Nov 2008 13:09:30 -0000 1.11
+++ game/game_io.cpp 23 Nov 2008 08:18:06 -0000 1.12
@@ -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 @@
// 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();
}
/**************************************/
Index: game/history.cpp
===================================================================
RCS file: /cvsroot/eliot/eliot/game/history.cpp,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -b -r1.13 -r1.14
--- game/history.cpp 22 Nov 2008 13:09:30 -0000 1.13
+++ game/history.cpp 23 Nov 2008 08:18:08 -0000 1.14
@@ -93,20 +93,20 @@
}
-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 @@
// 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
Index: game/history.h
===================================================================
RCS file: /cvsroot/eliot/eliot/game/history.h,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -b -r1.13 -r1.14
--- game/history.h 22 Nov 2008 13:09:31 -0000 1.13
+++ game/history.h 23 Nov 2008 08:18:08 -0000 1.14
@@ -84,8 +84,8 @@
* 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();
Index: game/move.cpp
===================================================================
RCS file: /cvsroot/eliot/eliot/game/move.cpp,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -b -r1.3 -r1.4
--- game/move.cpp 13 Sep 2008 21:32:46 -0000 1.3
+++ game/move.cpp 23 Nov 2008 08:18:08 -0000 1.4
@@ -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 @@
}
+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;
Index: game/move.h
===================================================================
RCS file: /cvsroot/eliot/eliot/game/move.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -b -r1.2 -r1.3
--- game/move.h 8 Jan 2008 13:52:38 -0000 1.2
+++ game/move.h 23 Nov 2008 08:18:08 -0000 1.3
@@ -25,6 +25,8 @@
#include "round.h"
+class Rack;
+class PlayedRack;
using std::wstring;
@@ -107,6 +109,14 @@
*/
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;
Index: game/player.cpp
===================================================================
RCS file: /cvsroot/eliot/eliot/game/player.cpp,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -b -r1.17 -r1.18
--- game/player.cpp 22 Nov 2008 13:09:31 -0000 1.17
+++ game/player.cpp 23 Nov 2008 08:18:09 -0000 1.18
@@ -63,12 +63,6 @@
}
-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 @@
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;
}
Index: game/player.h
===================================================================
RCS file: /cvsroot/eliot/eliot/game/player.h,v
retrieving revision 1.23
retrieving revision 1.24
diff -u -b -r1.23 -r1.24
--- game/player.h 22 Nov 2008 13:09:31 -0000 1.23
+++ game/player.h 23 Nov 2008 08:18:09 -0000 1.24
@@ -51,7 +51,8 @@
/// 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 @@
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 @@
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:
Index: game/training.cpp
===================================================================
RCS file: /cvsroot/eliot/eliot/game/training.cpp,v
retrieving revision 1.24
retrieving revision 1.25
diff -u -b -r1.24 -r1.25
--- game/training.cpp 22 Nov 2008 14:40:26 -0000 1.24
+++ game/training.cpp 23 Nov 2008 08:18:10 -0000 1.25
@@ -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 @@
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 @@
}
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 @@
}
-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 @@
// 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 @@
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();
Index: game/training.h
===================================================================
RCS file: /cvsroot/eliot/eliot/game/training.h,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -b -r1.18 -r1.19
--- game/training.h 22 Nov 2008 13:09:31 -0000 1.18
+++ game/training.h 23 Nov 2008 08:18:10 -0000 1.19
@@ -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 @@
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();
Index: game/turn.cpp
===================================================================
RCS file: /cvsroot/eliot/eliot/game/turn.cpp,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -b -r1.13 -r1.14
--- game/turn.cpp 22 Nov 2008 13:09:31 -0000 1.13
+++ game/turn.cpp 23 Nov 2008 08:18:10 -0000 1.14
@@ -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)
{
}
Index: game/turn.h
===================================================================
RCS file: /cvsroot/eliot/eliot/game/turn.h,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -b -r1.11 -r1.12
--- game/turn.h 22 Nov 2008 13:09:31 -0000 1.11
+++ game/turn.h 23 Nov 2008 08:18:10 -0000 1.12
@@ -32,7 +32,6 @@
/**
* 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 @@
{
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 @@
wstring toString(bool iShowExtraSigns = false) const;
private:
- unsigned int m_num;
unsigned int m_playerId;
PlayedRack m_pldrack;
Move m_move;
Index: utils/eliottxt.cpp
===================================================================
RCS file: /cvsroot/eliot/eliot/utils/eliottxt.cpp,v
retrieving revision 1.25
retrieving revision 1.26
diff -u -b -r1.25 -r1.26
--- utils/eliottxt.cpp 13 Sep 2008 21:32:47 -0000 1.25
+++ utils/eliottxt.cpp 23 Nov 2008 08:18:11 -0000 1.26
@@ -233,6 +233,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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;
Index: game/command.cpp
===================================================================
RCS file: game/command.cpp
diff -N game/command.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ game/command.cpp 23 Nov 2008 08:18:04 -0000 1.1
@@ -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;
+}
+
Index: game/command.h
===================================================================
RCS file: game/command.h
diff -N game/command.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ game/command.h 23 Nov 2008 08:18:05 -0000 1.1
@@ -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
+
Index: game/game_move_cmd.cpp
===================================================================
RCS file: game/game_move_cmd.cpp
diff -N game/game_move_cmd.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ game/game_move_cmd.cpp 23 Nov 2008 08:18:07 -0000 1.1
@@ -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
+}
+
Index: game/game_move_cmd.h
===================================================================
RCS file: game/game_move_cmd.h
diff -N game/game_move_cmd.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ game/game_move_cmd.h 23 Nov 2008 08:18:07 -0000 1.1
@@ -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
+
Index: game/player_move_cmd.cpp
===================================================================
RCS file: game/player_move_cmd.cpp
diff -N game/player_move_cmd.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ game/player_move_cmd.cpp 23 Nov 2008 08:18:09 -0000 1.1
@@ -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?
+}
+
Index: game/player_move_cmd.h
===================================================================
RCS file: game/player_move_cmd.h
diff -N game/player_move_cmd.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ game/player_move_cmd.h 23 Nov 2008 08:18:09 -0000 1.1
@@ -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
+
Index: game/player_rack_cmd.cpp
===================================================================
RCS file: game/player_rack_cmd.cpp
diff -N game/player_rack_cmd.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ game/player_rack_cmd.cpp 23 Nov 2008 08:18:09 -0000 1.1
@@ -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);
+}
+
Index: game/player_rack_cmd.h
===================================================================
RCS file: game/player_rack_cmd.h
diff -N game/player_rack_cmd.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ game/player_rack_cmd.h 23 Nov 2008 08:18:09 -0000 1.1
@@ -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
+
Index: game/turn_cmd.cpp
===================================================================
RCS file: game/turn_cmd.cpp
diff -N game/turn_cmd.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ game/turn_cmd.cpp 23 Nov 2008 08:18:10 -0000 1.1
@@ -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();
+ }
+}
+
Index: game/turn_cmd.h
===================================================================
RCS file: game/turn_cmd.h
diff -N game/turn_cmd.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ game/turn_cmd.h 23 Nov 2008 08:18:11 -0000 1.1
@@ -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
+
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Eliot-dev] eliot game/Makefile.am game/duplicate.cpp game/...,
eliot-dev <=