From 25a36ec9cd5be4f3295922bc4001db1b6d9554dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Teuli=C3=A8re?= Date: Sun, 11 Mar 2012 00:24:21 +0100 Subject: [PATCH] Arbitration mode: allow setting the rack randomly --- game/arbitration.cpp | 53 +++++++++++++++++++++++++++++++++++++++ game/arbitration.h | 17 +++++++++++++ game/duplicate.cpp | 33 ++++++++++++++---------- game/duplicate.h | 6 ++++- game/navigation.cpp | 6 +++++ game/navigation.h | 5 ++++ game/public_game.cpp | 11 ++++++++ game/public_game.h | 14 +++++++++++ game/turn_cmd.cpp | 27 ++++++++++++++++++++ game/turn_cmd.h | 3 +++ qt/arbitration_widget.cpp | 42 +++++++++++++++++++++++++++++++ qt/arbitration_widget.h | 1 + 12 files changed, 204 insertions(+), 14 deletions(-) diff --git a/game/arbitration.cpp b/game/arbitration.cpp index 9cd4f98..7effa41 100644 --- a/game/arbitration.cpp +++ b/game/arbitration.cpp @@ -18,9 +18,14 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *****************************************************************************/ +#include // For transform +#include // For towupper + #include "arbitration.h" #include "rack.h" #include "player.h" +#include "turn_cmd.h" +#include "game_rack_cmd.h" #include "settings.h" #include "encoding.h" #include "debug.h" @@ -35,6 +40,38 @@ Arbitration::Arbitration(const GameParams &iParams) } +void Arbitration::setRackRandom() +{ + undoCurrentRack(); + + const PlayedRack &newRack = + helperSetRackRandom(getHistory().getCurrentRack(), true, RACK_NEW); + setGameAndPlayersRack(newRack); + + // Clear the results if everything went well + m_results.clear(); +} + + +void Arbitration::setRackManual(const wstring &iLetters) +{ + undoCurrentRack(); + + // Letters can be lowercase or uppercase as they are + // coming from user input. We do not consider a lowercase + // letter to be a joker which has been assigned to a letter. + // As a result, we simply make all the letters uppercase + wstring upperLetters = iLetters; + std::transform(upperLetters.begin(), upperLetters.end(), + upperLetters.begin(), towupper); + const PlayedRack &newRack = helperSetRackManual(true, upperLetters); + setGameAndPlayersRack(newRack); + + // Clear the results if everything went well + m_results.clear(); +} + + void Arbitration::search() { // Search for the current player @@ -83,3 +120,19 @@ void Arbitration::finalizeTurn() // FIXME arbitration end } + +void Arbitration::undoCurrentRack() +{ + // The interface is supposed to make sure we are never in this case + ASSERT(getNavigation().isLastTurn(), + "Cannot change rack for an old turn"); + + // TODO + // Find the PlayerMoveCmd we want to undo + const GameRackCmd *cmd = + getNavigation().getCurrentTurn().findMatchingCmd(); + ASSERT(cmd != 0, "No matching GameRackCmd found"); + + accessNavigation().dropFrom(*cmd); +} + diff --git a/game/arbitration.h b/game/arbitration.h index 28eeeb9..948a023 100644 --- a/game/arbitration.h +++ b/game/arbitration.h @@ -35,6 +35,20 @@ class Arbitration: public Duplicate DEFINE_LOGGER(); friend class GameFactory; public: + /** + * Complete (or reset) the rack randomly. + * @exception EndGameException if it is impossible to complete the rack + * for some reason... + */ + void setRackRandom(); + + /** + * Set the rack with the given letters + * @exception EndGameException if the game is over + * @exception GameException if any other error occurs + */ + void setRackManual(const wstring &iLetters); + void search(); const Results& getResults() const { return m_results; } @@ -47,6 +61,9 @@ private: // Private constructor to force using the GameFactory class Arbitration(const GameParams &iParams); + /// Undo the current rack, and subsequent commands + void undoCurrentRack(); + /// Search results, with all the possible rounds up to a predefined limit LimitResults m_results; diff --git a/game/duplicate.cpp b/game/duplicate.cpp index 32eb8b5..d99993c 100644 --- a/game/duplicate.cpp +++ b/game/duplicate.cpp @@ -134,19 +134,7 @@ void Duplicate::start() const PlayedRack &newRack = helperSetRackRandom(getHistory().getCurrentRack(), true, RACK_NEW); - // Set the game rack - Command *pCmd = new GameRackCmd(*this, newRack); - accessNavigation().addAndExecute(pCmd); - LOG_INFO("Setting players rack to '" + lfw(newRack.toString()) + "'"); - // All the players have the same rack - BOOST_FOREACH(Player *player, m_players) - { - Command *pCmd = new PlayerRackCmd(*player, newRack); - accessNavigation().addAndExecute(pCmd); - // Nobody has played yet in this round - Command *pCmd2 = new MarkPlayedCmd(*this, player->getId(), false); - accessNavigation().addAndExecute(pCmd2); - } + setGameAndPlayersRack(newRack); } catch (EndGameException &e) { @@ -420,3 +408,22 @@ void Duplicate::setMasterMove(const Move &iMove) accessNavigation().addAndExecute(pCmd); } + +void Duplicate::setGameAndPlayersRack(const PlayedRack &iRack) +{ + // Set the game rack + Command *pCmd = new GameRackCmd(*this, iRack); + accessNavigation().addAndExecute(pCmd); + LOG_INFO("Setting players rack to '" + lfw(iRack.toString()) + "'"); + // All the players have the same rack + BOOST_FOREACH(Player *player, m_players) + { + Command *pCmd = new PlayerRackCmd(*player, iRack); + accessNavigation().addAndExecute(pCmd); + // Nobody has played yet in this round + Command *pCmd2 = new MarkPlayedCmd(*this, player->getId(), false); + accessNavigation().addAndExecute(pCmd2); + } +} + + diff --git a/game/duplicate.h b/game/duplicate.h index a10c684..b185a82 100644 --- a/game/duplicate.h +++ b/game/duplicate.h @@ -23,10 +23,11 @@ #include "game.h" #include "move.h" -#include "command.h" #include "logging.h" class Player; +class PlayedRack; +class Move; using std::string; using std::wstring; @@ -114,6 +115,9 @@ protected: /// Cancel the last move of a player (in the current turn) void undoPlayerMove(Player &ioPlayer); + /// Helper function to set the game rack and the players rack at the same time + void setGameAndPlayersRack(const PlayedRack &iRack); + /** * This function does not terminate the turn itself, but performs some * checks to know whether or not it should be terminated (with a call to diff --git a/game/navigation.cpp b/game/navigation.cpp index 69919bf..5eaa0ab 100644 --- a/game/navigation.cpp +++ b/game/navigation.cpp @@ -197,6 +197,12 @@ void Navigation::clearFuture() } +void Navigation::dropFrom(const Command &iCmd) +{ + m_turnCommands.back()->dropFrom(iCmd); +} + + const vector & Navigation::getTurns() const { return m_turnCommands; diff --git a/game/navigation.h b/game/navigation.h index ca044d0..a35870f 100644 --- a/game/navigation.h +++ b/game/navigation.h @@ -68,6 +68,11 @@ class Navigation */ void clearFuture(); + /** + * Remove the commands of the last turn, starting from the given one. + */ + void dropFrom(const Command &iCmd); + const vector & getTurns() const; const TurnCmd & getCurrentTurn() const; diff --git a/game/public_game.cpp b/game/public_game.cpp index 97b7fa9..016f85e 100644 --- a/game/public_game.cpp +++ b/game/public_game.cpp @@ -238,6 +238,17 @@ int PublicGame::freeGamePass(const wstring &iToChange) /***************************/ +void PublicGame::arbitrationSetRackRandom() +{ + getTypedGame(m_game).setRackRandom(); +} + + +void PublicGame::arbitrationSetRackManual(const wstring &iLetters) +{ + getTypedGame(m_game).setRackManual(iLetters); +} + void PublicGame::arbitrationSearch() { return getTypedGame(m_game).search(); diff --git a/game/public_game.h b/game/public_game.h index c214aa2..e3d59c7 100644 --- a/game/public_game.h +++ b/game/public_game.h @@ -242,6 +242,20 @@ public: * the Arbitration mode ***************/ + /** + * Complete the rack randomly. + * @exception EndGameException if it is impossible to complete the rack + * for some reason... + */ + void arbitrationSetRackRandom(); + + /** + * Set the rack manually + * @exception EndGameException if the game is over + * @exception GameException if any other error occurs + */ + void arbitrationSetRackManual(const wstring &iLetters); + void arbitrationSearch(); const Results& arbitrationGetResults() const; diff --git a/game/turn_cmd.cpp b/game/turn_cmd.cpp index d3a1dec..c37630d 100644 --- a/game/turn_cmd.cpp +++ b/game/turn_cmd.cpp @@ -113,6 +113,33 @@ void TurnCmd::dropNonExecutedCommands() } +void TurnCmd::dropFrom(const Command &iCmd) +{ + // Find the command index + unsigned idx = m_commands.size(); + for (unsigned i = 0; i < m_commands.size(); ++i) + { + if (m_commands[i] == &iCmd) + { + idx = i; + break; + } + } + ASSERT(idx != m_commands.size(), "Cannot find command to drop"); + LOG_DEBUG("Deleting last turn commands, starting from " << idx); + + while (m_commands.size() > idx) + { + if (m_commands.back()->isExecuted()) + m_commands.back()->undo(); + delete m_commands.back(); + m_commands.pop_back(); + } + if (m_firstNotExecuted > m_commands.size()) + m_firstNotExecuted = m_commands.size(); +} + + bool TurnCmd::isFullyExecuted() const { return m_firstNotExecuted == m_commands.size(); diff --git a/game/turn_cmd.h b/game/turn_cmd.h index 9578b5b..1c889c4 100644 --- a/game/turn_cmd.h +++ b/game/turn_cmd.h @@ -109,6 +109,9 @@ class TurnCmd /// Drop the non-executed commands. Use it with care... void dropNonExecutedCommands(); + /// Drop (and undo if needed) all the commands, starting with the given one. Use it with care... + void dropFrom(const Command &iCmd); + /** * Find the command matching the given predicate, or 0 if not found. * The commands are iterated from the last one to the first one, diff --git a/qt/arbitration_widget.cpp b/qt/arbitration_widget.cpp index 36563ec..e46c906 100644 --- a/qt/arbitration_widget.cpp +++ b/qt/arbitration_widget.cpp @@ -124,6 +124,10 @@ ArbitrationWidget::ArbitrationWidget(QWidget *parent, QObject::connect(lineEditRack, SIGNAL(textChanged(const QString&)), this, SLOT(clearResults())); + // Set a random rack + QObject::connect(buttonRandom, SIGNAL(clicked()), + this, SLOT(setRackRandom())); + // Display a preview of the selected word on the board QObject::connect(treeViewResults->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), @@ -214,6 +218,11 @@ void ArbitrationWidget::refresh() QString qrack = qfw(pldRack.toString(PlayedRack::RACK_SIMPLE)); if (qrack != lineEditRack->text()) lineEditRack->setText(qrack); + + if (m_game->isFinished()) + { + setEnabled(false); + } } @@ -379,6 +388,39 @@ int ArbitrationWidget::addSingleMove(const Move &iMove, int moveType, } +void ArbitrationWidget::setRackRandom() +{ + ASSERT(m_game->isLastTurn(), "The Random button should have been disabled"); + + // Warn if some players have already played + bool someoneHasPlayed = false; + for (unsigned int i = 0; i < m_game->getNbPlayers(); ++i) + { + if (m_game->hasPlayed(i)) + someoneHasPlayed = true; + } + if (someoneHasPlayed) + { + QString msg = _q("Some player(s) already have an assigned move. " + "These moves will be lost if you change the rack."); + QString question = _q("Do you really want to change the rack?"); + if (!QtCommon::requestConfirmation(msg, question)) + return; + } + + m_game->removeTestRound(); + try + { + m_game->arbitrationSetRackRandom(); + emit gameUpdated(); + } + catch (const std::exception &e) + { + emit notifyProblem(_q(e.what())); + } +} + + void ArbitrationWidget::resultsFilterChanged(const QString &iFilter) { treeViewResults->clearSelection(); diff --git a/qt/arbitration_widget.h b/qt/arbitration_widget.h index 85e48b6..9dcb8e3 100644 --- a/qt/arbitration_widget.h +++ b/qt/arbitration_widget.h @@ -57,6 +57,7 @@ public slots: void refresh(); private slots: + void setRackRandom(); void on_buttonSearch_clicked(); void on_checkBoxHideAssigned_toggled(bool); void resultsFilterChanged(const QString &);