Arbitration: new option to handle solos automatically.

The option is not yet accessible from the interface.
This commit is contained in:
Olivier Teulière 2012-12-05 23:02:12 +01:00
parent 4d38c99336
commit 19a93988ad
4 changed files with 109 additions and 32 deletions

View file

@ -221,6 +221,15 @@ void Arbitration::assignMove(unsigned int iPlayerId, const Move &iMove)
{ {
ASSERT(iPlayerId < getNPlayers(), "Wrong player number"); ASSERT(iPlayerId < getNPlayers(), "Wrong player number");
recordPlayerMove(*m_players[iPlayerId], iMove); recordPlayerMove(*m_players[iPlayerId], iMove);
// Automatically update the solos if requested
bool useSoloAuto = Settings::Instance().getBool("arbitration.solo-auto");
if (useSoloAuto)
{
unsigned minNbPlayers = Settings::Instance().getInt("arbitration.solo-players");
int soloValue = Settings::Instance().getInt("arbitration.solo-value");
setSoloAuto(minNbPlayers, soloValue);
}
} }

View file

@ -48,6 +48,7 @@
#include "ai_player.h" #include "ai_player.h"
#include "navigation.h" #include "navigation.h"
#include "turn.h" #include "turn.h"
#include "turn_data.h"
#include "settings.h" #include "settings.h"
#include "encoding.h" #include "encoding.h"
#include "debug.h" #include "debug.h"
@ -288,37 +289,21 @@ void Duplicate::endTurn()
} }
} }
// Handle solo bonus (not in arbitration mode, because we may not have all // Handle solo bonus
// the moves to decide whether a solo can be attributed).
if (!isArbitrationGame()) if (!isArbitrationGame())
{ {
// First check whether there are enough players in the game for the unsigned minNbPlayers = Settings::Instance().getInt("duplicate.solo-players");
// bonus to apply int soloValue = Settings::Instance().getInt("duplicate.solo-value");
unsigned int minNbPlayers = Settings::Instance().getInt("duplicate.solo-players"); setSoloAuto(minNbPlayers, soloValue);
// Find the player with the best score }
Player *bestPlayer = findBestPlayer(); else
if (getNPlayers() >= minNbPlayers && bestPlayer != NULL) {
bool useSoloAuto = Settings::Instance().getBool("arbitration.solo-auto");
if (useSoloAuto)
{ {
int bestScore = bestPlayer->getLastMove().getScore(); unsigned minNbPlayers = Settings::Instance().getInt("arbitration.solo-players");
// Find whether other players than imax have the same score int soloValue = Settings::Instance().getInt("arbitration.solo-value");
bool otherWithSameScore = false; setSoloAuto(minNbPlayers, soloValue);
BOOST_FOREACH(const Player *player, m_players)
{
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);
}
} }
} }
@ -435,6 +420,67 @@ void Duplicate::setGameAndPlayersRack(const PlayedRack &iRack)
} }
void Duplicate::setSoloAuto(unsigned int minNbPlayers, int iSoloValue)
{
// Remove all existing solos
BOOST_FOREACH(const Player *player, m_players)
{
const PlayerEventCmd *cmd = getPlayerEvent(player->getId(), PlayerEventCmd::SOLO);
if (cmd != 0)
{
accessNavigation().dropCommand(*cmd);
}
}
// Check whether there are enough players in the game for the
// solo to apply. We count only the "active" players, i.e. the ones
// which have played at least one word during the game, even if they
// have left the game since then, or have arrived after the beginning.
unsigned countActive = 0;
BOOST_FOREACH(const Player *player, m_players)
{
for (unsigned i = 0; i < player->getHistory().getSize(); ++i)
{
if (!player->getHistory().getTurn(i).getMove().isNull())
{
++countActive;
break;
}
}
}
if (countActive < minNbPlayers)
return;
// Find the player with the best score
Player *bestPlayer = findBestPlayer();
if (bestPlayer != NULL)
{
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)
{
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
LOG_INFO("Giving a solo of " << iSoloValue << " to player " << bestPlayer->getId());
Command *pCmd = new PlayerEventCmd(*bestPlayer, PlayerEventCmd::SOLO, iSoloValue);
accessNavigation().insertCommand(pCmd);
}
}
}
/// Predicate to help retrieving commands /// Predicate to help retrieving commands
struct MatchingPlayerAndEventType : public unary_function<PlayerEventCmd, bool> struct MatchingPlayerAndEventType : public unary_function<PlayerEventCmd, bool>
{ {

View file

@ -141,6 +141,16 @@ protected:
const PlayerEventCmd * getPlayerEvent(unsigned iPlayerId, const PlayerEventCmd * getPlayerEvent(unsigned iPlayerId,
int iEventType) const; int iEventType) const;
/**
* Automatically set the solo for the current turn.
* First, all the existing solos (there could be several, in arbitration
* mode with a crazy arbitrator...) are removed. Then, a solo is given to
* the player deserving it, if any.
* Note that the minimum number of players specified in the preferences
* must be reached for the solo to be applicable.
*/
void setSoloAuto(unsigned int minNbPlayers, int iSoloValue);
private: // Used by friend classes private: // Used by friend classes
void innerSetMasterMove(const Move &iMove); void innerSetMasterMove(const Move &iMove);
bool isArbitrationGame() const; bool isArbitrationGame() const;

View file

@ -233,8 +233,12 @@ Settings::Settings()
// If true, a random rack is defined, otherwise the rack is left untouched // If true, a random rack is defined, otherwise the rack is left untouched
arbitration.add("fill-rack", Setting::TypeBoolean) = true; arbitration.add("fill-rack", Setting::TypeBoolean) = true;
// Number of search results kept in a search // If true, solos are automatically given when appropriate
arbitration.add("search-limit", Setting::TypeInt) = 100; // If false, the arbitrator has full control (but must do everything manually)
arbitration.add("solo-auto", Setting::TypeBoolean) = true;
// Minimum number of players in a duplicate 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) // Number of points granted for a solo (10 is the ODS value)
arbitration.add("solo-value", Setting::TypeInt) = 10; arbitration.add("solo-value", Setting::TypeInt) = 10;
@ -245,6 +249,9 @@ Settings::Settings()
// Maximum number of warnings before getting penalties // Maximum number of warnings before getting penalties
arbitration.add("warnings-limit", Setting::TypeInt) = 3; arbitration.add("warnings-limit", Setting::TypeInt) = 3;
// Number of search results kept in a search
arbitration.add("search-limit", Setting::TypeInt) = 100;
// Try to read the values from the configuration file // Try to read the values from the configuration file
try try
{ {
@ -260,13 +267,16 @@ Settings::Settings()
copySetting<bool>(tmpConf, *m_conf, "freegame.reject-invalid"); copySetting<bool>(tmpConf, *m_conf, "freegame.reject-invalid");
copySetting<bool>(tmpConf, *m_conf, "arbitration.fill-rack"); copySetting<bool>(tmpConf, *m_conf, "arbitration.fill-rack");
copySetting<int>(tmpConf, *m_conf, "arbitration.search-limit"); copySetting<int>(tmpConf, *m_conf, "arbitration.search-limit");
copySetting<bool>(tmpConf, *m_conf, "arbitration.solo-auto");
copySetting<int>(tmpConf, *m_conf, "arbitration.solo-players");
copySetting<int>(tmpConf, *m_conf, "arbitration.solo-value"); copySetting<int>(tmpConf, *m_conf, "arbitration.solo-value");
copySetting<int>(tmpConf, *m_conf, "arbitration.penalty-value"); copySetting<int>(tmpConf, *m_conf, "arbitration.penalty-value");
copySetting<int>(tmpConf, *m_conf, "arbitration.warnings-limit"); copySetting<int>(tmpConf, *m_conf, "arbitration.warnings-limit");
} }
catch (...) catch (const std::exception &e)
{ {
// Ignore the exception // Only log the exception
LOG_ERROR("Error reading config file: " << e.what());
} }
#endif #endif
} }
@ -347,6 +357,8 @@ int Settings::getInt(const string &iName) const
return 10; return 10;
else if (iName == "arbitration.search-limit") else if (iName == "arbitration.search-limit")
return 100; return 100;
else if (iName == "arbitration.solo-players")
return 16;
else if (iName == "arbitration.solo-value") else if (iName == "arbitration.solo-value")
return 5; return 5;
else if (iName == "arbitration.penalty-value") else if (iName == "arbitration.penalty-value")