mirror of
git://git.savannah.nongnu.org/eliot.git
synced 2025-01-18 10:26:15 +01:00
New arbitration mode, dedicate to arbitration of duplicate games.
It is mostly working, but many things are still missing. In particular: - ability to enter (or change) moves for a past turn - ability to change the rack (manually, or randomly) - ability to add/remove players during the game - support for solos, warnings, penalties - support for table number - more ergonomic interface - non regression tests - ... and probably bugs to fix
This commit is contained in:
parent
f44048f31a
commit
05a51101db
29 changed files with 1829 additions and 83 deletions
|
@ -52,6 +52,7 @@ libgame_a_SOURCES= \
|
|||
game_rack_cmd.h game_rack_cmd.cpp \
|
||||
turn_cmd.cpp turn_cmd.h \
|
||||
duplicate.cpp duplicate.h \
|
||||
arbitration.cpp arbitration.h \
|
||||
mark_played_cmd.h mark_played_cmd.cpp \
|
||||
master_move_cmd.h master_move_cmd.cpp \
|
||||
freegame.cpp freegame.h \
|
||||
|
|
87
game/arbitration.cpp
Normal file
87
game/arbitration.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2009 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*****************************************************************************/
|
||||
|
||||
#include "arbitration.h"
|
||||
#include "rack.h"
|
||||
#include "player.h"
|
||||
#include "settings.h"
|
||||
#include "encoding.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
INIT_LOGGER(game, Arbitration);
|
||||
|
||||
|
||||
Arbitration::Arbitration(const GameParams &iParams)
|
||||
: Duplicate(iParams), m_results(1000)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void Arbitration::search()
|
||||
{
|
||||
// Search for the current player
|
||||
const Rack &rack = m_players[m_currPlayer]->getCurrentRack().getRack();
|
||||
LOG_DEBUG("Performing search for rack " + lfw(rack.toString()));
|
||||
// FIXME arbitration begin
|
||||
int limit = Settings::Instance().getInt("training.search-limit");
|
||||
// FIXME arbitration end
|
||||
m_results.setLimit(limit);
|
||||
m_results.search(getDic(), getBoard(), rack, getHistory().beforeFirstRound());
|
||||
LOG_DEBUG("Found " << m_results.size() << " results");
|
||||
}
|
||||
|
||||
|
||||
Move Arbitration::checkWord(const wstring &iWord,
|
||||
const wstring &iCoords) const
|
||||
{
|
||||
Round round;
|
||||
int res = checkPlayedWord(iCoords, iWord, round);
|
||||
if (res == 0)
|
||||
return Move(round);
|
||||
return Move(iWord, iCoords);
|
||||
}
|
||||
|
||||
|
||||
void Arbitration::assignMove(unsigned int iPlayerId, const Move &iMove)
|
||||
{
|
||||
ASSERT(iPlayerId < getNPlayers(), "Wrong player number");
|
||||
// A move can only be assigned for the last turn
|
||||
ASSERT(accessNavigation().isLastTurn(), "This is not the last turn!");
|
||||
|
||||
Player &player = *m_players[iPlayerId];
|
||||
if (hasPlayed(iPlayerId))
|
||||
{
|
||||
LOG_INFO("Re-assigning move for player " << iPlayerId);
|
||||
undoPlayerMove(player);
|
||||
}
|
||||
recordPlayerMove(iMove, player, true);
|
||||
}
|
||||
|
||||
|
||||
void Arbitration::finalizeTurn()
|
||||
{
|
||||
m_results.clear();
|
||||
|
||||
// FIXME arbitration begin
|
||||
tryEndTurn();
|
||||
// FIXME arbitration end
|
||||
}
|
||||
|
56
game/arbitration.h
Normal file
56
game/arbitration.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2012 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef ARBITRATION_H_
|
||||
#define ARBITRATION_H_
|
||||
|
||||
#include "duplicate.h"
|
||||
#include "results.h"
|
||||
#include "logging.h"
|
||||
|
||||
|
||||
/**
|
||||
* This class simply extends the Duplicate game,
|
||||
* to specialize it for arbitration purposes.
|
||||
*/
|
||||
class Arbitration: public Duplicate
|
||||
{
|
||||
DEFINE_LOGGER();
|
||||
friend class GameFactory;
|
||||
public:
|
||||
void search();
|
||||
const Results& getResults() const { return m_results; }
|
||||
|
||||
Move checkWord(const wstring &iWord, const wstring &iCoords) const;
|
||||
|
||||
void assignMove(unsigned int iPlayerId, const Move &iMove);
|
||||
void finalizeTurn();
|
||||
|
||||
private:
|
||||
// Private constructor to force using the GameFactory class
|
||||
Arbitration(const GameParams &iParams);
|
||||
|
||||
/// Search results, with all the possible rounds up to a predefined limit
|
||||
LimitResults m_results;
|
||||
|
||||
};
|
||||
|
||||
#endif /* ARBITRATION_H_ */
|
||||
|
|
@ -46,6 +46,8 @@
|
|||
#include "mark_played_cmd.h"
|
||||
#include "master_move_cmd.h"
|
||||
#include "ai_player.h"
|
||||
#include "navigation.h"
|
||||
#include "turn_cmd.h"
|
||||
#include "settings.h"
|
||||
#include "encoding.h"
|
||||
#include "debug.h"
|
||||
|
@ -62,6 +64,8 @@ Duplicate::Duplicate(const GameParams &iParams)
|
|||
|
||||
int Duplicate::play(const wstring &iCoord, const wstring &iWord)
|
||||
{
|
||||
ASSERT(!hasPlayed(m_currPlayer), "Human player has already played");
|
||||
|
||||
// Perform all the validity checks, and try to fill a round
|
||||
Round round;
|
||||
int res = checkPlayedWord(iCoord, iWord, round);
|
||||
|
@ -97,6 +101,7 @@ int Duplicate::play(const wstring &iCoord, const wstring &iWord)
|
|||
void Duplicate::playAI(unsigned int p)
|
||||
{
|
||||
ASSERT(p < getNPlayers(), "Wrong player number");
|
||||
ASSERT(!hasPlayed(p), "AI player has already played");
|
||||
|
||||
AIPlayer *player = dynamic_cast<AIPlayer*>(m_players[p]);
|
||||
ASSERT(player != NULL, "AI requested for a human player");
|
||||
|
@ -129,6 +134,7 @@ void Duplicate::start()
|
|||
// 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)
|
||||
{
|
||||
|
@ -175,9 +181,11 @@ void Duplicate::tryEndTurn()
|
|||
|
||||
// Now that all the human players have played,
|
||||
// make AI players play their turn
|
||||
// Some may have already played, in arbitration mode, if the future turns
|
||||
// were removed (because of the isHumanIndependent() behaviour)
|
||||
for (unsigned int i = 0; i < getNPlayers(); i++)
|
||||
{
|
||||
if (!m_players[i]->isHuman())
|
||||
if (!m_players[i]->isHuman() && !m_hasPlayed[i])
|
||||
{
|
||||
playAI(i);
|
||||
}
|
||||
|
@ -191,7 +199,8 @@ void Duplicate::tryEndTurn()
|
|||
void Duplicate::recordPlayerMove(const Move &iMove, Player &ioPlayer, bool isForHuman)
|
||||
{
|
||||
LOG_INFO("Player " << ioPlayer.getId() << " plays: " << lfw(iMove.toString()));
|
||||
Command *pCmd = new PlayerMoveCmd(ioPlayer, iMove);
|
||||
bool isArbitration = getParams().getMode() == GameParams::kARBITRATION;
|
||||
Command *pCmd = new PlayerMoveCmd(ioPlayer, iMove, isArbitration);
|
||||
pCmd->setHumanIndependent(!isForHuman);
|
||||
accessNavigation().addAndExecute(pCmd);
|
||||
|
||||
|
@ -201,6 +210,43 @@ void Duplicate::recordPlayerMove(const Move &iMove, Player &ioPlayer, bool isFor
|
|||
}
|
||||
|
||||
|
||||
struct MatchingPlayer : public unary_function<PlayerMoveCmd, bool>
|
||||
{
|
||||
MatchingPlayer(unsigned iPlayerId) : m_playerId(iPlayerId) {}
|
||||
|
||||
bool operator()(const PlayerMoveCmd *cmd)
|
||||
{
|
||||
return cmd->getPlayer().getId() == m_playerId;
|
||||
}
|
||||
|
||||
const unsigned m_playerId;
|
||||
};
|
||||
|
||||
|
||||
void Duplicate::undoPlayerMove(Player &ioPlayer)
|
||||
{
|
||||
ASSERT(hasPlayed(ioPlayer.getId()), "The player has no assigned move yet!");
|
||||
// There must be no NAEC in the current (i.e. last) turn.
|
||||
// If there was, it might not be such a big deal, though.
|
||||
ASSERT(!getNavigation().getTurns().back()->hasNonAutoExecCmd(),
|
||||
"Cannot undo a player move when there are some NAEC commands");
|
||||
|
||||
// Find the PlayerMoveCmd we want to undo
|
||||
MatchingPlayer predicate(ioPlayer.getId());
|
||||
const PlayerMoveCmd *cmd =
|
||||
getNavigation().getCurrentTurn().findMatchingCmd<PlayerMoveCmd>(predicate);
|
||||
ASSERT(cmd != 0, "No matching PlayerMoveCmd found");
|
||||
|
||||
// Undo the player move
|
||||
Command *copyCmd = new PlayerMoveCmd(*cmd);
|
||||
accessNavigation().addAndExecute(new UndoCmd(copyCmd));
|
||||
|
||||
// OK, now flag the player as "not played". We can do it more directly...
|
||||
Command *pCmd = new MarkPlayedCmd(*this, ioPlayer.getId(), false);
|
||||
accessNavigation().addAndExecute(pCmd);
|
||||
}
|
||||
|
||||
|
||||
Player * Duplicate::findBestPlayer() const
|
||||
{
|
||||
Player *bestPlayer = NULL;
|
||||
|
@ -367,6 +413,7 @@ void Duplicate::setMasterMove(const Move &iMove)
|
|||
// If this method is called several times for the same turn, it will
|
||||
// result in many MasterMoveCmd commands in the command stack.
|
||||
// This shouldn't be a problem though.
|
||||
LOG_DEBUG("Setting master move: " + lfw(iMove.toString()));
|
||||
Command *pCmd = new MasterMoveCmd(*this, iMove);
|
||||
accessNavigation().addAndExecute(pCmd);
|
||||
}
|
||||
|
|
|
@ -104,29 +104,15 @@ public:
|
|||
/// Return true if the player has played for the current turn
|
||||
virtual bool hasPlayed(unsigned int iPlayerId) const;
|
||||
|
||||
private: // Used by friend classes
|
||||
/// Change the "has played" status of the given player to the given status
|
||||
// Note: only used by friend classes
|
||||
void setPlayedFlag(unsigned int iPlayerId, bool iNewFlag);
|
||||
|
||||
void innerSetMasterMove(const Move &iMove);
|
||||
|
||||
private:
|
||||
// Private constructor to force using the GameFactory class
|
||||
protected:
|
||||
// Protected constructor to force using the GameFactory class
|
||||
Duplicate(const GameParams &iParams);
|
||||
|
||||
/// Record a player move
|
||||
void recordPlayerMove(const Move &iMove, Player &ioPlayer, bool isForHuman);
|
||||
|
||||
/// Make the AI player whose ID is p play its turn
|
||||
void playAI(unsigned int p);
|
||||
|
||||
/**
|
||||
* Find the player who scored the most (with a valid move) at this turn.
|
||||
* If several players have the same score, one is returned arbitrarily.
|
||||
* If nobody played a valid move, the method returns a null pointer.
|
||||
*/
|
||||
Player * findBestPlayer() const;
|
||||
/// Cancel the last move of a player (in the current turn)
|
||||
void undoPlayerMove(Player &ioPlayer);
|
||||
|
||||
/**
|
||||
* This function does not terminate the turn itself, but performs some
|
||||
|
@ -142,6 +128,24 @@ private:
|
|||
*/
|
||||
void tryEndTurn();
|
||||
|
||||
private: // Used by friend classes
|
||||
/// Change the "has played" status of the given player to the given status
|
||||
// Note: only used by friend classes
|
||||
void setPlayedFlag(unsigned int iPlayerId, bool iNewFlag);
|
||||
|
||||
void innerSetMasterMove(const Move &iMove);
|
||||
|
||||
private:
|
||||
/// Make the AI player whose ID is p play its turn
|
||||
void playAI(unsigned int p);
|
||||
|
||||
/**
|
||||
* Find the player who scored the most (with a valid move) at this turn.
|
||||
* If several players have the same score, one is returned arbitrarily.
|
||||
* If nobody played a valid move, the method returns a null pointer.
|
||||
*/
|
||||
Player * findBestPlayer() const;
|
||||
|
||||
/**
|
||||
* This function really changes the turn, i.e. the best word is played,
|
||||
* the game history is updated, a "solo" bonus is given if needed, and
|
||||
|
|
|
@ -543,7 +543,9 @@ int Game::checkPlayedWord(const wstring &iCoord,
|
|||
if (res != 0)
|
||||
return res + 4;
|
||||
// In duplicate mode, the first word must be horizontal
|
||||
if (getMode() == GameParams::kDUPLICATE && m_board.isVacant(8, 8))
|
||||
if (m_board.isVacant(8, 8) &&
|
||||
(getMode() == GameParams::kDUPLICATE ||
|
||||
getMode() == GameParams::kARBITRATION))
|
||||
{
|
||||
if (oRound.getCoord().getDir() == Coord::VERTICAL)
|
||||
return 10;
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "training.h"
|
||||
#include "freegame.h"
|
||||
#include "duplicate.h"
|
||||
#include "arbitration.h"
|
||||
#include "player.h"
|
||||
#include "ai_percent.h"
|
||||
#include "dic.h"
|
||||
|
@ -98,6 +99,12 @@ Game *GameFactory::createGame(const GameParams &iParams)
|
|||
Duplicate *game = new Duplicate(iParams);
|
||||
return game;
|
||||
}
|
||||
if (iParams.getMode() == GameParams::kARBITRATION)
|
||||
{
|
||||
LOG_INFO("Creating an arbitration game");
|
||||
Arbitration *game = new Arbitration(iParams);
|
||||
return game;
|
||||
}
|
||||
throw GameException("Unknown game type");
|
||||
}
|
||||
|
||||
|
|
|
@ -212,6 +212,8 @@ Game* Game::gameLoadFormat_15(FILE *fin, const Dictionary& iDic)
|
|||
mode = GameParams::kFREEGAME;
|
||||
else if (strstr(buff, "Duplicate"))
|
||||
mode = GameParams::kDUPLICATE;
|
||||
else if (strstr(buff, "Arbitration"))
|
||||
mode = GameParams::kARBITRATION;
|
||||
else
|
||||
throw GameException("Unknown game type");
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ class GameParams
|
|||
kTRAINING,
|
||||
kFREEGAME,
|
||||
kDUPLICATE,
|
||||
kARBITRATION,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -28,10 +28,10 @@
|
|||
INIT_LOGGER(game, PlayerMoveCmd);
|
||||
|
||||
|
||||
PlayerMoveCmd::PlayerMoveCmd(Player &ioPlayer, const Move &iMove)
|
||||
PlayerMoveCmd::PlayerMoveCmd(Player &ioPlayer, const Move &iMove, bool iAutoExec)
|
||||
: m_player(ioPlayer), m_move(iMove)
|
||||
{
|
||||
setAutoExecutable(false);
|
||||
setAutoExecutable(iAutoExec);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -45,7 +45,8 @@ class PlayerMoveCmd: public Command
|
|||
DEFINE_LOGGER();
|
||||
|
||||
public:
|
||||
PlayerMoveCmd(Player &ioPlayer, const Move &iMove);
|
||||
PlayerMoveCmd(Player &ioPlayer, const Move &iMove,
|
||||
bool iAutoExec = false);
|
||||
|
||||
virtual wstring toString() const;
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "game.h"
|
||||
#include "training.h"
|
||||
#include "duplicate.h"
|
||||
#include "arbitration.h"
|
||||
#include "freegame.h"
|
||||
#include "game_factory.h"
|
||||
#include "game_exception.h"
|
||||
|
@ -43,7 +44,9 @@ PublicGame::~PublicGame()
|
|||
|
||||
PublicGame::GameMode PublicGame::getMode() const
|
||||
{
|
||||
if (dynamic_cast<Duplicate*>(&m_game))
|
||||
if (dynamic_cast<Arbitration*>(&m_game))
|
||||
return kARBITRATION;
|
||||
else if (dynamic_cast<Duplicate*>(&m_game))
|
||||
return kDUPLICATE;
|
||||
else if (dynamic_cast<FreeGame*>(&m_game))
|
||||
return kFREEGAME;
|
||||
|
@ -164,82 +167,102 @@ void PublicGame::removeTestRound()
|
|||
|
||||
/***************************/
|
||||
|
||||
static Training & getTrainingGame(Game &iGame)
|
||||
template <typename T>
|
||||
static T & getTypedGame(Game &iGame)
|
||||
{
|
||||
Training *trGame = dynamic_cast<Training *>(&iGame);
|
||||
if (trGame == NULL)
|
||||
T *typedGame = dynamic_cast<T*>(&iGame);
|
||||
if (typedGame == NULL)
|
||||
{
|
||||
throw GameException("Invalid game type");
|
||||
}
|
||||
return *trGame;
|
||||
return *typedGame;
|
||||
}
|
||||
|
||||
/***************************/
|
||||
|
||||
void PublicGame::trainingSearch()
|
||||
{
|
||||
getTrainingGame(m_game).search();
|
||||
getTypedGame<Training>(m_game).search();
|
||||
}
|
||||
|
||||
|
||||
const Results& PublicGame::trainingGetResults() const
|
||||
{
|
||||
return getTrainingGame(m_game).getResults();
|
||||
return getTypedGame<Training>(m_game).getResults();
|
||||
}
|
||||
|
||||
|
||||
int PublicGame::trainingPlayResult(unsigned int iResultIndex)
|
||||
{
|
||||
return getTrainingGame(m_game).playResult(iResultIndex);
|
||||
return getTypedGame<Training>(m_game).playResult(iResultIndex);
|
||||
}
|
||||
|
||||
|
||||
void PublicGame::trainingSetRackRandom(bool iCheck, RackMode iRackMode)
|
||||
{
|
||||
if (iRackMode == kRACK_NEW)
|
||||
getTrainingGame(m_game).setRackRandom(iCheck, Game::RACK_NEW);
|
||||
getTypedGame<Training>(m_game).setRackRandom(iCheck, Game::RACK_NEW);
|
||||
else
|
||||
getTrainingGame(m_game).setRackRandom(iCheck, Game::RACK_ALL);
|
||||
getTypedGame<Training>(m_game).setRackRandom(iCheck, Game::RACK_ALL);
|
||||
}
|
||||
|
||||
|
||||
void PublicGame::trainingSetRackManual(bool iCheck, const wstring &iLetters)
|
||||
{
|
||||
getTrainingGame(m_game).setRackManual(iCheck, iLetters);
|
||||
getTypedGame<Training>(m_game).setRackManual(iCheck, iLetters);
|
||||
}
|
||||
|
||||
/***************************/
|
||||
|
||||
static Duplicate & getDuplicateGame(Game &iGame)
|
||||
{
|
||||
Duplicate *dupGame = dynamic_cast<Duplicate *>(&iGame);
|
||||
if (dupGame == NULL)
|
||||
{
|
||||
throw GameException("Invalid game type");
|
||||
}
|
||||
return *dupGame;
|
||||
}
|
||||
|
||||
|
||||
void PublicGame::duplicateSetPlayer(unsigned int p)
|
||||
{
|
||||
getDuplicateGame(m_game).setPlayer(p);
|
||||
getTypedGame<Duplicate>(m_game).setPlayer(p);
|
||||
}
|
||||
|
||||
void PublicGame::duplicateSetMasterMove(const Move &iMove)
|
||||
{
|
||||
getTypedGame<Duplicate>(m_game).setMasterMove(iMove);
|
||||
}
|
||||
|
||||
const Move & PublicGame::duplicateGetMasterMove() const
|
||||
{
|
||||
return getTypedGame<Duplicate>(m_game).getMasterMove();
|
||||
}
|
||||
|
||||
/***************************/
|
||||
|
||||
static FreeGame & getFreeGameGame(Game &iGame)
|
||||
int PublicGame::freeGamePass(const wstring &iToChange)
|
||||
{
|
||||
FreeGame *frGame = dynamic_cast<FreeGame *>(&iGame);
|
||||
if (frGame == NULL)
|
||||
{
|
||||
throw GameException("Invalid game type");
|
||||
}
|
||||
return *frGame;
|
||||
return getTypedGame<FreeGame>(m_game).pass(iToChange);
|
||||
}
|
||||
|
||||
/***************************/
|
||||
|
||||
void PublicGame::arbitrationSearch()
|
||||
{
|
||||
return getTypedGame<Arbitration>(m_game).search();
|
||||
}
|
||||
|
||||
|
||||
int PublicGame::freeGamePass(const wstring &iToChange)
|
||||
const Results & PublicGame::arbitrationGetResults() const
|
||||
{
|
||||
return getFreeGameGame(m_game).pass(iToChange);
|
||||
return getTypedGame<Arbitration>(m_game).getResults();
|
||||
}
|
||||
|
||||
Move PublicGame::arbitrationCheckWord(const wstring &iWord,
|
||||
const wstring &iCoords) const
|
||||
{
|
||||
return getTypedGame<Arbitration>(m_game).checkWord(iWord, iCoords);
|
||||
}
|
||||
|
||||
void PublicGame::arbitrationAssign(unsigned int playerId, const Move &iMove)
|
||||
{
|
||||
getTypedGame<Arbitration>(m_game).assignMove(playerId, iMove);
|
||||
}
|
||||
|
||||
void PublicGame::arbitrationFinalizeTurn()
|
||||
{
|
||||
getTypedGame<Arbitration>(m_game).finalizeTurn();
|
||||
}
|
||||
|
||||
/***************************/
|
||||
|
|
|
@ -33,6 +33,7 @@ class Player;
|
|||
class Navigation;
|
||||
class Round;
|
||||
class Results;
|
||||
class Move;
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -66,7 +67,8 @@ public:
|
|||
{
|
||||
kTRAINING,
|
||||
kFREEGAME,
|
||||
kDUPLICATE
|
||||
kDUPLICATE,
|
||||
kARBITRATION,
|
||||
};
|
||||
GameMode getMode() const;
|
||||
|
||||
|
@ -209,6 +211,9 @@ public:
|
|||
*/
|
||||
void duplicateSetPlayer(unsigned int p);
|
||||
|
||||
void duplicateSetMasterMove(const Move &iMove);
|
||||
const Move & duplicateGetMasterMove() const;
|
||||
|
||||
/***************
|
||||
* FreeGame games
|
||||
* These methods throw an exception if the current game is not in
|
||||
|
@ -231,6 +236,21 @@ public:
|
|||
*/
|
||||
int freeGamePass(const wstring &iToChange);
|
||||
|
||||
/***************
|
||||
* Arbitration games
|
||||
* These methods throw an exception if the current game is not in
|
||||
* the Arbitration mode
|
||||
***************/
|
||||
|
||||
void arbitrationSearch();
|
||||
const Results& arbitrationGetResults() const;
|
||||
|
||||
Move arbitrationCheckWord(const wstring &iWord,
|
||||
const wstring &iCoords) const;
|
||||
|
||||
void arbitrationAssign(unsigned int playerId, const Move &iMove);
|
||||
void arbitrationFinalizeTurn();
|
||||
|
||||
/***************
|
||||
* Saved games handling
|
||||
***************/
|
||||
|
|
|
@ -187,7 +187,7 @@ void XmlReader::startElement(const string& namespaceURI,
|
|||
}
|
||||
}
|
||||
else if (tag == "GameRack" || tag == "PlayerRack" ||
|
||||
tag == "PlayerMove" || tag == "GameMove")
|
||||
tag == "PlayerMove" || tag == "GameMove" || tag == "MasterMove")
|
||||
{
|
||||
m_attributes.clear();
|
||||
for (int i = 0; i < atts.getLength(); ++i)
|
||||
|
@ -219,6 +219,8 @@ void XmlReader::endElement(const string& namespaceURI,
|
|||
m_params.setMode(GameParams::kFREEGAME);
|
||||
else if (m_data == "training")
|
||||
m_params.setMode(GameParams::kTRAINING);
|
||||
else if (m_data == "arbitration")
|
||||
m_params.setMode(GameParams::kARBITRATION);
|
||||
else
|
||||
throw GameException("Invalid game mode: " + m_data);
|
||||
return;
|
||||
|
|
|
@ -61,7 +61,7 @@ static string toUtf8(const wstring &s)
|
|||
}
|
||||
|
||||
static void writeMove(ostream &out, const Move &iMove,
|
||||
const string &iTag, unsigned int iPlayerId)
|
||||
const string &iTag, int iPlayerId)
|
||||
{
|
||||
out << "<" << iTag << " playerid=\"" << iPlayerId << "\" type=\"";
|
||||
if (iMove.getType() == Move::VALID_ROUND)
|
||||
|
@ -109,6 +109,8 @@ void XmlWriter::write(const Game &iGame, const string &iFileName)
|
|||
out << "duplicate";
|
||||
else if (iGame.getMode() == GameParams::kFREEGAME)
|
||||
out << "freegame";
|
||||
else if (iGame.getMode() == GameParams::kARBITRATION)
|
||||
out << "arbitration";
|
||||
else
|
||||
out << "training";
|
||||
out << "</Mode>" << endl;
|
||||
|
|
|
@ -44,6 +44,7 @@ EXTRA_DIST = \
|
|||
ui/dic_wizard_info_page.ui \
|
||||
ui/dic_wizard_letters_def_page.ui \
|
||||
ui/dic_wizard_conclusion_page.ui \
|
||||
ui/arbitration_widget.ui \
|
||||
ui/bag_widget.ui \
|
||||
ui/main_window.ui \
|
||||
ui/new_game.ui \
|
||||
|
@ -63,6 +64,7 @@ eliot_SOURCES = \
|
|||
tile_layout.cpp tile_layout.h \
|
||||
validator_factory.h validator_factory.cpp \
|
||||
custom_popup.cpp custom_popup.h \
|
||||
arbitration_widget.cpp arbitration_widget.h \
|
||||
bag_widget.cpp bag_widget.h \
|
||||
bag_widget2.cpp bag_widget2.h \
|
||||
dic_tools_widget.cpp dic_tools_widget.h \
|
||||
|
@ -82,6 +84,7 @@ eliot_SOURCES = \
|
|||
|
||||
nodist_eliot_SOURCES = \
|
||||
ui/main_window.ui.h \
|
||||
ui/arbitration_widget.ui.h \
|
||||
ui/bag_widget.ui.h \
|
||||
ui/new_game.ui.h \
|
||||
ui/player_widget.ui.h \
|
||||
|
@ -101,6 +104,7 @@ nodist_eliot_SOURCES = \
|
|||
players_table_helper.moc.cpp \
|
||||
new_game.moc.cpp \
|
||||
dic_tools_widget.moc.cpp \
|
||||
arbitration_widget.moc.cpp \
|
||||
bag_widget.moc.cpp \
|
||||
bag_widget2.moc.cpp \
|
||||
score_widget.moc.cpp \
|
||||
|
|
897
qt/arbitration_widget.cpp
Normal file
897
qt/arbitration_widget.cpp
Normal file
|
@ -0,0 +1,897 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2012 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*****************************************************************************/
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <vector>
|
||||
|
||||
#include <QtGui/QStandardItemModel>
|
||||
#include <QtGui/QSortFilterProxyModel>
|
||||
#include <QtGui/QMessageBox>
|
||||
#include <QtGui/QMenu>
|
||||
#include <QtGui/QKeyEvent>
|
||||
#include <QtCore/QSettings>
|
||||
|
||||
#include "arbitration_widget.h"
|
||||
#include "qtcommon.h"
|
||||
#include "prefs_dialog.h"
|
||||
#include "validator_factory.h"
|
||||
#include "play_word_mediator.h"
|
||||
#include "custom_popup.h"
|
||||
|
||||
#include "public_game.h"
|
||||
#include "player.h"
|
||||
#include "pldrack.h"
|
||||
#include "rack.h"
|
||||
#include "results.h"
|
||||
#include "coord_model.h"
|
||||
#include "settings.h"
|
||||
#include "dic.h"
|
||||
#include "debug.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static const int MOVE_TYPE_ROLE = Qt::UserRole;
|
||||
static const int MOVE_INDEX_ROLE = Qt::UserRole + 1;
|
||||
|
||||
static const int TYPE_ROUND = 1; // The result is a valid round, coming from a search
|
||||
static const int TYPE_ADDED = 2; // The result can be valid or invalid, it was manually added
|
||||
|
||||
INIT_LOGGER(qt, ArbitrationWidget);
|
||||
|
||||
|
||||
ArbitrationWidget::ArbitrationWidget(QWidget *parent,
|
||||
PublicGame *iGame, CoordModel &iCoordModel)
|
||||
: QWidget(parent), m_game(iGame), m_coordModel(iCoordModel)
|
||||
{
|
||||
setupUi(this);
|
||||
|
||||
// FIXME arbitration begin
|
||||
// The players widget uses more space by default
|
||||
splitter->setStretchFactor(0, 1);
|
||||
splitter->setStretchFactor(1, 2);
|
||||
// FIXME arbitration end
|
||||
|
||||
lineEditCoords->setValidator(ValidatorFactory::newCoordsValidator(this));
|
||||
|
||||
// Associate a model to the players view.
|
||||
// We use a proxy, to enable easy sorting of the players.
|
||||
m_proxyPlayersModel = new QSortFilterProxyModel(this);
|
||||
m_proxyPlayersModel->setDynamicSortFilter(true);
|
||||
m_playersModel = new QStandardItemModel(this);
|
||||
m_proxyPlayersModel->setSourceModel(m_playersModel);
|
||||
treeViewPlayers->setModel(m_proxyPlayersModel);
|
||||
m_playersModel->setColumnCount(4);
|
||||
m_playersModel->setHeaderData(0, Qt::Horizontal, _q("Player"), Qt::DisplayRole);
|
||||
m_playersModel->setHeaderData(1, Qt::Horizontal, _q("Word"), Qt::DisplayRole);
|
||||
m_playersModel->setHeaderData(2, Qt::Horizontal, _q("Ref"), Qt::DisplayRole);
|
||||
m_playersModel->setHeaderData(3, Qt::Horizontal, _q("Points"), Qt::DisplayRole);
|
||||
treeViewPlayers->sortByColumn(0, Qt::AscendingOrder);
|
||||
|
||||
treeViewPlayers->setColumnWidth(0, 160);
|
||||
treeViewPlayers->setColumnWidth(1, 160);
|
||||
treeViewPlayers->setColumnWidth(2, 40);
|
||||
treeViewPlayers->setColumnWidth(3, 50);
|
||||
|
||||
KeyEventFilter *filter = new KeyEventFilter(this, Qt::Key_T);
|
||||
QObject::connect(filter, SIGNAL(keyPressed()),
|
||||
this, SLOT(assignTopMove()));
|
||||
treeViewPlayers->installEventFilter(filter);
|
||||
|
||||
// Associate a model to the results view.
|
||||
// We use a proxy, to enable easy sorting/filtering of the results.
|
||||
m_proxyResultsModel = new QSortFilterProxyModel(this);
|
||||
m_proxyResultsModel->setDynamicSortFilter(true);
|
||||
m_proxyResultsModel->setFilterKeyColumn(0);
|
||||
m_proxyResultsModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
m_resultsModel = new QStandardItemModel(this);
|
||||
m_proxyResultsModel->setSourceModel(m_resultsModel);
|
||||
treeViewResults->setModel(m_proxyResultsModel);
|
||||
|
||||
m_resultsModel->setColumnCount(4);
|
||||
m_resultsModel->setHeaderData(0, Qt::Horizontal, _q("Word"), Qt::DisplayRole);
|
||||
m_resultsModel->setHeaderData(1, Qt::Horizontal, _q("Ref"), Qt::DisplayRole);
|
||||
m_resultsModel->setHeaderData(2, Qt::Horizontal, _q("Points"), Qt::DisplayRole);
|
||||
m_resultsModel->setHeaderData(3, Qt::Horizontal, _q("Status"), Qt::DisplayRole);
|
||||
treeViewResults->sortByColumn(2);
|
||||
|
||||
m_proxyResultsModel->setSupportedDragActions(Qt::CopyAction);
|
||||
|
||||
treeViewResults->setColumnWidth(0, 120);
|
||||
treeViewResults->setColumnWidth(1, 40);
|
||||
treeViewResults->setColumnWidth(2, 70);
|
||||
|
||||
// Propagate the information on rack change
|
||||
QObject::connect(lineEditRack, SIGNAL(textChanged(const QString&)),
|
||||
this, SIGNAL(rackUpdated(const QString&)));
|
||||
// Clear the results when the rack changes
|
||||
QObject::connect(lineEditRack, SIGNAL(textChanged(const QString&)),
|
||||
this, SLOT(clearResults()));
|
||||
|
||||
// Display a preview of the selected word on the board
|
||||
QObject::connect(treeViewResults->selectionModel(),
|
||||
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
|
||||
this, SLOT(showPreview(const QItemSelection&)));
|
||||
|
||||
// Dynamic filter for search results
|
||||
QObject::connect(lineEditFilter, SIGNAL(textChanged(const QString&)),
|
||||
this, SLOT(resultsFilterChanged(const QString&)));
|
||||
|
||||
// Enable the assignment buttons according to the selections in trees
|
||||
QObject::connect(treeViewPlayers->selectionModel(),
|
||||
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
|
||||
this, SLOT(enableAssignmentButtons()));
|
||||
QObject::connect(treeViewResults->selectionModel(),
|
||||
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
|
||||
this, SLOT(enableAssignmentButtons()));
|
||||
|
||||
// Coordinates model
|
||||
QObject::connect(&m_coordModel, SIGNAL(coordChanged(const Coord&, const Coord&)),
|
||||
this, SLOT(updateCoordText(const Coord&, const Coord&)));
|
||||
QObject::connect(lineEditCoords, SIGNAL(textChanged(const QString&)),
|
||||
this, SLOT(updateCoordModel(const QString&)));
|
||||
|
||||
// Enable the "Check word" button only when there is a word with coordinates
|
||||
QObject::connect(lineEditWord, SIGNAL(textChanged(const QString&)),
|
||||
this, SLOT(enableCheckWordButton()));
|
||||
QObject::connect(lineEditCoords, SIGNAL(textChanged(const QString&)),
|
||||
this, SLOT(enableCheckWordButton()));
|
||||
|
||||
// Check the given word
|
||||
QObject::connect(lineEditWord, SIGNAL(returnPressed()),
|
||||
this, SLOT(checkWord()));
|
||||
QObject::connect(lineEditCoords, SIGNAL(returnPressed()),
|
||||
this, SLOT(checkWord()));
|
||||
QObject::connect(buttonCheck, SIGNAL(clicked()),
|
||||
this, SLOT(checkWord()));
|
||||
|
||||
// Move assignment
|
||||
QObject::connect(treeViewResults, SIGNAL(activated(const QModelIndex&)),
|
||||
this, SLOT(assignMasterMove()));
|
||||
QObject::connect(buttonSelectMaster, SIGNAL(clicked()),
|
||||
this, SLOT(assignMasterMove()));
|
||||
QObject::connect(buttonAssign, SIGNAL(clicked()),
|
||||
this, SLOT(assignSelectedMove()));
|
||||
QObject::connect(buttonNoMove, SIGNAL(clicked()),
|
||||
this, SLOT(assignNoMove()));
|
||||
QObject::connect(treeViewPlayers, SIGNAL(activated(const QModelIndex&)),
|
||||
this, SLOT(assignSelectedMove()));
|
||||
|
||||
// End turn
|
||||
QObject::connect(buttonEndTurn, SIGNAL(clicked()),
|
||||
this, SLOT(endTurn()));
|
||||
|
||||
// Add a context menu for the results
|
||||
m_resultsPopup = new CustomPopup(treeViewResults);
|
||||
QObject::connect(m_resultsPopup, SIGNAL(popupCreated(QMenu&, const QPoint&)),
|
||||
this, SLOT(populateResultsMenu(QMenu&, const QPoint&)));
|
||||
QObject::connect(m_resultsPopup, SIGNAL(requestDefinition(QString)),
|
||||
this, SIGNAL(requestDefinition(QString)));
|
||||
// Add a context menu for the players
|
||||
CustomPopup *playersPopup = new CustomPopup(treeViewPlayers);
|
||||
QObject::connect(playersPopup, SIGNAL(popupCreated(QMenu&, const QPoint&)),
|
||||
this, SLOT(populatePlayersMenu(QMenu&, const QPoint&)));
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::refresh()
|
||||
{
|
||||
updateResultsModel();
|
||||
updatePlayersModel();
|
||||
|
||||
// Update the master move
|
||||
const Move &masterMove = m_game->duplicateGetMasterMove();
|
||||
if (masterMove.getType() == Move::NO_MOVE)
|
||||
{
|
||||
labelMasterMove->setText(QString("<i>%1</i>").arg(_q("Not selected yet")));
|
||||
}
|
||||
else
|
||||
{
|
||||
QString label = QString("<b>%1</b>").arg(formatMove(masterMove));
|
||||
labelMasterMove->setText(label);
|
||||
}
|
||||
|
||||
const PlayedRack &pldRack = m_game->getHistory().getCurrentRack();
|
||||
// Update the rack only if needed, to avoid losing cursor position
|
||||
QString qrack = qfw(pldRack.toString(PlayedRack::RACK_SIMPLE));
|
||||
if (qrack != lineEditRack->text())
|
||||
lineEditRack->setText(qrack);
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::updateResultsModel()
|
||||
{
|
||||
#if 1
|
||||
// FIXME arbitration begin
|
||||
// Consider that there is nothing to do if the number of lines is correct
|
||||
// This avoids problems when the game is updated for a test play
|
||||
if (m_game != NULL && m_game->isLastTurn() &&
|
||||
m_game->arbitrationGetResults().size() + m_addedMoves.size() ==
|
||||
static_cast<unsigned int>(m_resultsModel->rowCount()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// FIXME arbitration end
|
||||
#endif
|
||||
|
||||
m_resultsModel->removeRows(0, m_resultsModel->rowCount());
|
||||
if (m_game == NULL || !m_game->isLastTurn())
|
||||
return;
|
||||
|
||||
// First step: add the search results (type TYPE_ROUND)
|
||||
const Results &results = m_game->arbitrationGetResults();
|
||||
// Find the highest score
|
||||
int bestScore = getBestScore();
|
||||
for (unsigned int i = 0; i < results.size(); ++i)
|
||||
{
|
||||
Move move(results.get(i));
|
||||
addSingleMove(move, TYPE_ROUND, i, bestScore);
|
||||
}
|
||||
|
||||
// Second step: add the manually added words (type TYPE_ADDED)
|
||||
for (int i = 0; i < m_addedMoves.size(); ++i)
|
||||
{
|
||||
const Move &move = m_addedMoves.at(i);
|
||||
LOG_DEBUG("Adding custom move: " << lfw(move.toString()));
|
||||
addSingleMove(move, TYPE_ADDED, i, bestScore);
|
||||
}
|
||||
|
||||
// FIXME arbitration begin
|
||||
// XXX: useful?
|
||||
//m_proxyResultsModel->invalidate();
|
||||
// FIXME arbitration end
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::updatePlayersModel()
|
||||
{
|
||||
// Save the ID of the selected players
|
||||
QSet<unsigned int> playersIdSet = getSelectedPlayers();
|
||||
|
||||
m_playersModel->removeRows(0, m_playersModel->rowCount());
|
||||
if (m_game == NULL)
|
||||
return;
|
||||
|
||||
const bool hideAssignedPlayers = checkBoxHideAssigned->isChecked();
|
||||
for (unsigned int i = 0; i < m_game->getNbPlayers(); ++i)
|
||||
{
|
||||
const Player &player = m_game->getPlayer(i);
|
||||
if (hideAssignedPlayers && m_game->hasPlayed(player.getId()))
|
||||
continue;
|
||||
// Only display human players
|
||||
if (!player.isHuman())
|
||||
continue;
|
||||
const int rowNum = m_playersModel->rowCount();
|
||||
m_playersModel->insertRow(rowNum);
|
||||
m_playersModel->setData(m_playersModel->index(rowNum, 0), qfw(player.getName()));
|
||||
// Store the player ID
|
||||
m_playersModel->setData(m_playersModel->index(rowNum, 0),
|
||||
player.getId(), Qt::UserRole);
|
||||
// Display the played word, if any
|
||||
if (m_game->hasPlayed(player.getId()))
|
||||
{
|
||||
const Move &move = player.getLastMove();
|
||||
m_playersModel->setData(m_playersModel->index(rowNum, 3), move.getScore());
|
||||
|
||||
QPalette palette = treeViewPlayers->palette();
|
||||
QColor color = palette.color(QPalette::Normal, QPalette::WindowText);
|
||||
if (move.getType() == Move::VALID_ROUND)
|
||||
{
|
||||
const Round &round = move.getRound();
|
||||
m_playersModel->setData(m_playersModel->index(rowNum, 1), qfw(round.getWord()));
|
||||
m_playersModel->setData(m_playersModel->index(rowNum, 2), qfw(round.getCoord().toString()));
|
||||
}
|
||||
else if (move.getType() == Move::NO_MOVE)
|
||||
{
|
||||
m_playersModel->setData(m_playersModel->index(rowNum, 1), _q("(NO MOVE)"));
|
||||
color = Qt::blue;
|
||||
}
|
||||
else if (move.getType() == Move::INVALID_WORD)
|
||||
{
|
||||
m_playersModel->setData(m_playersModel->index(rowNum, 1), "<" + qfw(move.getBadWord()) + ">");
|
||||
m_playersModel->setData(m_playersModel->index(rowNum, 2), qfw(move.getBadCoord()));
|
||||
color = Qt::red;
|
||||
}
|
||||
// Apply the color
|
||||
const QBrush brush(color);
|
||||
m_playersModel->setData(m_playersModel->index(rowNum, 1),
|
||||
brush, Qt::ForegroundRole);
|
||||
m_playersModel->setData(m_playersModel->index(rowNum, 2),
|
||||
brush, Qt::ForegroundRole);
|
||||
}
|
||||
// Restore the selection
|
||||
if (playersIdSet.contains(player.getId()))
|
||||
{
|
||||
LOG_DEBUG("selecting player " << player.getId());
|
||||
treeViewPlayers->selectionModel()->select(m_playersModel->index(rowNum, 0),
|
||||
QItemSelectionModel::Select | QItemSelectionModel::Rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int ArbitrationWidget::addSingleMove(const Move &iMove, int moveType,
|
||||
unsigned int index, int bestScore)
|
||||
{
|
||||
static const QBrush redBrush(Qt::red);
|
||||
|
||||
int rowNum = m_resultsModel->rowCount();
|
||||
m_resultsModel->insertRow(rowNum);
|
||||
if (iMove.getType() == Move::VALID_ROUND)
|
||||
{
|
||||
const Round &r = iMove.getRound();
|
||||
m_resultsModel->setData(m_resultsModel->index(rowNum, 0), qfw(r.getWord()));
|
||||
m_resultsModel->setData(m_resultsModel->index(rowNum, 1),
|
||||
qfw(r.getCoord().toString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(iMove.getType() == Move::INVALID_WORD, "Unexpected move type");
|
||||
m_resultsModel->setData(m_resultsModel->index(rowNum, 0), qfw(iMove.getBadWord()));
|
||||
m_resultsModel->setData(m_resultsModel->index(rowNum, 1), qfw(iMove.getBadCoord()));
|
||||
m_resultsModel->setData(m_resultsModel->index(rowNum, 3), _q("Invalid"));
|
||||
m_resultsModel->setData(m_resultsModel->index(rowNum, 3), redBrush, Qt::ForegroundRole);
|
||||
#if 0
|
||||
QFont myFont;
|
||||
myFont.setBold(true);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
m_resultsModel->setData(m_resultsModel->index(rowNum, i),
|
||||
myFont, Qt::FontRole);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
m_resultsModel->setData(m_resultsModel->index(rowNum, 2), iMove.getScore());
|
||||
|
||||
if (iMove.getScore() == bestScore)
|
||||
{
|
||||
// Color the line in red if this is the top score
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
m_resultsModel->setData(m_resultsModel->index(rowNum, i),
|
||||
redBrush, Qt::ForegroundRole);
|
||||
}
|
||||
}
|
||||
|
||||
// Store the move type and round index in the first column, as user data
|
||||
m_resultsModel->setData(m_resultsModel->index(rowNum, 0), moveType, MOVE_TYPE_ROLE);
|
||||
m_resultsModel->setData(m_resultsModel->index(rowNum, 0), index, MOVE_INDEX_ROLE);
|
||||
|
||||
return rowNum;
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::resultsFilterChanged(const QString &iFilter)
|
||||
{
|
||||
treeViewResults->clearSelection();
|
||||
m_proxyResultsModel->setFilterFixedString(iFilter);
|
||||
emit gameUpdated();
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::updateCoordText(const Coord &, const Coord &iNewCoord)
|
||||
{
|
||||
if (iNewCoord.isValid() && lineEditCoords->text() != qfw(iNewCoord.toString()))
|
||||
lineEditCoords->setText(qfw(iNewCoord.toString()));
|
||||
else if (!iNewCoord.isValid() && lineEditCoords->hasAcceptableInput())
|
||||
lineEditCoords->setText("");
|
||||
lineEditWord->setFocus();
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::updateCoordModel(const QString &iText)
|
||||
{
|
||||
Coord c(wfq(iText));
|
||||
if (!(m_coordModel.getCoord() == c))
|
||||
m_coordModel.setCoord(Coord(wfq(iText)));
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::enableCheckWordButton()
|
||||
{
|
||||
buttonCheck->setEnabled(!lineEditWord->text().isEmpty() &&
|
||||
!lineEditCoords->text().isEmpty());
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::enableAssignmentButtons()
|
||||
{
|
||||
bool hasSelResult = m_game->isLastTurn() &&
|
||||
treeViewResults->selectionModel()->hasSelection();
|
||||
bool hasSelPlayer = m_game->isLastTurn() &&
|
||||
treeViewPlayers->selectionModel()->hasSelection();
|
||||
// Enable the "Assign move" button iff a move is selected
|
||||
// and at least one player in the tree view is selected
|
||||
buttonAssign->setEnabled(hasSelResult && hasSelPlayer);
|
||||
if (hasSelResult && hasSelPlayer)
|
||||
{
|
||||
const Move &move = getSelectedMove();
|
||||
buttonAssign->setToolTip(_q("Assign move (%1) to the selected player(s)")
|
||||
.arg(formatMove(move)));
|
||||
}
|
||||
buttonNoMove->setEnabled(hasSelPlayer);
|
||||
buttonSelectMaster->setEnabled(hasSelResult);
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::on_buttonSearch_clicked()
|
||||
{
|
||||
m_game->removeTestRound();
|
||||
emit notifyInfo(_q("Searching with rack '%1'...").arg(lineEditRack->text()));
|
||||
m_game->arbitrationSearch();
|
||||
emit notifyInfo(_q("Search done"));
|
||||
emit gameUpdated();
|
||||
|
||||
QSettings settings;
|
||||
if (settings.value(PrefsDialog::kARBIT_AUTO_MASTER, false).toBool())
|
||||
{
|
||||
assignDefaultMasterMove();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::populateResultsMenu(QMenu &iMenu, const QPoint &iPoint)
|
||||
{
|
||||
const QModelIndex &index = treeViewResults->indexAt(iPoint);
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
const Move &move = getSelectedMove();
|
||||
if (move.getType() == Move::VALID_ROUND)
|
||||
{
|
||||
// Action to display the word definition
|
||||
const QModelIndex &wordIndex = m_resultsModel->index(index.row(), 0);
|
||||
QString selectedWord = m_resultsModel->data(wordIndex).toString();
|
||||
m_resultsPopup->addShowDefinitionEntry(iMenu, selectedWord);
|
||||
|
||||
// Action to select as master move
|
||||
QAction *setAsMasterAction =
|
||||
new QAction(_q("Use as master move"), this);
|
||||
setAsMasterAction->setStatusTip(_q("Use the selected move (%1) as master move")
|
||||
.arg(formatMove(move)));
|
||||
setAsMasterAction->setShortcut(Qt::Key_Enter);
|
||||
QObject::connect(setAsMasterAction, SIGNAL(triggered()),
|
||||
this, SLOT(assignMasterMove()));
|
||||
iMenu.addAction(setAsMasterAction);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::populatePlayersMenu(QMenu &iMenu, const QPoint &iPoint)
|
||||
{
|
||||
const QModelIndex &index = treeViewPlayers->indexAt(iPoint);
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
// Action to assign the selected move
|
||||
if (treeViewResults->selectionModel()->hasSelection())
|
||||
{
|
||||
const Move &move = getSelectedMove();
|
||||
QAction *assignSelMoveAction =
|
||||
new QAction(_q("Assign selected move (%1)").arg(formatMove(move)), this);
|
||||
assignSelMoveAction->setStatusTip(_q("Assign move (%1) to the selected player(s)")
|
||||
.arg(formatMove(move)));
|
||||
assignSelMoveAction->setShortcut(Qt::Key_Enter);
|
||||
QObject::connect(assignSelMoveAction, SIGNAL(triggered()),
|
||||
this, SLOT(assignSelectedMove()));
|
||||
iMenu.addAction(assignSelMoveAction);
|
||||
}
|
||||
|
||||
#if 1
|
||||
// Action to assign the top move
|
||||
QAction *assignTopMoveAction =
|
||||
new QAction(_q("Assign top move"), this);
|
||||
assignTopMoveAction->setStatusTip(_q("Assign the top move (if unique) to the selected player(s)"));
|
||||
assignTopMoveAction->setShortcut(Qt::Key_T);
|
||||
QObject::connect(assignTopMoveAction, SIGNAL(triggered()),
|
||||
this, SLOT(assignTopMove()));
|
||||
iMenu.addAction(assignTopMoveAction);
|
||||
#endif
|
||||
|
||||
// TODO: add other actions
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::checkWord()
|
||||
{
|
||||
if (lineEditWord->text().isEmpty() ||
|
||||
lineEditCoords->text().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the played word
|
||||
wstring playedWord;
|
||||
QString errorMsg;
|
||||
bool ok = PlayWordMediator::GetPlayedWord(*lineEditWord, m_game->getDic(),
|
||||
&playedWord, &errorMsg);
|
||||
if (!ok)
|
||||
{
|
||||
emit notifyProblem(errorMsg);
|
||||
return;
|
||||
}
|
||||
// Retrieve the coordinates
|
||||
const wstring &coords = wfq(lineEditCoords->text());
|
||||
|
||||
// Check the word
|
||||
const Move &move = m_game->arbitrationCheckWord(playedWord, coords);
|
||||
|
||||
// XXX: we add it even if it is already present in the results.
|
||||
// This is not a real problem, but it is not particularly nice :)
|
||||
m_addedMoves.append(move);
|
||||
int rowNum = addSingleMove(move, TYPE_ADDED, m_addedMoves.size() - 1,
|
||||
getBestScore());
|
||||
|
||||
lineEditWord->clear();
|
||||
lineEditCoords->clear();
|
||||
|
||||
// Show the new result and select it
|
||||
const QModelIndex &index =
|
||||
m_proxyResultsModel->mapFromSource(m_resultsModel->index(rowNum, 0));
|
||||
treeViewResults->scrollTo(index);
|
||||
treeViewResults->selectionModel()->clearSelection();
|
||||
treeViewResults->selectionModel()->select(index,
|
||||
QItemSelectionModel::Select | QItemSelectionModel::Rows);
|
||||
treeViewResults->setFocus();
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::on_checkBoxHideAssigned_toggled(bool)
|
||||
{
|
||||
treeViewPlayers->selectionModel()->clearSelection();
|
||||
updatePlayersModel();
|
||||
}
|
||||
|
||||
|
||||
Move ArbitrationWidget::getSelectedMove() const
|
||||
{
|
||||
// Get the tree selection
|
||||
const QItemSelection &proxySelected = treeViewResults->selectionModel()->selection();
|
||||
ASSERT(!proxySelected.empty(), "Empty selection!");
|
||||
|
||||
// Map the selection to a source model index
|
||||
const QItemSelection &srcSelected = m_proxyResultsModel->mapSelectionToSource(proxySelected);
|
||||
// Use the hidden column to get the result number
|
||||
const QModelIndex &index =
|
||||
m_resultsModel->index(srcSelected.indexes().first().row(), 0);
|
||||
|
||||
int origin = m_resultsModel->data(index, MOVE_TYPE_ROLE).toInt();
|
||||
if (origin == TYPE_ROUND)
|
||||
{
|
||||
unsigned int resNb = m_resultsModel->data(index, MOVE_INDEX_ROLE).toUInt();
|
||||
const Results &results = m_game->arbitrationGetResults();
|
||||
ASSERT(resNb < results.size(), "Wrong result number");
|
||||
return Move(results.get(resNb));
|
||||
}
|
||||
else
|
||||
{
|
||||
int vectPos = m_resultsModel->data(index, MOVE_INDEX_ROLE).toInt();
|
||||
ASSERT(vectPos >= 0 && vectPos < m_addedMoves.size(), "Wrong index number");
|
||||
return m_addedMoves.at(vectPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QSet<unsigned int> ArbitrationWidget::getSelectedPlayers() const
|
||||
{
|
||||
QSet<unsigned int> playersIdSet;
|
||||
|
||||
// Get the tree selection
|
||||
const QItemSelection &proxySelected = treeViewPlayers->selectionModel()->selection();
|
||||
// Map the selection to a source model index
|
||||
const QItemSelection &srcSelected = m_proxyPlayersModel->mapSelectionToSource(proxySelected);
|
||||
// Get the player ID for each line
|
||||
Q_FOREACH(const QModelIndex &index, srcSelected.indexes())
|
||||
{
|
||||
// Only take the first column into account
|
||||
if (index.column() != 0)
|
||||
continue;
|
||||
playersIdSet.insert(m_playersModel->data(index, Qt::UserRole).toUInt());
|
||||
}
|
||||
|
||||
return playersIdSet;
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::clearResults()
|
||||
{
|
||||
#if 0
|
||||
// FIXME arbitration begin
|
||||
m_game->removeTestRound();
|
||||
m_resultsModel->clear();
|
||||
// FIXME arbitration end
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::showPreview(const QItemSelection &iSelected)
|
||||
{
|
||||
m_game->removeTestRound();
|
||||
if (!iSelected.indexes().empty())
|
||||
{
|
||||
const Move &move = getSelectedMove();
|
||||
if (move.getType() == Move::VALID_ROUND)
|
||||
{
|
||||
m_game->setTestRound(move.getRound());
|
||||
}
|
||||
emit gameUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rack ArbitrationWidget::getRack() const
|
||||
{
|
||||
return m_game->getHistory().getCurrentRack().getRack();
|
||||
}
|
||||
|
||||
|
||||
int ArbitrationWidget::getBestScore() const
|
||||
{
|
||||
// Note: we could cache the result of this method
|
||||
BestResults results;
|
||||
results.search(m_game->getDic(), m_game->getBoard(),
|
||||
getRack(), m_game->getHistory().beforeFirstRound());
|
||||
ASSERT(results.size() != 0, "No possible valid move");
|
||||
return results.get(0).getPoints();
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::assignMasterMove()
|
||||
{
|
||||
if (m_game->isFinished())
|
||||
return;
|
||||
|
||||
const Move &masterMove = m_game->duplicateGetMasterMove();
|
||||
// Make sure the user knows what she's doing
|
||||
if (masterMove.getType() != Move::NO_MOVE)
|
||||
{
|
||||
QString msg = _q("There is already a master move for this turn.");
|
||||
QString question = _q("Do you want to replace it?");
|
||||
if (!QtCommon::requestConfirmation(msg, question))
|
||||
return;
|
||||
}
|
||||
|
||||
const Move &move = getSelectedMove();
|
||||
if (move.getType() != Move::VALID_ROUND)
|
||||
{
|
||||
notifyProblem(_q("The master move must be a valid move."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Warn if the selected move is not a top move
|
||||
BestResults results;
|
||||
results.search(m_game->getDic(), m_game->getBoard(),
|
||||
getRack(), m_game->getHistory().beforeFirstRound());
|
||||
ASSERT(results.size() != 0, "No possible valid move");
|
||||
int bestScore = results.get(0).getPoints();
|
||||
if (bestScore > move.getScore())
|
||||
{
|
||||
QString msg = _q("The selected move scores less than the maximum.");
|
||||
QString question = _q("Do you really want to select it as master move?");
|
||||
if (!QtCommon::requestConfirmation(msg, question))
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// A top move saving a joker should be prefered over one not
|
||||
// saving it, according to the French official rules.
|
||||
// Warn if the user tries to ignore the rule.
|
||||
unsigned jokerCount = move.getRound().countJokersFromRack();
|
||||
if (jokerCount > 0)
|
||||
{
|
||||
for (unsigned i = 0; i < results.size(); ++i)
|
||||
{
|
||||
if (results.get(i).countJokersFromRack() < jokerCount)
|
||||
{
|
||||
QString msg = _q("The selected move uses more jokers than "
|
||||
"another move with the same score (%1).")
|
||||
.arg(formatMove(Move(results.get(i))));
|
||||
QString question = _q("Do you really want to select it as master move?");
|
||||
if (!QtCommon::requestConfirmation(msg, question))
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assign the master move
|
||||
m_game->duplicateSetMasterMove(move);
|
||||
emit gameUpdated();
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::assignDefaultMasterMove()
|
||||
{
|
||||
const Move &currMove = m_game->duplicateGetMasterMove();
|
||||
// Do not overwrite an existing move
|
||||
if (currMove.getType() != Move::NO_MOVE)
|
||||
return;
|
||||
|
||||
// Search the best moves
|
||||
BestResults results;
|
||||
results.search(m_game->getDic(), m_game->getBoard(),
|
||||
getRack(), m_game->getHistory().beforeFirstRound());
|
||||
// XXX: End of game
|
||||
if (results.size() == 0)
|
||||
return;
|
||||
|
||||
unsigned currIndex = 0;
|
||||
unsigned jokerCount = results.get(0).countJokersFromRack();
|
||||
if (jokerCount > 0)
|
||||
{
|
||||
for (unsigned i = 1; i < results.size(); ++i)
|
||||
{
|
||||
if (results.get(i).countJokersFromRack() < jokerCount)
|
||||
{
|
||||
currIndex = i;
|
||||
jokerCount = results.get(i).countJokersFromRack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assign the master move
|
||||
Move move = Move(results.get(currIndex));
|
||||
m_game->duplicateSetMasterMove(move);
|
||||
emit gameUpdated();
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::assignSelectedMove()
|
||||
{
|
||||
if (!treeViewResults->selectionModel()->hasSelection())
|
||||
return;
|
||||
helperAssignMove(getSelectedMove());
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::assignTopMove()
|
||||
{
|
||||
BestResults results;
|
||||
results.search(m_game->getDic(), m_game->getBoard(),
|
||||
getRack(), m_game->getHistory().beforeFirstRound());
|
||||
// TODO: what if there are several moves?
|
||||
if (results.size() == 1)
|
||||
helperAssignMove(Move(results.get(0)));
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::assignNoMove()
|
||||
{
|
||||
helperAssignMove(Move());
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::helperAssignMove(const Move &iMove)
|
||||
{
|
||||
QSet<unsigned int> playersIdSet = getSelectedPlayers();
|
||||
|
||||
// Warn if some of the selected players already have an assigned move
|
||||
QSet<unsigned int> assignedIdSet;
|
||||
BOOST_FOREACH(unsigned int id, playersIdSet)
|
||||
{
|
||||
if (m_game->hasPlayed(id))
|
||||
assignedIdSet.insert(id);
|
||||
}
|
||||
if (!assignedIdSet.empty())
|
||||
{
|
||||
QString msg = _q("The following players already have an assigned move:\n");
|
||||
BOOST_FOREACH(unsigned int id, assignedIdSet)
|
||||
{
|
||||
msg += QString("\t%1\n").arg(qfw(m_game->getPlayer(id).getName()));
|
||||
}
|
||||
QString question = _q("Do you want to replace it?");
|
||||
if (!QtCommon::requestConfirmation(msg, question))
|
||||
return;
|
||||
}
|
||||
|
||||
// Assign the move to each selected player
|
||||
BOOST_FOREACH(unsigned int id, playersIdSet)
|
||||
{
|
||||
LOG_DEBUG(lfq(QString("Assigning move %1 to player %2")
|
||||
.arg(qfw(iMove.toString())).arg(id)));
|
||||
|
||||
// Assign the move
|
||||
m_game->arbitrationAssign(id, iMove);
|
||||
}
|
||||
emit notifyInfo(_q("Move assigned to player(s)"));
|
||||
emit gameUpdated();
|
||||
}
|
||||
|
||||
|
||||
QString ArbitrationWidget::formatMove(const Move &iMove) const
|
||||
{
|
||||
if (iMove.getType() == Move::VALID_ROUND)
|
||||
{
|
||||
return QString("%1 - %2 - %3")
|
||||
.arg(qfw(iMove.getRound().getWord()))
|
||||
.arg(qfw(iMove.getRound().getCoord().toString()))
|
||||
.arg(iMove.getScore());
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(iMove.getType() == Move::INVALID_WORD, "Unexpected move type");
|
||||
return QString("%1 - %2 - %3")
|
||||
.arg(qfw(iMove.getBadWord()))
|
||||
.arg(qfw(iMove.getBadCoord()))
|
||||
.arg(iMove.getScore());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ArbitrationWidget::endTurn()
|
||||
{
|
||||
QSet<unsigned int> notPlayerIdSet;
|
||||
unsigned int nbPlayers = m_game->getNbPlayers();
|
||||
for (unsigned int i = 0; i < nbPlayers; ++i)
|
||||
{
|
||||
if (!m_game->hasPlayed(i) && m_game->getPlayer(i).isHuman())
|
||||
notPlayerIdSet.insert(i);
|
||||
}
|
||||
if (!notPlayerIdSet.empty())
|
||||
{
|
||||
notifyProblem(_q("All the players must have an assigned move before ending the turn."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_game->duplicateGetMasterMove().getType() != Move::VALID_ROUND)
|
||||
{
|
||||
notifyProblem(_q("You must select a master move before ending the turn."));
|
||||
return;
|
||||
}
|
||||
|
||||
m_addedMoves.clear();
|
||||
|
||||
emit notifyInfo(_q("New turn started"));
|
||||
|
||||
m_game->removeTestRound();
|
||||
m_game->arbitrationFinalizeTurn();
|
||||
setEnabled(!m_game->isFinished());
|
||||
|
||||
emit gameUpdated();
|
||||
}
|
||||
|
||||
|
||||
|
||||
KeyEventFilter::KeyEventFilter(QObject *parent, int key, int modifiers)
|
||||
: QObject(parent), m_modifiers(modifiers), m_key(key)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool KeyEventFilter::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
// If the Delete key is pressed, remove the selected line, if any
|
||||
if (event->type() == QEvent::KeyPress)
|
||||
{
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
if (keyEvent->key() == m_key &&
|
||||
keyEvent->modifiers() == m_modifiers)
|
||||
{
|
||||
emit keyPressed();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Standard event processing
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
|
154
qt/arbitration_widget.h
Normal file
154
qt/arbitration_widget.h
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2012 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef ARBITRATION_WIDGET_H_
|
||||
#define ARBITRATION_WIDGET_H_
|
||||
|
||||
#include <QtGui/QWidget>
|
||||
|
||||
#include <ui/arbitration_widget.ui.h>
|
||||
#include "move.h"
|
||||
#include "logging.h"
|
||||
|
||||
class PublicGame;
|
||||
class CoordModel;
|
||||
class CustomPopup;
|
||||
class QStandardItemModel;
|
||||
class QSortFilterProxyModel;
|
||||
class QMenu;
|
||||
class QPoint;
|
||||
class QKeyEvent;
|
||||
|
||||
class ArbitrationWidget: public QWidget, private Ui::ArbitrationWidget
|
||||
{
|
||||
Q_OBJECT;
|
||||
DEFINE_LOGGER();
|
||||
|
||||
public:
|
||||
ArbitrationWidget(QWidget *parent,
|
||||
PublicGame *iGame,
|
||||
CoordModel &iCoordModel);
|
||||
|
||||
signals:
|
||||
void gameUpdated();
|
||||
void notifyProblem(QString iMsg);
|
||||
void notifyInfo(QString iMsg);
|
||||
void rackUpdated(const QString &iRack);
|
||||
void requestDefinition(QString iWord);
|
||||
|
||||
public slots:
|
||||
void refresh();
|
||||
|
||||
private slots:
|
||||
void on_buttonSearch_clicked();
|
||||
void on_checkBoxHideAssigned_toggled(bool);
|
||||
void resultsFilterChanged(const QString &);
|
||||
void enableCheckWordButton();
|
||||
void enableAssignmentButtons();
|
||||
void clearResults();
|
||||
void showPreview(const QItemSelection &);
|
||||
void updateCoordText(const Coord&, const Coord&);
|
||||
void updateCoordModel(const QString&);
|
||||
void populateResultsMenu(QMenu &iMenu, const QPoint &iPoint);
|
||||
void populatePlayersMenu(QMenu &iMenu, const QPoint &iPoint);
|
||||
void checkWord();
|
||||
void assignMasterMove();
|
||||
void assignDefaultMasterMove();
|
||||
void assignSelectedMove();
|
||||
void assignTopMove();
|
||||
void assignNoMove();
|
||||
void endTurn();
|
||||
|
||||
private:
|
||||
/// Encapsulated game, can be NULL
|
||||
PublicGame *m_game;
|
||||
|
||||
/// Coordinates of the next word to play
|
||||
CoordModel &m_coordModel;
|
||||
|
||||
/// Model for the search results
|
||||
QStandardItemModel *m_resultsModel;
|
||||
/// Proxy for the results model
|
||||
QSortFilterProxyModel *m_proxyResultsModel;
|
||||
|
||||
/// Model for the players list
|
||||
QStandardItemModel *m_playersModel;
|
||||
/// Proxy for the players model
|
||||
QSortFilterProxyModel *m_proxyPlayersModel;
|
||||
|
||||
/// Popup menu for the search results
|
||||
CustomPopup *m_resultsPopup;
|
||||
|
||||
/// Container for the moves manually entered in the interface
|
||||
QVector<Move> m_addedMoves;
|
||||
|
||||
/// Force synchronizing the model with the search results
|
||||
void updateResultsModel();
|
||||
/// Force synchronizing the model with the players
|
||||
void updatePlayersModel();
|
||||
|
||||
/**
|
||||
* Add the given move to the results list.
|
||||
* Return the row number of the added item.
|
||||
*/
|
||||
int addSingleMove(const Move &iMove, int moveType,
|
||||
unsigned int index, int bestScore);
|
||||
|
||||
/// Return the rack for the current turn
|
||||
// FIXME: this feature should be provided by the core
|
||||
Rack getRack() const;
|
||||
|
||||
int getBestScore() const;
|
||||
|
||||
/// Helper method to return a structured move for the selected result
|
||||
Move getSelectedMove() const;
|
||||
/// Helper method to return the ID of the selected player(s)
|
||||
QSet<unsigned int> getSelectedPlayers() const;
|
||||
|
||||
/// Helper method to assign the given move to the selected player(s)
|
||||
void helperAssignMove(const Move &iMove);
|
||||
|
||||
/// Format the given move under the format WORD - Ref - Pts
|
||||
QString formatMove(const Move &iMove) const;
|
||||
};
|
||||
|
||||
|
||||
/// Event filter used for the edition of the players display
|
||||
class KeyEventFilter: public QObject
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
KeyEventFilter(QObject *parent, int key, int modifier = Qt::NoModifier);
|
||||
|
||||
signals:
|
||||
/// As its name indicates...
|
||||
void keyPressed();
|
||||
|
||||
protected:
|
||||
virtual bool eventFilter(QObject *obj, QEvent *event);
|
||||
|
||||
private:
|
||||
int m_modifiers;
|
||||
int m_key;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -54,6 +54,7 @@
|
|||
#include "score_widget.h"
|
||||
#include "player_widget.h"
|
||||
#include "training_widget.h"
|
||||
#include "arbitration_widget.h"
|
||||
#include "history_widget.h"
|
||||
#include "dic_tools_widget.h"
|
||||
#include "players_table_helper.h"
|
||||
|
@ -73,7 +74,8 @@ const char *MainWindow::m_windowName = "MainWindow";
|
|||
MainWindow::MainWindow(QWidget *iParent)
|
||||
: QMainWindow(iParent), m_dic(NULL), m_game(NULL),
|
||||
m_newGameDialog(NULL), m_prefsDialog(NULL),
|
||||
m_playersWidget(NULL), m_trainingWidget(NULL), m_scoresWidget(NULL),
|
||||
m_playersWidget(NULL), m_trainingWidget(NULL),
|
||||
m_arbitrationWidget(NULL), m_scoresWidget(NULL),
|
||||
m_bagWindow(NULL), m_boardWindow(NULL),
|
||||
m_historyWindow(NULL), m_timerWindow(NULL),
|
||||
m_dicToolsWindow(NULL), m_dicNameLabel(NULL), m_timerModel(NULL),
|
||||
|
@ -270,16 +272,33 @@ void MainWindow::linkTrainingAnd7P1()
|
|||
// dictionary tools
|
||||
m_trainingWidget->disconnect(SIGNAL(rackUpdated(const QString&)));
|
||||
// Reconnect it only if needed
|
||||
if (m_dicToolsWindow != NULL)
|
||||
QSettings qs;
|
||||
if (qs.value(PrefsDialog::kINTF_LINK_TRAINING_7P1, false).toBool())
|
||||
{
|
||||
QSettings qs;
|
||||
if (qs.value(PrefsDialog::kINTF_LINK_TRAINING_7P1, false).toBool())
|
||||
{
|
||||
QObject::connect(m_trainingWidget,
|
||||
SIGNAL(rackUpdated(const QString&)),
|
||||
&m_dicToolsWindow->getWidget(),
|
||||
SLOT(setPlus1Rack(const QString&)));
|
||||
}
|
||||
QObject::connect(m_trainingWidget,
|
||||
SIGNAL(rackUpdated(const QString&)),
|
||||
&m_dicToolsWindow->getWidget(),
|
||||
SLOT(setPlus1Rack(const QString&)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::linkArbitrationAnd7P1()
|
||||
{
|
||||
if (m_arbitrationWidget == NULL || m_dicToolsWindow == NULL)
|
||||
return;
|
||||
|
||||
// Disconnect the arbitration rack updates from the "Plus 1" tab of the
|
||||
// dictionary tools
|
||||
m_arbitrationWidget->disconnect(SIGNAL(rackUpdated(const QString&)));
|
||||
// Reconnect it only if needed
|
||||
QSettings qs;
|
||||
if (qs.value(PrefsDialog::kARBIT_LINK_7P1, false).toBool())
|
||||
{
|
||||
QObject::connect(m_arbitrationWidget,
|
||||
SIGNAL(rackUpdated(const QString&)),
|
||||
&m_dicToolsWindow->getWidget(),
|
||||
SLOT(setPlus1Rack(const QString&)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,6 +308,7 @@ void MainWindow::prefsUpdated()
|
|||
LOG_DEBUG("Preferences updated");
|
||||
// Refresh one signal/slot connection
|
||||
linkTrainingAnd7P1();
|
||||
linkArbitrationAnd7P1();
|
||||
|
||||
// XXX: is this preference still useful?
|
||||
#if 0
|
||||
|
@ -335,6 +355,10 @@ void MainWindow::updateForGame(PublicGame *iGame)
|
|||
QtCommon::DestroyObject(m_trainingWidget, this);
|
||||
m_trainingWidget = NULL;
|
||||
|
||||
// Destroy the arbitration widget
|
||||
QtCommon::DestroyObject(m_arbitrationWidget, this);
|
||||
m_arbitrationWidget = NULL;
|
||||
|
||||
// Destroy the scores widget
|
||||
QtCommon::DestroyObject(m_scoresWidget, this);
|
||||
m_scoresWidget = NULL;
|
||||
|
@ -375,6 +399,26 @@ void MainWindow::updateForGame(PublicGame *iGame)
|
|||
QObject::connect(this, SIGNAL(gameUpdated()),
|
||||
m_scoresWidget, SLOT(refresh()));
|
||||
}
|
||||
else if (iGame->getMode() == PublicGame::kARBITRATION)
|
||||
{
|
||||
setWindowTitle(_q("Arbitration game") + " - Eliot");
|
||||
m_ui.groupBoxPlayers->setTitle(_q("Arbitration"));
|
||||
|
||||
m_arbitrationWidget = new ArbitrationWidget(NULL, iGame, m_coordModel);
|
||||
m_ui.groupBoxPlayers->layout()->addWidget(m_arbitrationWidget);
|
||||
QObject::connect(m_arbitrationWidget, SIGNAL(gameUpdated()),
|
||||
this, SIGNAL(gameUpdated()));
|
||||
QObject::connect(m_arbitrationWidget, SIGNAL(notifyInfo(QString)),
|
||||
this, SLOT(displayInfoMsg(QString)));
|
||||
QObject::connect(m_arbitrationWidget, SIGNAL(notifyProblem(QString)),
|
||||
this, SLOT(displayErrorMsg(QString)));
|
||||
QObject::connect(m_arbitrationWidget, SIGNAL(requestDefinition(QString)),
|
||||
this, SLOT(showDefinition(QString)));
|
||||
QObject::connect(this, SIGNAL(gameUpdated()),
|
||||
m_arbitrationWidget, SLOT(refresh()));
|
||||
// Connect with the dictionary tools only if needed
|
||||
linkArbitrationAnd7P1();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iGame->getMode() == PublicGame::kDUPLICATE)
|
||||
|
@ -1146,8 +1190,9 @@ void MainWindow::onWindowsDicTools()
|
|||
dicTools, SLOT(setDic(const Dictionary*)));
|
||||
QObject::connect(dicTools, SIGNAL(requestDefinition(QString)),
|
||||
this, SLOT(showDefinition(QString)));
|
||||
// Link the training rack with the "Plus 1" one
|
||||
// Link the training or arbitration rack with the "Plus 1" one
|
||||
linkTrainingAnd7P1();
|
||||
linkArbitrationAnd7P1();
|
||||
// Fake a dictionary selection
|
||||
dicTools->setDic(m_dic);
|
||||
dicTools->setFocus();
|
||||
|
|
|
@ -38,6 +38,7 @@ class PrefsDialog;
|
|||
class PlayerTabWidget;
|
||||
class ScoreWidget;
|
||||
class TrainingWidget;
|
||||
class ArbitrationWidget;
|
||||
class AuxWindow;
|
||||
class TimerModel;
|
||||
class QLabel;
|
||||
|
@ -135,6 +136,9 @@ private:
|
|||
/// Widget for the training mode
|
||||
TrainingWidget *m_trainingWidget;
|
||||
|
||||
/// Widget for the arbitration mode
|
||||
ArbitrationWidget *m_arbitrationWidget;
|
||||
|
||||
/// Widget for the scores
|
||||
ScoreWidget *m_scoresWidget;
|
||||
|
||||
|
@ -203,6 +207,12 @@ private:
|
|||
*/
|
||||
void linkTrainingAnd7P1();
|
||||
|
||||
/**
|
||||
* Handle correctly the signal/slot connection between the Arbitration
|
||||
* widget and the 7+1 dictionary tool
|
||||
*/
|
||||
void linkArbitrationAnd7P1();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -27,9 +27,6 @@
|
|||
#include "game_factory.h"
|
||||
#include "game.h"
|
||||
#include "public_game.h"
|
||||
#include "training.h"
|
||||
#include "freegame.h"
|
||||
#include "duplicate.h"
|
||||
#include "player.h"
|
||||
#include "ai_percent.h"
|
||||
|
||||
|
@ -100,6 +97,8 @@ NewGame::NewGame(QWidget *iParent)
|
|||
this, SLOT(enablePlayers(bool)));
|
||||
QObject::connect(radioButtonTraining, SIGNAL(toggled(bool)),
|
||||
this, SLOT(enablePlayers(bool)));
|
||||
QObject::connect(radioButtonArbitration, SIGNAL(toggled(bool)),
|
||||
this, SLOT(enablePlayers(bool)));
|
||||
|
||||
QObject::connect(buttonAddFav, SIGNAL(clicked()),
|
||||
this, SLOT(addFavoritePlayers()));
|
||||
|
@ -114,8 +113,10 @@ PublicGame * NewGame::createGame(const Dictionary &iDic) const
|
|||
params.setMode(GameParams::kTRAINING);
|
||||
else if (radioButtonFreeGame->isChecked())
|
||||
params.setMode(GameParams::kFREEGAME);
|
||||
else
|
||||
else if (radioButtonDuplicate->isChecked())
|
||||
params.setMode(GameParams::kDUPLICATE);
|
||||
else
|
||||
params.setMode(GameParams::kARBITRATION);
|
||||
|
||||
if (checkBoxJoker->isChecked())
|
||||
params.addVariant(GameParams::kJOKER);
|
||||
|
|
|
@ -45,6 +45,8 @@ const QString PrefsDialog::kINTF_LINK_TRAINING_7P1 = "Interface/LinkTrainingRack
|
|||
const QString PrefsDialog::kINTF_DEFAULT_AI_LEVEL = "Interface/DefaultAiLevel";
|
||||
const QString PrefsDialog::kINTF_TIMER_TOTAL_DURATION = "Interface/TimerTotalDuration";
|
||||
const QString PrefsDialog::kINTF_TIMER_ALERT_DURATION = "Interface/TimerAlertDuration";
|
||||
const QString PrefsDialog::kARBIT_AUTO_MASTER = "Arbitration/AutoAssignMaster";
|
||||
const QString PrefsDialog::kARBIT_LINK_7P1 = "Arbitration/LinkRackWith7P1";
|
||||
const QString PrefsDialog::kDEFAULT_DEF_SITE = "http://fr.wiktionary.org/wiki/%w";
|
||||
|
||||
|
||||
|
@ -65,6 +67,9 @@ PrefsDialog::PrefsDialog(QWidget *iParent)
|
|||
spinBoxTimerAlert->setToolTip(_q("Number of remaining seconds when an alert is triggered.\n"
|
||||
"Use a value of -1 to disable the alert.\n"
|
||||
"Changing this value will reset the timer."));
|
||||
checkBoxArbitAutoMaster->setToolTip(_q("If checked, a Master move will be selected "
|
||||
"by default when searching the results.\n"
|
||||
"It is still possible to change the Master move afterwards."));
|
||||
|
||||
// Auto-completion on the dictionary path
|
||||
QCompleter *completer = new QCompleter(this);
|
||||
|
@ -104,6 +109,12 @@ PrefsDialog::PrefsDialog(QWidget *iParent)
|
|||
|
||||
// Training settings
|
||||
spinBoxTrainSearchLimit->setValue(Settings::Instance().getInt("training.search-limit"));
|
||||
|
||||
// Arbitration settings
|
||||
bool autoAssignMaster = qs.value(kARBIT_AUTO_MASTER, false).toBool();
|
||||
checkBoxArbitAutoMaster->setChecked(autoAssignMaster);
|
||||
bool linkArbit7P1 = qs.value(kARBIT_LINK_7P1, false).toBool();
|
||||
checkBoxArbitLink7P1->setChecked(linkArbit7P1);
|
||||
}
|
||||
catch (GameException &e)
|
||||
{
|
||||
|
@ -193,6 +204,16 @@ void PrefsDialog::updateSettings()
|
|||
// Training settings
|
||||
Settings::Instance().setInt("training.search-limit",
|
||||
spinBoxTrainSearchLimit->value());
|
||||
|
||||
// Arbitration settings
|
||||
qs.setValue(kARBIT_AUTO_MASTER, checkBoxArbitAutoMaster->isChecked());
|
||||
if (qs.value(kARBIT_LINK_7P1, false).toBool() != checkBoxArbitLink7P1->isChecked())
|
||||
{
|
||||
// We need to (dis)connect the arbitration widget with the dictionary
|
||||
// tools window
|
||||
shouldEmitUpdate = true;
|
||||
qs.setValue(kARBIT_LINK_7P1, checkBoxArbitLink7P1->isChecked());
|
||||
}
|
||||
}
|
||||
catch (GameException &e)
|
||||
{
|
||||
|
|
|
@ -49,6 +49,9 @@ public:
|
|||
static const QString kINTF_TIMER_TOTAL_DURATION;
|
||||
static const QString kINTF_TIMER_ALERT_DURATION;
|
||||
|
||||
static const QString kARBIT_AUTO_MASTER;
|
||||
static const QString kARBIT_LINK_7P1;
|
||||
|
||||
static const QString kDEFAULT_DEF_SITE;
|
||||
|
||||
public slots:
|
||||
|
|
314
qt/ui/arbitration_widget.ui
Normal file
314
qt/ui/arbitration_widget.ui
Normal file
|
@ -0,0 +1,314 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ArbitrationWidget</class>
|
||||
<widget class="QWidget" name="ArbitrationWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>839</width>
|
||||
<height>292</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
<string>_("Rack")</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>_("Rack:")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEditRack">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonRandom">
|
||||
<property name="text">
|
||||
<string>_("Random")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonSearch">
|
||||
<property name="text">
|
||||
<string>_("Search")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>_("Possible words")</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>_("Filter results:")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEditFilter"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeView" name="treeViewResults">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="indentation">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>_("Word:")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEditWord">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>_("Ref.:")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEditCoords">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>45</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonCheck">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>_("Check word")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="verticalLayoutWidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>_("Assignments")</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>_("Master move:")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelMasterMove">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonSelectMaster">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>_("Select")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxHideAssigned">
|
||||
<property name="text">
|
||||
<string>_("Hide players with an assigned move")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeView" name="treeViewPlayers">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="indentation">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonAssign">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>_("Assign move")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonNoMove">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>_("Indicate that the selected player(s) did not play")</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>_("No move")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonEndTurn">
|
||||
<property name="toolTip">
|
||||
<string>_("Validate the current turn and start a new one")</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>_("End turn")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>lineEditRack</tabstop>
|
||||
<tabstop>buttonRandom</tabstop>
|
||||
<tabstop>buttonSearch</tabstop>
|
||||
<tabstop>lineEditFilter</tabstop>
|
||||
<tabstop>treeViewResults</tabstop>
|
||||
<tabstop>lineEditWord</tabstop>
|
||||
<tabstop>lineEditCoords</tabstop>
|
||||
<tabstop>buttonCheck</tabstop>
|
||||
<tabstop>buttonSelectMaster</tabstop>
|
||||
<tabstop>checkBoxHideAssigned</tabstop>
|
||||
<tabstop>treeViewPlayers</tabstop>
|
||||
<tabstop>buttonAssign</tabstop>
|
||||
<tabstop>buttonNoMove</tabstop>
|
||||
<tabstop>buttonEndTurn</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -65,6 +65,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioButtonArbitration">
|
||||
<property name="text">
|
||||
<string>_("Arbitration (duplicate)")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>443</width>
|
||||
<height>629</height>
|
||||
<height>693</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -281,7 +281,7 @@
|
|||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxIntfLinkTraining7P1">
|
||||
<property name="toolTip">
|
||||
<string>_("If checked, any change to the player rack in training mode will update the 'Plus 1' tab of the 'Dictionary tools' window")</string>
|
||||
<string>_("If checked, any change to the rack in arbitration mode will update the 'Plus 1' tab of the 'Dictionary tools' window")</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>_("Copy rack to the 'Plus 1' dictionary tool")</string>
|
||||
|
@ -328,6 +328,35 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>_("Arbitration mode")</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxArbitAutoMaster">
|
||||
<property name="toolTip">
|
||||
<string>_("If checked, a Master move will be selected by default when searching the results. It is still possible to change the Master move afterwards.")</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>_("Choose a Master move automatically")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxArbitLink7P1">
|
||||
<property name="toolTip">
|
||||
<string>_("If checked, any change to the player rack in training mode will update the 'Plus 1' tab of the 'Dictionary tools' window")</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>_("Copy rack to the 'Plus 1' dictionary tool")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>540</width>
|
||||
<width>673</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -45,12 +45,6 @@
|
|||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>_("Enter the word to play (case-insensitive). A joker from the rack must be written in parentheses. E.g.: w(o)rd or W(O)RD")</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>_("Enter the word to play (case-insensitive). A joker from the rack must be written in parentheses. E.g.: w(o)rd or W(O)RD")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -166,6 +160,12 @@
|
|||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="indentation">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
|
|
@ -898,6 +898,10 @@ void mainLoop(const Dictionary &iDic)
|
|||
case PublicGame::kDUPLICATE:
|
||||
loopDuplicate(*game);
|
||||
break;
|
||||
case PublicGame::kARBITRATION:
|
||||
// TODO
|
||||
printf("The Arbitration mode is not yet supported\n");
|
||||
break;
|
||||
}
|
||||
//GameFactory::Instance()->releaseGame(*game);
|
||||
delete game;
|
||||
|
|
|
@ -279,6 +279,8 @@ void GameIO::printGameDebug(ostream &out, const PublicGame &iGame)
|
|||
out << "Game: mode=Free game" << endl;
|
||||
else if (iGame.getParams().getMode() == GameParams::kTRAINING)
|
||||
out << "Game: mode=Training" << endl;
|
||||
else if (iGame.getParams().getMode() == GameParams::kARBITRATION)
|
||||
out << "Game: mode=Arbitration" << endl;
|
||||
if (iGame.getParams().hasVariant(GameParams::kJOKER))
|
||||
out << "Game: variant=joker" << endl;
|
||||
if (iGame.getParams().hasVariant(GameParams::kEXPLOSIVE))
|
||||
|
|
Loading…
Reference in a new issue