Arbitration mode: allow setting the rack randomly

This commit is contained in:
Olivier Teulière 2012-03-11 00:24:21 +01:00
parent 9c82c8f7bc
commit 25a36ec9cd
12 changed files with 204 additions and 14 deletions

View file

@ -18,9 +18,14 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*****************************************************************************/
#include <algorithm> // For transform
#include <cwctype> // 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<GameRackCmd>();
ASSERT(cmd != 0, "No matching GameRackCmd found");
accessNavigation().dropFrom(*cmd);
}

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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

View file

@ -197,6 +197,12 @@ void Navigation::clearFuture()
}
void Navigation::dropFrom(const Command &iCmd)
{
m_turnCommands.back()->dropFrom(iCmd);
}
const vector<TurnCmd *> & Navigation::getTurns() const
{
return m_turnCommands;

View file

@ -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<TurnCmd *> & getTurns() const;
const TurnCmd & getCurrentTurn() const;

View file

@ -238,6 +238,17 @@ int PublicGame::freeGamePass(const wstring &iToChange)
/***************************/
void PublicGame::arbitrationSetRackRandom()
{
getTypedGame<Arbitration>(m_game).setRackRandom();
}
void PublicGame::arbitrationSetRackManual(const wstring &iLetters)
{
getTypedGame<Arbitration>(m_game).setRackManual(iLetters);
}
void PublicGame::arbitrationSearch()
{
return getTypedGame<Arbitration>(m_game).search();

View file

@ -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;

View file

@ -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();

View file

@ -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,

View file

@ -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();

View file

@ -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 &);