Arbitration: do not handle solos automatically.

The arbitrator has to specify them manually. Otherwise, we cannot know
when a turn is complete and thus we cannot determine if solos can/should
be applied.
This commit is contained in:
Olivier Teulière 2012-10-05 12:52:42 +02:00
parent 66cfd66681
commit a800863c1f
12 changed files with 174 additions and 97 deletions

View file

@ -109,6 +109,51 @@ struct MatchingPlayerAndEventType : public unary_function<PlayerEventCmd, bool>
};
void Arbitration::setSolo(unsigned iPlayerId, int iPoints)
{
ASSERT(iPlayerId < getNPlayers(), "Wrong player number");
ASSERT(iPoints >= 0, "Expected a positive value for the solo");
if (iPoints == 0)
{
// Retrieve the default value of the solo
iPoints = Settings::Instance().getInt("arbitration.solo-value");
}
LOG_INFO("Giving a solo of " << iPoints << " to player " << iPlayerId);
// If an existing solo exists, get rid of it
const PlayerEventCmd *cmd = getPlayerEvent(iPlayerId, PlayerEventCmd::SOLO);
if (cmd != 0)
{
accessNavigation().dropCommand(*cmd);
}
Command *pCmd = new PlayerEventCmd(*m_players[iPlayerId],
PlayerEventCmd::SOLO, iPoints);
accessNavigation().insertCommand(pCmd);
}
void Arbitration::removeSolo(unsigned iPlayerId)
{
ASSERT(iPlayerId < getNPlayers(), "Wrong player number");
const PlayerEventCmd *cmd = getPlayerEvent(iPlayerId, PlayerEventCmd::SOLO);
ASSERT(cmd != 0, "No matching PlayerEventCmd found");
accessNavigation().dropCommand(*cmd);
}
int Arbitration::getSolo(unsigned iPlayerId) const
{
ASSERT(iPlayerId < getNPlayers(), "Wrong player number");
const PlayerEventCmd *cmd = getPlayerEvent(iPlayerId, PlayerEventCmd::SOLO);
if (cmd == 0)
return 0;
return cmd->getPoints();
}
void Arbitration::addWarning(unsigned iPlayerId)
{
ASSERT(iPlayerId < getNPlayers(), "Wrong player number");
@ -144,7 +189,7 @@ void Arbitration::addPenalty(unsigned iPlayerId, int iPoints)
if (iPoints == 0)
{
// Retrieve the default value of the penalty
iPoints = Settings::Instance().getInt("arbitration.default-penalty");
iPoints = Settings::Instance().getInt("arbitration.penalty-value");
// By convention, use negative values to indicate a penalty
iPoints = -iPoints;

View file

@ -56,6 +56,10 @@ public:
Move checkWord(const wstring &iWord, const wstring &iCoords) const;
void setSolo(unsigned iPlayerId, int iPoints = 0);
void removeSolo(unsigned iPlayerId);
int getSolo(unsigned iPlayerId) const;
void addWarning(unsigned iPlayerId);
void removeWarning(unsigned iPlayerId);
bool hasWarning(unsigned iPlayerId) const;

View file

@ -288,35 +288,37 @@ void Duplicate::endTurn()
}
}
// Handle solo bonus
// First check whether there are enough players in the game for the
// bonus to apply
unsigned int minNbPlayers = Settings::Instance().getInt(
isArbitrationGame() ? "arbitration.solo-players" : "duplicate.solo-players");
// Find the player with the best score
Player *bestPlayer = findBestPlayer();
if (getNPlayers() >= minNbPlayers && bestPlayer != NULL)
// Handle solo bonus (not in arbitration mode, because we may not have all
// the moves to decide whether a solo can be attributed).
if (!isArbitrationGame())
{
int bestScore = bestPlayer->getLastMove().getScore();
// Find whether other players than imax have the same score
bool otherWithSameScore = false;
BOOST_FOREACH(const Player *player, m_players)
// First check whether there are enough players in the game for the
// bonus to apply
unsigned int minNbPlayers = Settings::Instance().getInt("duplicate.solo-players");
// Find the player with the best score
Player *bestPlayer = findBestPlayer();
if (getNPlayers() >= minNbPlayers && bestPlayer != NULL)
{
if (player != bestPlayer &&
player->getLastMove().getScore() >= bestScore &&
player->getLastMove().isValid())
int bestScore = bestPlayer->getLastMove().getScore();
// Find whether other players than imax have the same score
bool otherWithSameScore = false;
BOOST_FOREACH(const Player *player, m_players)
{
otherWithSameScore = true;
break;
if (player != bestPlayer &&
player->getLastMove().getScore() >= bestScore &&
player->getLastMove().isValid())
{
otherWithSameScore = true;
break;
}
}
if (!otherWithSameScore)
{
// Give the bonus to the player of the best move
int bonus = Settings::Instance().getInt("duplicate.solo-value");
Command *pCmd = new PlayerEventCmd(*bestPlayer, PlayerEventCmd::SOLO, bonus);
accessNavigation().addAndExecute(pCmd);
}
}
if (!otherWithSameScore)
{
// Give the bonus to the player of the best move
int bonus = Settings::Instance().getInt(
isArbitrationGame() ? "arbitration.solo-value" : "duplicate.solo-value");
Command *pCmd = new PlayerEventCmd(*bestPlayer, PlayerEventCmd::SOLO, bonus);
accessNavigation().addAndExecute(pCmd);
}
}

View file

@ -109,7 +109,7 @@ int Player::getPenaltyPoints() const
if ((int)warningsNb > limit)
{
int penaltiesPoints =
Settings::Instance().getInt("arbitration.default-penalty");
Settings::Instance().getInt("arbitration.penalty-value");
total -= penaltiesPoints * (warningsNb - limit);
}
return total;

View file

@ -284,6 +284,22 @@ Move PublicGame::arbitrationCheckWord(const wstring &iWord,
}
void PublicGame::arbitrationToggleSolo(unsigned iPlayerId)
{
Arbitration &game = getTypedGame<Arbitration>(m_game);
if (game.getSolo(iPlayerId) != 0)
game.removeSolo(iPlayerId);
else
game.setSolo(iPlayerId);
}
int PublicGame::arbitrationGetSolo(unsigned iPlayerId) const
{
return getTypedGame<Arbitration>(m_game).getSolo(iPlayerId);
}
void PublicGame::arbitrationToggleWarning(unsigned iPlayerId)
{
Arbitration &game = getTypedGame<Arbitration>(m_game);

View file

@ -268,6 +268,9 @@ public:
Move arbitrationCheckWord(const wstring &iWord,
const wstring &iCoords) const;
void arbitrationToggleSolo(unsigned iPlayerId);
int arbitrationGetSolo(unsigned iPlayerId) const;
void arbitrationToggleWarning(unsigned iPlayerId);
bool arbitrationHasWarning(unsigned iPlayerId) const;

View file

@ -236,18 +236,15 @@ Settings::Settings()
// Number of search results kept in a search
arbitration.add("search-limit", Setting::TypeInt) = 100;
// Number of points granted for a solo (10 is the ODS value)
arbitration.add("solo-value", Setting::TypeInt) = 10;
// Default value of a penalty
arbitration.add("default-penalty", Setting::TypeInt) = 5;
arbitration.add("penalty-value", Setting::TypeInt) = 5;
// Maximum number of warnings before getting penalties
arbitration.add("warnings-limit", Setting::TypeInt) = 3;
// Minimum number of players in an arbitration game needed to apply a "solo" bonus
// (16 is the ODS value)
arbitration.add("solo-players", Setting::TypeInt) = 16;
// Number of points granted for a solo (10 is the ODS value)
arbitration.add("solo-value", Setting::TypeInt) = 10;
// Try to read the values from the configuration file
try
{
@ -263,10 +260,9 @@ Settings::Settings()
copySetting<bool>(tmpConf, *m_conf, "freegame.reject-invalid");
copySetting<bool>(tmpConf, *m_conf, "arbitration.fill-rack");
copySetting<int>(tmpConf, *m_conf, "arbitration.search-limit");
copySetting<int>(tmpConf, *m_conf, "arbitration.default-penalty");
copySetting<int>(tmpConf, *m_conf, "arbitration.warnings-limit");
copySetting<int>(tmpConf, *m_conf, "arbitration.solo-players");
copySetting<int>(tmpConf, *m_conf, "arbitration.solo-value");
copySetting<int>(tmpConf, *m_conf, "arbitration.penalty-value");
copySetting<int>(tmpConf, *m_conf, "arbitration.warnings-limit");
}
catch (...)
{
@ -351,7 +347,9 @@ int Settings::getInt(const string &iName) const
return 10;
else if (iName == "arbitration.search-limit")
return 100;
else if (iName == "arbitration.default-penalty")
else if (iName == "arbitration.solo-value")
return 5;
else if (iName == "arbitration.penalty-value")
return 5;
else if (iName == "arbitration.warnings-limit")
return 3;

View file

@ -78,6 +78,11 @@ ArbitAssignments::ArbitAssignments(QWidget *parent, PublicGame *iGame)
this, SLOT(assignTopMove()));
treeViewPlayers->installEventFilter(filter);
filter = new KeyEventFilter(this, Qt::Key_S);
QObject::connect(filter, SIGNAL(keyPressed(int, int)),
this, SLOT(addRemoveSolo()));
treeViewPlayers->installEventFilter(filter);
filter = new KeyEventFilter(this, Qt::Key_W);
QObject::connect(filter, SIGNAL(keyPressed(int, int)),
this, SLOT(addRemoveWarning()));
@ -271,7 +276,15 @@ void ArbitAssignments::populatePlayersMenu(QMenu &iMenu, const QPoint &iPoint)
this, SLOT(assignTopMove()));
iMenu.addAction(assignTopMoveAction);
// Action to warn (or "unwarn") players
// Action to give or remove a solo to players
QAction *soloAction = new QAction(_q("Give (or remove) a solo"), this);
soloAction->setStatusTip(_q("Give a solo to the selected player, or remove it if (s)he already has one"));
soloAction->setShortcut(Qt::Key_S);
QObject::connect(soloAction, SIGNAL(triggered()),
this, SLOT(addRemoveSolo()));
iMenu.addAction(soloAction);
// Action to give or remove a warning to players
QAction *warningAction = new QAction(_q("Give (or remove) a warning"), this);
warningAction->setStatusTip(_q("Give a warning to the selected player(s), or remove it if they already have one"));
warningAction->setShortcut(Qt::Key_W);
@ -279,9 +292,9 @@ void ArbitAssignments::populatePlayersMenu(QMenu &iMenu, const QPoint &iPoint)
this, SLOT(addRemoveWarning()));
iMenu.addAction(warningAction);
// Action to give a penalty to players
QAction *penaltyAction = new QAction(_q("Give a penalty"), this);
penaltyAction->setStatusTip(_q("Give a penalty to the selected player(s)"));
// Action to give or remove a penalty to players
QAction *penaltyAction = new QAction(_q("Give (or remove) a penalty"), this);
penaltyAction->setStatusTip(_q("Give a penalty to the selected player(s), or remove it if they already have one"));
penaltyAction->setShortcut(Qt::Key_P);
QObject::connect(penaltyAction, SIGNAL(triggered()),
this, SLOT(addRemovePenalty()));
@ -596,6 +609,21 @@ void ArbitAssignments::helperAssignMove(const Move &iMove)
}
void ArbitAssignments::addRemoveSolo()
{
QSet<unsigned int> playersIdSet = getSelectedPlayers();
// Only one player can have a solo
if (playersIdSet.size() != 1)
return;
BOOST_FOREACH(unsigned int id, playersIdSet)
{
m_game->arbitrationToggleSolo(id);
}
emit gameUpdated();
}
void ArbitAssignments::addRemoveWarning()
{
QSet<unsigned int> playersIdSet = getSelectedPlayers();

View file

@ -81,6 +81,7 @@ private slots:
void populatePlayersMenu(QMenu &iMenu, const QPoint &iPoint);
void assignTopMove();
void suppressMove();
void addRemoveSolo();
void addRemoveWarning();
void addRemovePenalty();
void endTurn();

View file

@ -135,9 +135,8 @@ PrefsDialog::PrefsDialog(QWidget *iParent)
bool linkArbit7P1 = qs.value(kARBIT_LINK_7P1, false).toBool();
checkBoxArbitLink7P1->setChecked(linkArbit7P1);
spinBoxArbitSearchLimit->setValue(Settings::Instance().getInt("arbitration.search-limit"));
spinBoxArbitDefPenalty->setValue(Settings::Instance().getInt("arbitration.default-penalty"));
spinBoxArbitPenaltyValue->setValue(Settings::Instance().getInt("arbitration.penalty-value"));
spinBoxArbitWarnLimit->setValue(Settings::Instance().getInt("arbitration.warnings-limit"));
spinBoxArbitSoloPlayers->setValue(Settings::Instance().getInt("arbitration.solo-players"));
spinBoxArbitSoloValue->setValue(Settings::Instance().getInt("arbitration.solo-value"));
// Confirmations
@ -255,12 +254,10 @@ void PrefsDialog::updateSettings()
}
Settings::Instance().setInt("arbitration.search-limit",
spinBoxArbitSearchLimit->value());
Settings::Instance().setInt("arbitration.default-penalty",
spinBoxArbitDefPenalty->value());
Settings::Instance().setInt("arbitration.penalty-value",
spinBoxArbitPenaltyValue->value());
Settings::Instance().setInt("arbitration.warnings-limit",
spinBoxArbitWarnLimit->value());
Settings::Instance().setInt("arbitration.solo-players",
spinBoxArbitSoloPlayers->value());
Settings::Instance().setInt("arbitration.solo-value",
spinBoxArbitSoloValue->value());

View file

@ -108,12 +108,12 @@ void StatsWidget::refresh()
setSectionHidden(col, !isArbit && !canHaveSolos);
setModelHeader(col++, _q("Sub-total"), false);
setSectionHidden(col, !isArbit);
setModelHeader(col++, _q("Warnings"), false);
setSectionHidden(col, !isArbit);
setModelHeader(col++, _q("Penalties"), false);
setSectionHidden(col, !isArbit && !canHaveSolos);
setModelHeader(col++, _q("Solo points"), false);
setSectionHidden(col, !isArbit);
setModelHeader(col++, _q("Penalties"), false);
setSectionHidden(col, !isArbit);
setModelHeader(col++, _q("Warnings"), false);
setModelHeader(col++, _q("Total"), false);
setModelHeader(col++, _q("Diff"), false);
@ -277,12 +277,12 @@ void StatsWidget::setModelTurnData(const QModelIndex &iIndex,
}
// Set the background c constolor
if (iTurn.getPenaltyPoints() != 0)
if (iTurn.getSoloPoints() != 0)
m_model->setData(iIndex, SoloBrush, Qt::BackgroundRole);
else if (iTurn.getPenaltyPoints() != 0)
m_model->setData(iIndex, PenaltyBrush, Qt::BackgroundRole);
else if (iTurn.getWarningsNb() != 0)
m_model->setData(iIndex, WarningBrush, Qt::BackgroundRole);
else if (iTurn.getSoloPoints() != 0)
m_model->setData(iIndex, SoloBrush, Qt::BackgroundRole);
else if (iTurn.getMove().isNull())
m_model->setData(iIndex, PassBrush, Qt::BackgroundRole);
@ -300,12 +300,12 @@ void StatsWidget::setModelEventData(const QModelIndex &iIndex,
int iEvent, const Player &iPlayer)
{
QVariant text;
if (iEvent == 0 && iPlayer.getWarningsNb() != 0)
text = iPlayer.getWarningsNb();
if (iEvent == 0 && iPlayer.getSoloPoints() != 0)
text = iPlayer.getSoloPoints();
else if (iEvent == 1 && iPlayer.getPenaltyPoints() != 0)
text = iPlayer.getPenaltyPoints();
else if (iEvent == 2 && iPlayer.getSoloPoints() != 0)
text = iPlayer.getSoloPoints();
else if (iEvent == 2 && iPlayer.getWarningsNb() != 0)
text = iPlayer.getWarningsNb();
setModelText(iIndex, text);
}
@ -352,6 +352,10 @@ QString StatsWidget::getTooltip(const Turn &iTurn, const Turn &iGameTurn) const
tooltip += "\n" + scoreString.arg(score - gameScore);
}
if (iTurn.getSoloPoints())
{
tooltip += "\n" + _q("Solo: %1").arg(iTurn.getSoloPoints());
}
if (iTurn.getWarningsNb())
{
tooltip += "\n" + _q("Warnings: %1").arg(iTurn.getWarningsNb());
@ -360,10 +364,6 @@ QString StatsWidget::getTooltip(const Turn &iTurn, const Turn &iGameTurn) const
{
tooltip += "\n" + _q("Penalties: %1").arg(iTurn.getPenaltyPoints());
}
if (iTurn.getSoloPoints())
{
tooltip += "\n" + _q("Solo: %1").arg(iTurn.getSoloPoints());
}
return tooltip;
}

View file

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>474</width>
<height>655</height>
<height>622</height>
</rect>
</property>
<property name="windowTitle">
@ -363,7 +363,7 @@
</item>
<item>
<layout class="QGridLayout" name="gridLayout_3">
<item row="2" column="1">
<item row="3" column="1">
<widget class="QSpinBox" name="spinBoxArbitWarnLimit">
<property name="value">
<number>3</number>
@ -380,22 +380,22 @@
</property>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>_(&quot;Default penalty value:&quot;)</string>
<string>_(&quot;Penalty value:&quot;)</string>
</property>
</widget>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>_(&quot;Warnings limit:&quot;)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="spinBoxArbitDefPenalty">
<item row="2" column="1">
<widget class="QSpinBox" name="spinBoxArbitPenaltyValue">
<property name="toolTip">
<string>_(&quot;Default number of points for a penalty&quot;)</string>
</property>
@ -407,13 +407,6 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>_(&quot;Search results limit:&quot;)</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_3">
<property name="orientation">
@ -427,37 +420,27 @@
</property>
</spacer>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="spinBoxArbitSoloValue">
<property name="toolTip">
<string>_(&quot;Value of the solo bonus. Set it to 0 if you don't want solo bonus&quot;)</string>
</property>
<property name="value">
<number>10</number>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>_(&quot;Search results limit:&quot;)</string>
</property>
</widget>
</item>
<item row="4" column="0">
<item row="1" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>_(&quot;Solo value:&quot;)</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>_(&quot;Min. players for a solo:&quot;)</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="spinBoxArbitSoloPlayers">
<item row="1" column="1">
<widget class="QSpinBox" name="spinBoxArbitSoloValue">
<property name="toolTip">
<string>_(&quot;Minimum number of players needed to take into account the solo bonus&quot;)</string>
<string>_(&quot;Value of the solo bonus. Set it to 0 if you don't want solo bonus&quot;)</string>
</property>
<property name="value">
<number>16</number>
<number>10</number>
</property>
</widget>
</item>
@ -636,7 +619,7 @@
<tabstop>checkBoxArbitFillRack</tabstop>
<tabstop>checkBoxArbitLink7P1</tabstop>
<tabstop>spinBoxArbitSearchLimit</tabstop>
<tabstop>spinBoxArbitDefPenalty</tabstop>
<tabstop>spinBoxArbitPenaltyValue</tabstop>
<tabstop>checkBoxConfoStartGame</tabstop>
<tabstop>checkBoxConfoLoadGame</tabstop>
<tabstop>checkBoxConfoLoadDic</tabstop>