summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Fultz <jfultz@wolfram.com>2016-08-02 04:08:43 -0500
committerJohn Fultz <jfultz@wolfram.com>2016-08-02 06:37:15 -0500
commitdbcb13d3c87133117bc54023c162ad0a202efa1d (patch)
tree955715420b4584b73baa676c1b4b5fe4febb3c48
parenta3aa3602d173939a2b616bb78a5a739eb9761d9b (diff)
Fixes to allow bad endgames to be loaded from GCGs.
Not perfect, but it now does a decent job of not corrupting GCGs when it loads one where players have messed up tile counts/drawing in the end-game. It also tries to annotate these for the player and in any GCG file that's round-tripped. Also, a lot of C++11-izing of ranged iterators.
-rw-r--r--game.cpp192
-rw-r--r--game.h19
-rw-r--r--move.cpp7
-rw-r--r--move.h2
-rw-r--r--quacker/graphicalboard.cpp2
-rw-r--r--quacker/graphicalreporter.cpp1
-rw-r--r--quackleio/gcgio.cpp11
-rw-r--r--quackleio/util.cpp7
-rw-r--r--reporter.cpp1
9 files changed, 142 insertions, 100 deletions
diff --git a/game.cpp b/game.cpp
index 8e63ed5..7e0f438 100644
--- a/game.cpp
+++ b/game.cpp
@@ -106,15 +106,14 @@ void Game::associateComputerPlayer(int playerId, ComputerPlayer *computerPlayer)
void Game::associateKnownComputerPlayers()
{
- const PlayerList::const_iterator end(m_positions.players().end());
- for (PlayerList::const_iterator it = m_positions.players().begin(); it != end; ++it)
+ for (const auto &it : m_positions.players())
{
- if ((*it).type() == Player::ComputerPlayerType)
+ if (it.type() == Player::ComputerPlayerType)
{
- ComputerPlayer *computerPlayer = (*it).computerPlayer();
+ ComputerPlayer *computerPlayer = it.computerPlayer();
if (computerPlayer)
- associateComputerPlayer((*it).id(), computerPlayer);
+ associateComputerPlayer(it.id(), computerPlayer);
}
}
}
@@ -256,9 +255,8 @@ void GamePosition::kibitz(int nmoves)
m_moves = generator.kibitzList();
- const MoveList::iterator end(m_moves.end());
- for (MoveList::iterator it = m_moves.begin(); it != end; ++it)
- ensureMovePrettiness(*it);
+ for (auto &it : m_moves)
+ ensureMovePrettiness(it);
}
const Move &GamePosition::staticBestMove()
@@ -311,9 +309,9 @@ void GamePosition::addMove(const Move &move)
void GamePosition::makeSureMoveListContainsMoves(const MoveList &moves)
{
- for (MoveList::const_iterator it = moves.begin(); it != moves.end(); ++it)
- if (!m_moves.contains(*it))
- addMove(*it);
+ for (const auto &it : moves)
+ if (!m_moves.contains(it))
+ addMove(it);
}
void GamePosition::kibitzAs(ComputerPlayer *computerPlayer, int nmoves)
@@ -366,6 +364,7 @@ int GamePosition::validateMove(const Move &move) const
break;
case Move::UnusedTilesBonus:
+ case Move::UnusedTilesBonusError:
case Move::TimePenalty:
ret = InvalidAction;
break;
@@ -387,9 +386,9 @@ bool GamePosition::formsAcceptableWords(const Move &move) const
{
vector<Move> words = m_board.allWordsFormedBy(move);
- for (vector<Move>::const_iterator it = words.begin(); it != words.end(); ++it)
+ for (const auto &it : words)
{
- if (!isAcceptableWord((*it).wordTiles()))
+ if (!isAcceptableWord(it.wordTiles()))
return false;
}
@@ -427,15 +426,15 @@ int GamePosition::handleOverdraw(const LetterString &letters, LetterString *thro
double minimumLeaveValue = 99999;
- for (ProbableRackList::const_iterator it = racks.begin(); it != racks.end(); ++it)
+ for (const auto &it : racks)
{
- double value = leaveValue((*it).rack.tiles());
+ double value = leaveValue(it.rack.tiles());
if (value < minimumLeaveValue)
{
Rack rack(letters);
- rack.unload((*it).rack.tiles());
- *throwback = (Rack(letters) - (*it).rack).tiles();
+ rack.unload(it.rack.tiles());
+ *throwback = (Rack(letters) - it.rack).tiles();
minimumLeaveValue = value;
}
}
@@ -506,12 +505,11 @@ Bag GamePosition::unseenBagFromPlayerPerspective(const Player &player) const
// other way:
Bag ret(m_bag);
- const PlayerList::const_iterator end(m_players.end());
- for (PlayerList::const_iterator it = m_players.begin(); it != end; ++it)
+ for (const auto &it : m_players)
{
- if (!((*it) == player))
+ if (!(it == player))
{
- ret.toss((*it).rack());
+ ret.toss(it.rack());
}
}
@@ -528,11 +526,10 @@ void GamePosition::ensureProperBag() const
Bag racks;
racks.clear();
- const PlayerList::const_iterator end(m_players.end());
- for (PlayerList::const_iterator it = m_players.begin(); it != end; ++it)
+ for (const auto &it : m_players)
{
- allTiles.toss((*it).rack());
- racks.toss((*it).rack());
+ allTiles.toss(it.rack());
+ racks.toss(it.rack());
}
allTiles.toss(m_bag.shuffledTiles());
@@ -625,13 +622,12 @@ UVString GamePosition::nestednessIndentation() const
void GamePosition::setOppRack(const Rack &rack, bool adjustBag)
{
int oppID;
- const PlayerList::iterator end(m_players.end());
- for (PlayerList::iterator it = m_players.begin(); it != end; ++it)
- if ((*it).id() != currentPlayer().id())
+ for (auto &it : m_players)
+ if (it.id() != currentPlayer().id())
{
- m_bag.toss((*it).rack());
- (*it).setRack(Rack(""));
- oppID = (*it).id();
+ m_bag.toss(it.rack());
+ it.setRack(Rack(""));
+ oppID = it.id();
setPlayerRack(oppID, rack, adjustBag);
return;
}
@@ -642,15 +638,14 @@ Rack GamePosition::oppRack()
{
UVcout << "currentPlayer(): " << currentPlayer() << endl;
- const PlayerList::iterator end(m_players.end());
- for (PlayerList::iterator it = m_players.begin(); it != end; ++it)
- UVcout << "rack " << *it << " " << (*it).rack() << endl;
+ for (const auto &it : m_players)
+ UVcout << "rack " << it << " " << it.rack() << endl;
- for (PlayerList::iterator it = m_players.begin(); it != end; ++it)
+ for (const auto &it : m_players)
{
- if ((*it).id() != currentPlayer().id())
+ if (it.id() != currentPlayer().id())
{
- return (*it).rack();
+ return it.rack();
}
}
@@ -659,20 +654,19 @@ Rack GamePosition::oppRack()
void GamePosition::setPlayerRack(int playerID, const Rack &rack, bool adjustBag)
{
- const PlayerList::iterator end(m_players.end());
- for (PlayerList::iterator it = m_players.begin(); it != end; ++it)
+ for (auto &it : m_players)
{
- if ((*it).id() == playerID)
+ if (it.id() == playerID)
{
// restore bag
if (adjustBag)
{
// please be correct code
- m_bag.toss((*it).rack() - rack);
- removeLetters((rack - (*it).rack()).tiles());
+ m_bag.toss(it.rack() - rack);
+ removeLetters((rack - it.rack()).tiles());
}
- (*it).setRack(rack);
+ it.setRack(rack);
}
}
}
@@ -687,9 +681,8 @@ bool GamePosition::canSetPlayerRackWithoutBagExpansion(int playerID, const Rack
(void) playerID;
Bag someTiles(m_bag);
- const PlayerList::const_iterator end(m_players.end());
- for (PlayerList::const_iterator it = m_players.begin(); it != end; ++it)
- someTiles.toss((*it).rack());
+ for (const auto &it : m_players)
+ someTiles.toss(it.rack());
// Now we have a bag with all tiles not on the board
return someTiles.removeLetters(rack.tiles());
@@ -727,6 +720,32 @@ bool GamePosition::setPlayerOnTurn(int playerID)
return false;
}
+void GamePosition::setTileBonus(const UVString &player, const LetterString &allegedTiles, int allegedTileBonus)
+{
+ bool found;
+ Quackle::Move tileBonusMove = Quackle::Move::createUnusedTilesBonus(allegedTiles, allegedTileBonus);
+ PlayerList::const_iterator currentPlayer = playerWithAbbreviatedName(player, found);
+ if (currentPlayer == m_currentPlayer && m_gameOver)
+ {
+ if (allegedTileBonus == m_committedMove.effectiveScore())
+ return; // this has already been computed
+
+ // We computed this, but it differs from the actual tile bonus we're requested to add
+ tileBonusMove.action = Quackle::Move::UnusedTilesBonusError;
+ }
+ else if (allegedTileBonus > 0)
+ {
+ ++m_turnNumber;
+ tileBonusMove.action = Quackle::Move::UnusedTilesBonusError; // we didn't empty a rack, but somebody's claiming we did
+ }
+
+ setCurrentPlayer(currentPlayer->id());
+ setMoveMade(tileBonusMove);
+ setCommittedMove(tileBonusMove);
+ m_gameOver = true;
+ m_explanatoryNote = UVString("Quackle says: Bag wasn't empty or tiles drawn out of order");
+}
+
void GamePosition::prepareForCommit()
{
setCommittedMove(moveMade());
@@ -876,10 +895,9 @@ bool GamePosition::incrementTurn(const History* history)
// refill rack of any player whose rack is empty (again for
// beginning of game)
- const PlayerList::iterator end(m_players.end());
- for (PlayerList::iterator it = m_players.begin(); it != end; ++it)
- if ((*it).rack().empty())
- replenishAndSetRack((*it).rack(), *it);
+ for (auto &it : m_players)
+ if (it.rack().empty())
+ replenishAndSetRack(it.rack(), it);
// freeze current player as last person who played out
if (gameOver())
@@ -891,7 +909,8 @@ bool GamePosition::incrementTurn(const History* history)
m_drawingOrder = LetterString();
// reset note to a clean slate
- m_explanatoryNote = UVString();
+ if (m_moveMade.action != Quackle::Move::UnusedTilesBonusError)
+ m_explanatoryNote = UVString();
return ret;
}
@@ -900,14 +919,13 @@ bool GamePosition::removeLetters(const LetterString &letters)
{
bool ret = true;
- const LetterString::const_iterator end(letters.end());
- for (LetterString::const_iterator it = letters.begin(); it != end; ++it)
+ for (const auto &it : letters)
{
#ifdef VERBOSE_DEBUG_BAG
UVcout << "removeLetters processing " << QUACKLE_ALPHABET_PARAMETERS->userVisible(*it) << endl;
#endif
- const bool removedFromBag = m_bag.removeLetter(*it);
+ const bool removedFromBag = m_bag.removeLetter(it);
if (removedFromBag)
{
#ifdef VERBOSE_DEBUG_BAG
@@ -917,20 +935,19 @@ bool GamePosition::removeLetters(const LetterString &letters)
else
{
bool removedFromPlayer = false;
- const PlayerList::iterator playersEnd(m_players.end());
- for (PlayerList::iterator playerIt = m_players.begin(); playerIt != playersEnd; ++playerIt)
+ for (auto &playerIt : m_players)
{
- if (*playerIt == currentPlayer())
+ if (playerIt == currentPlayer())
continue;
LetterString letterString;
- letterString += (*it);
+ letterString += it;
- Rack newRack((*playerIt).rack());
+ Rack newRack(playerIt.rack());
removedFromPlayer = newRack.unload(letterString);
if (removedFromPlayer)
{
- replenishAndSetRack(newRack, *playerIt);
+ replenishAndSetRack(newRack, playerIt);
break;
}
}
@@ -964,7 +981,7 @@ PlayerList::const_iterator GamePosition::nextPlayer() const
return ret;
}
-PlayerList::const_iterator GamePosition::playerWithId(int id, bool &found) const
+PlayerList::const_iterator GamePosition::playerWithAbbreviatedName(const UVString &abbreviatedName, bool &found) const
{
PlayerList::const_iterator it = m_currentPlayer;
for (++it; ; ++it)
@@ -972,7 +989,7 @@ PlayerList::const_iterator GamePosition::playerWithId(int id, bool &found) const
if (it == m_players.end())
it = m_players.begin();
- if ((*it).id() == id)
+ if ((*it).abbreviatedName() == abbreviatedName)
{
found = true;
return it;
@@ -1014,10 +1031,9 @@ PlayerList::const_iterator GamePosition::nextPlayerOfType(Player::PlayerType typ
PlayerList GamePosition::endgameAdjustedScores() const
{
PlayerList ret;
- const PlayerList::const_iterator end(m_players.end());
- for (PlayerList::const_iterator it = m_players.begin(); it != end; ++it)
+ for (const auto &it : m_players)
{
- Player adjustedPlayer(*it);
+ Player adjustedPlayer(it);
if (gameOver() && adjustedPlayer == currentPlayer())
{
@@ -1035,13 +1051,13 @@ PlayerList GamePosition::leadingPlayers() const
PlayerList ret;
PlayerList players(endgameAdjustedScores());
- for (PlayerList::const_iterator it = players.begin(); it != players.end(); ++it)
+ for (const auto &it : players)
{
- if (!ret.empty() && (*it).score() > ret.back().score())
+ if (!ret.empty() && it.score() > ret.back().score())
ret.clear();
- if (ret.empty() || (*it).score() >= ret.back().score())
- ret.push_back(*it);
+ if (ret.empty() || it.score() >= ret.back().score())
+ ret.push_back(it);
}
return ret;
@@ -1053,16 +1069,16 @@ int GamePosition::spread(int playerID) const
int currentPlayerScore = 0;
PlayerList players(endgameAdjustedScores());
- for (PlayerList::const_iterator it = players.begin(); it != players.end(); ++it)
+ for (const auto &it : players)
{
- if ((*it).id() == playerID)
+ if (it.id() == playerID)
{
- currentPlayerScore = (*it).score();
+ currentPlayerScore = it.score();
continue;
}
- if ((*it).score() > nextBest)
- nextBest = (*it).score();
+ if (it.score() > nextBest)
+ nextBest = it.score();
}
return currentPlayerScore - nextBest;
@@ -1070,17 +1086,16 @@ int GamePosition::spread(int playerID) const
void GamePosition::adjustScoresToFinishPassedOutGame()
{
- const PlayerList::iterator end(m_players.end());
- for (PlayerList::iterator it = m_players.begin(); it != end; ++it)
+ for (auto &it : m_players)
{
- if ((*it) == currentPlayer())
+ if (it == currentPlayer())
{
- m_moveMade = Move::createUnusedTilesBonus((*it).rack().tiles(), -(*it).rack().score());
+ m_moveMade = Move::createUnusedTilesBonus(it.rack().tiles(), -it.rack().score());
m_committedMove = m_moveMade;
}
else
{
- (*it).addToScore(-(*it).rack().score());
+ it.addToScore(-it.rack().score());
}
}
}
@@ -1101,14 +1116,13 @@ int GamePosition::deadwood(LetterString *tiles) const
int addand = 0;
tiles->clear();
- const PlayerList::const_iterator end(m_players.end());
- for (PlayerList::const_iterator it = m_players.begin(); it != end; ++it)
+ for (const auto &it : m_players)
{
- if ((*it) == currentPlayer())
+ if (it == currentPlayer())
continue;
- addand += (*it).rack().score();
- *tiles += (*it).rack().tiles();
+ addand += it.rack().score();
+ *tiles += it.rack().tiles();
}
return addand * 2;
@@ -1127,9 +1141,8 @@ UVOStream& operator<<(UVOStream& o, const Quackle::GamePosition &position)
o << "Move made is " << position.moveMade() << endl;
o << "All Players: " << endl;
PlayerList players(position.players());
- const PlayerList::const_iterator end(players.end());
- for (PlayerList::const_iterator it = players.begin(); it != end; ++it)
- o << *it << endl;
+ for (const auto &it : players)
+ o << it << endl;
o << position.bag() << endl;
if (position.gameOver())
o << "Game over." << endl;
@@ -1139,9 +1152,8 @@ UVOStream& operator<<(UVOStream& o, const Quackle::GamePosition &position)
UVOStream& operator<<(UVOStream& o, const Quackle::PositionList &positions)
{
- const Quackle::PositionList::const_iterator end(positions.end());
- for (Quackle::PositionList::const_iterator it = positions.begin(); it != end; ++it)
- o << *it << endl;
+ for (const auto &it : positions)
+ o << it << endl;
return o;
}
diff --git a/game.h b/game.h
index 7ef60fd..c022848 100644
--- a/game.h
+++ b/game.h
@@ -270,9 +270,9 @@ public:
// and found is set to false.
PlayerList::const_iterator nextPlayerOfType(Player::PlayerType type, bool &found) const;
- // Returns the player with id specified, or the current player
+ // Returns the player with the abbreviated name specified, or the current player
// if not found.
- PlayerList::const_iterator playerWithId(int id, bool &found) const;
+ PlayerList::const_iterator playerWithAbbreviatedName(const UVString &abbreviatedName, bool &found) const;
const PlayerList &players() const;
@@ -296,7 +296,7 @@ public:
bool gameOver() const;
// the move made will
- // be set to the proper UnusedTileBonus move.
+ // be set to the proper UnusedTilesBonus move.
// The score of the bonus is *not* added to score of current player,
// therefor this method is quite misnamed.
void adjustScoresToFinishGame();
@@ -356,6 +356,8 @@ public:
void setCommittedMove(const Move &move);
const Move &committedMove() const;
+ void setTileBonus(const UVString &player, const LetterString &allegedTiles, int allegedTileBonus);
+
// saves the current candidate as the committedMove.
void prepareForCommit();
@@ -536,6 +538,17 @@ inline bool GamePosition::gameOver() const
inline void GamePosition::setMoveMade(const Move &move)
{
m_moveMade = move;
+ if (m_gameOver && move.action != Quackle::Move::UnusedTilesBonus && move.action != Quackle::Move::UnusedTilesBonusError)
+ {
+ m_gameOver = false; // apparently the game isn't over...somebody's force-feeding us bad plays
+ --m_turnNumber;
+ m_moveMade.action = Quackle::Move::PlaceError;
+ m_explanatoryNote = "Quackle says: Tiles were drawn out of order, leading to extra turns";
+ if (++m_currentPlayer == m_players.end())
+ {
+ m_currentPlayer = m_players.begin();
+ }
+ }
}
inline const Move &GamePosition::moveMade() const
diff --git a/move.cpp b/move.cpp
index 60e14e4..2d0a9c4 100644
--- a/move.cpp
+++ b/move.cpp
@@ -40,10 +40,12 @@ bool operator==(const Move &move1, const Move &move2)
switch (move1.action)
{
case Quackle::Move::Place:
+ case Quackle::Move::PlaceError:
ret = (move1.horizontal == move2.horizontal && move1.startrow == move2.startrow && move1.startcol == move2.startcol && move1.tiles() == move2.tiles() && move1.isChallengedPhoney() == move2.isChallengedPhoney());
break;
case Quackle::Move::UnusedTilesBonus:
+ case Quackle::Move::UnusedTilesBonusError:
case Quackle::Move::Exchange:
ret = (Quackle::String::alphabetize(move1.tiles()) == Quackle::String::alphabetize(move2.tiles()));
break;
@@ -142,6 +144,7 @@ UVString Move::xml() const
includeScore = true;
break;
+ case UnusedTilesBonusError:
case UnusedTilesBonus:
actionString = MARK_UV("unusedtilesbonus");
includeTiles = true;
@@ -195,9 +198,9 @@ UVString Move::toString() const
ss << "nonmove";
else if (action == Quackle::Move::TimePenalty)
ss << "timepenalty " << score;
- else if (action == Quackle::Move::UnusedTilesBonus)
+ else if (action == Quackle::Move::UnusedTilesBonus || action == Quackle::Move::UnusedTilesBonusError)
ss << "(" << QUACKLE_ALPHABET_PARAMETERS->userVisible(m_tiles) << ")";
- else if (action == Quackle::Move::Place)
+ else if (action == Quackle::Move::Place || action == Quackle::Move::PlaceError)
{
ss << positionString();
ss << " " << QUACKLE_ALPHABET_PARAMETERS->userVisible(m_tiles);
diff --git a/move.h b/move.h
index e628b52..3e0d396 100644
--- a/move.h
+++ b/move.h
@@ -43,7 +43,7 @@ namespace Quackle
class Move
{
public:
- enum Action { Place = 0, Exchange, BlindExchange, Pass, UnusedTilesBonus, TimePenalty, Nonmove };
+ enum Action { Place = 0, PlaceError, Exchange, BlindExchange, Pass, UnusedTilesBonus, UnusedTilesBonusError, TimePenalty, Nonmove };
// creates a pass move with 0 equity;
// tiles is "", score and equity are zero
diff --git a/quacker/graphicalboard.cpp b/quacker/graphicalboard.cpp
index 12244a1..cf82f3a 100644
--- a/quacker/graphicalboard.cpp
+++ b/quacker/graphicalboard.cpp
@@ -325,7 +325,7 @@ QPoint GraphicalBoardFrame::coordinatesOfMark(const QSize &loc)
void GraphicalBoardFrame::drawMove(const Quackle::Move &move)
{
- if (move.action == Quackle::Move::Place)
+ if (move.action == Quackle::Move::Place || move.action == Quackle::Move::PlaceError)
{
if (move.tiles().empty())
return;
diff --git a/quacker/graphicalreporter.cpp b/quacker/graphicalreporter.cpp
index a7ca9f5..d234ff6 100644
--- a/quacker/graphicalreporter.cpp
+++ b/quacker/graphicalreporter.cpp
@@ -160,6 +160,7 @@ void GraphicalReporter::reportPosition(const Quackle::GamePosition &position, Qu
switch ((*it).action)
{
case Quackle::Move::Place:
+ case Quackle::Move::PlaceError:
{
if (m_generateImages)
{
diff --git a/quackleio/gcgio.cpp b/quackleio/gcgio.cpp
index 483678b..b60d57c 100644
--- a/quackleio/gcgio.cpp
+++ b/quackleio/gcgio.cpp
@@ -143,6 +143,7 @@ Quackle::Game *GCGIO::read(QTextStream &stream, int flags)
gameStarted = true;
}
+ UVString currentPlayer = Util::qstringToString(strings.front().mid(1, strings.front().size() - 2));
strings.pop_front();
if (strings.isEmpty())
@@ -158,10 +159,11 @@ Quackle::Game *GCGIO::read(QTextStream &stream, int flags)
if (rackString.startsWith("(") && rackString.endsWith(")"))
{
// end the game
- if (ret->hasPositions())
+ if (ret->hasPositions() && !ret->currentPosition().gameOver())
ret->commitCandidate(canMaintainCrosses);
else
ret->addPosition();
+ ret->currentPosition().setTileBonus(currentPlayer, Util::encode(rackString.mid(1, rackString.size() - 2)), strings.front().toInt());
continue;
}
@@ -359,11 +361,14 @@ void GCGIO::write(const Quackle::Game &game, QTextStream &stream)
outputScoreAddition = 0;
}
- stream << ">" << Util::uvStringToQString((*it).currentPlayer().abbreviatedName()) << ": " << Util::letterStringToQString((*it).currentPlayer().rack().alphaTiles()) << " " << Util::uvStringToQString(move.toString()) << " +" << outputScore << " " << outputScore + (*it).currentPlayer().score() << endl;
+ QString rackString = Util::letterStringToQString((*it).currentPlayer().rack().alphaTiles());
+ if (move.action == Quackle::Move::UnusedTilesBonusError)
+ rackString = QString();
+ stream << ">" << Util::uvStringToQString((*it).currentPlayer().abbreviatedName()) << ": " << rackString << " " << Util::uvStringToQString(move.toString()) << " +" << outputScore << " " << outputScore + (*it).currentPlayer().score() << endl;
if (move.isChallengedPhoney())
{
- stream << ">" << Util::uvStringToQString((*it).currentPlayer().abbreviatedName()) << ": " << Util::letterStringToQString((*it).currentPlayer().rack().alphaTiles()) << " -- -" << outputScore << " " << move.effectiveScore() + (*it).currentPlayer().score() << endl;
+ stream << ">" << Util::uvStringToQString((*it).currentPlayer().abbreviatedName()) << ": " << rackString << " -- -" << outputScore << " " << move.effectiveScore() + (*it).currentPlayer().score() << endl;
}
if (outputScoreAddition != 0)
diff --git a/quackleio/util.cpp b/quackleio/util.cpp
index 901c65e..774dcad 100644
--- a/quackleio/util.cpp
+++ b/quackleio/util.cpp
@@ -59,8 +59,11 @@ QString Util::moveToDetailedString(const Quackle::Move &move)
ret = QObject::tr("Exch. %1").arg(move.tiles().length());
break;
+ case Quackle::Move::UnusedTilesBonusError:
case Quackle::Move::UnusedTilesBonus:
ret = QObject::tr("2*(%1)").arg(letterStringToQString(Util::alphagram(move.usedTiles())));
+ if (move.action == Quackle::Move::UnusedTilesBonusError)
+ ret += " [Endgame Error]";
break;
case Quackle::Move::TimePenalty:
@@ -72,6 +75,7 @@ QString Util::moveToDetailedString(const Quackle::Move &move)
break;
case Quackle::Move::Place:
+ case Quackle::Move::PlaceError:
ret = uvStringToQString(move.positionString()) + " ";
ret += prettyTiles;
@@ -81,6 +85,9 @@ QString Util::moveToDetailedString(const Quackle::Move &move)
if (move.isChallengedPhoney())
ret = QObject::tr("%1 [Challenged Off]").arg(ret);
+ if (move.action == Quackle::Move::PlaceError)
+ ret += " [Endgame Misdraw]";
+
break;
}
diff --git a/reporter.cpp b/reporter.cpp
index 7054763..602dd8b 100644
--- a/reporter.cpp
+++ b/reporter.cpp
@@ -164,6 +164,7 @@ void Reporter::reportPosition(const GamePosition &position, ComputerPlayer *comp
s << MARK_UV("pas");
break;
case Move::UnusedTilesBonus:
+ case Move::UnusedTilesBonusError:
case Move::TimePenalty:
case Move::Nonmove:
break;