mirror of
git://git.savannah.nongnu.org/eliot.git
synced 2025-01-18 10:26:15 +01:00
Arbitration mode: allow setting the rack randomly
This commit is contained in:
parent
9c82c8f7bc
commit
25a36ec9cd
12 changed files with 204 additions and 14 deletions
|
@ -18,9 +18,14 @@
|
||||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include <algorithm> // For transform
|
||||||
|
#include <cwctype> // For towupper
|
||||||
|
|
||||||
#include "arbitration.h"
|
#include "arbitration.h"
|
||||||
#include "rack.h"
|
#include "rack.h"
|
||||||
#include "player.h"
|
#include "player.h"
|
||||||
|
#include "turn_cmd.h"
|
||||||
|
#include "game_rack_cmd.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "encoding.h"
|
#include "encoding.h"
|
||||||
#include "debug.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()
|
void Arbitration::search()
|
||||||
{
|
{
|
||||||
// Search for the current player
|
// Search for the current player
|
||||||
|
@ -83,3 +120,19 @@ void Arbitration::finalizeTurn()
|
||||||
// FIXME arbitration end
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,20 @@ class Arbitration: public Duplicate
|
||||||
DEFINE_LOGGER();
|
DEFINE_LOGGER();
|
||||||
friend class GameFactory;
|
friend class GameFactory;
|
||||||
public:
|
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();
|
void search();
|
||||||
const Results& getResults() const { return m_results; }
|
const Results& getResults() const { return m_results; }
|
||||||
|
|
||||||
|
@ -47,6 +61,9 @@ private:
|
||||||
// Private constructor to force using the GameFactory class
|
// Private constructor to force using the GameFactory class
|
||||||
Arbitration(const GameParams &iParams);
|
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
|
/// Search results, with all the possible rounds up to a predefined limit
|
||||||
LimitResults m_results;
|
LimitResults m_results;
|
||||||
|
|
||||||
|
|
|
@ -134,19 +134,7 @@ void Duplicate::start()
|
||||||
|
|
||||||
const PlayedRack &newRack =
|
const PlayedRack &newRack =
|
||||||
helperSetRackRandom(getHistory().getCurrentRack(), true, RACK_NEW);
|
helperSetRackRandom(getHistory().getCurrentRack(), true, RACK_NEW);
|
||||||
// Set the game rack
|
setGameAndPlayersRack(newRack);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (EndGameException &e)
|
catch (EndGameException &e)
|
||||||
{
|
{
|
||||||
|
@ -420,3 +408,22 @@ void Duplicate::setMasterMove(const Move &iMove)
|
||||||
accessNavigation().addAndExecute(pCmd);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,11 @@
|
||||||
|
|
||||||
#include "game.h"
|
#include "game.h"
|
||||||
#include "move.h"
|
#include "move.h"
|
||||||
#include "command.h"
|
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
|
||||||
class Player;
|
class Player;
|
||||||
|
class PlayedRack;
|
||||||
|
class Move;
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::wstring;
|
using std::wstring;
|
||||||
|
@ -114,6 +115,9 @@ protected:
|
||||||
/// Cancel the last move of a player (in the current turn)
|
/// Cancel the last move of a player (in the current turn)
|
||||||
void undoPlayerMove(Player &ioPlayer);
|
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
|
* 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
|
* checks to know whether or not it should be terminated (with a call to
|
||||||
|
|
|
@ -197,6 +197,12 @@ void Navigation::clearFuture()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Navigation::dropFrom(const Command &iCmd)
|
||||||
|
{
|
||||||
|
m_turnCommands.back()->dropFrom(iCmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const vector<TurnCmd *> & Navigation::getTurns() const
|
const vector<TurnCmd *> & Navigation::getTurns() const
|
||||||
{
|
{
|
||||||
return m_turnCommands;
|
return m_turnCommands;
|
||||||
|
|
|
@ -68,6 +68,11 @@ class Navigation
|
||||||
*/
|
*/
|
||||||
void clearFuture();
|
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 vector<TurnCmd *> & getTurns() const;
|
||||||
const TurnCmd & getCurrentTurn() const;
|
const TurnCmd & getCurrentTurn() const;
|
||||||
|
|
||||||
|
|
|
@ -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()
|
void PublicGame::arbitrationSearch()
|
||||||
{
|
{
|
||||||
return getTypedGame<Arbitration>(m_game).search();
|
return getTypedGame<Arbitration>(m_game).search();
|
||||||
|
|
|
@ -242,6 +242,20 @@ public:
|
||||||
* the Arbitration mode
|
* 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();
|
void arbitrationSearch();
|
||||||
const Results& arbitrationGetResults() const;
|
const Results& arbitrationGetResults() const;
|
||||||
|
|
||||||
|
|
|
@ -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
|
bool TurnCmd::isFullyExecuted() const
|
||||||
{
|
{
|
||||||
return m_firstNotExecuted == m_commands.size();
|
return m_firstNotExecuted == m_commands.size();
|
||||||
|
|
|
@ -109,6 +109,9 @@ class TurnCmd
|
||||||
/// Drop the non-executed commands. Use it with care...
|
/// Drop the non-executed commands. Use it with care...
|
||||||
void dropNonExecutedCommands();
|
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.
|
* Find the command matching the given predicate, or 0 if not found.
|
||||||
* The commands are iterated from the last one to the first one,
|
* The commands are iterated from the last one to the first one,
|
||||||
|
|
|
@ -124,6 +124,10 @@ ArbitrationWidget::ArbitrationWidget(QWidget *parent,
|
||||||
QObject::connect(lineEditRack, SIGNAL(textChanged(const QString&)),
|
QObject::connect(lineEditRack, SIGNAL(textChanged(const QString&)),
|
||||||
this, SLOT(clearResults()));
|
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
|
// Display a preview of the selected word on the board
|
||||||
QObject::connect(treeViewResults->selectionModel(),
|
QObject::connect(treeViewResults->selectionModel(),
|
||||||
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
|
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
|
||||||
|
@ -214,6 +218,11 @@ void ArbitrationWidget::refresh()
|
||||||
QString qrack = qfw(pldRack.toString(PlayedRack::RACK_SIMPLE));
|
QString qrack = qfw(pldRack.toString(PlayedRack::RACK_SIMPLE));
|
||||||
if (qrack != lineEditRack->text())
|
if (qrack != lineEditRack->text())
|
||||||
lineEditRack->setText(qrack);
|
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)
|
void ArbitrationWidget::resultsFilterChanged(const QString &iFilter)
|
||||||
{
|
{
|
||||||
treeViewResults->clearSelection();
|
treeViewResults->clearSelection();
|
||||||
|
|
|
@ -57,6 +57,7 @@ public slots:
|
||||||
void refresh();
|
void refresh();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void setRackRandom();
|
||||||
void on_buttonSearch_clicked();
|
void on_buttonSearch_clicked();
|
||||||
void on_checkBoxHideAssigned_toggled(bool);
|
void on_checkBoxHideAssigned_toggled(bool);
|
||||||
void resultsFilterChanged(const QString &);
|
void resultsFilterChanged(const QString &);
|
||||||
|
|
Loading…
Reference in a new issue