diff options
author | John Fultz <jfultz@wolfram.com> | 2016-07-07 11:32:02 -0500 |
---|---|---|
committer | John Fultz <jfultz@wolfram.com> | 2016-07-07 11:44:45 -0500 |
commit | e1592dfbc6972dda664d7e1a3c208a9921691149 (patch) | |
tree | 0307551e3d0fac2702c5e7c0fb488bba02f97cb6 | |
parent | 3d46663b8d7221ee84c656715d18cfbdc28a0e85 (diff) |
Fix problems entering games with unknown racks.
If you're entering games with unknown racks, Quackle can
get confused about which player has how many tiles in the
end game. This is because the racks may have been set to
have less than 7 tiles, and some of the tiles "in the bag" may
actually belong on another player's rack. Fixed this by...
* Adding a mechanism which tracks the actual count of
tiles in the bag and on the rack in GamePosition. This
count is independent of what's actually on the rack.
* If the game is about to end because the bag and rack
are empty, check to see if the rack *should* have been
empty. If not, pull tiles from another player's rack.
This code might fail for games of more than two players.
* If the game should be ending because the bag and
rack are empty, but the rack isn't actually empty, then
dump the files to another player's rack. Once again,
this code doesn't take into account >2 players.
-rw-r--r-- | bag.cpp | 9 | ||||
-rw-r--r-- | bag.h | 3 | ||||
-rw-r--r-- | game.cpp | 48 | ||||
-rw-r--r-- | game.h | 5 | ||||
-rw-r--r-- | preendgame.cpp | 2 | ||||
-rw-r--r-- | rack.cpp | 9 | ||||
-rw-r--r-- | rack.h | 4 |
7 files changed, 73 insertions, 7 deletions
@@ -55,6 +55,15 @@ void Bag::prepareFullBag() m_tiles.push_back(letter); } +int Bag::fullBagTileCount() +{ + int tileCount = 0; + // we start at 0 because we want to include blanks etcetera + for (Letter letter = 0; letter <= QUACKLE_ALPHABET_PARAMETERS->lastLetter(); ++letter) + tileCount += QUACKLE_ALPHABET_PARAMETERS->count(letter); + return tileCount; +} + void Bag::toss(const LetterString &letters) { const LetterString::const_iterator end(letters.end()); @@ -72,6 +72,9 @@ public: // use this to start out your bag for use void prepareFullBag(); + // Assuming a full bag, how many tiles would that be? + int fullBagTileCount(); + // whether there are no tiles left in the bag bool empty() const; @@ -70,7 +70,7 @@ void Game::setPlayers(const PlayerList &list) void Game::addPosition() { addClonePosition(); - m_positions.lastPosition().incrementTurn(); + m_positions.lastPosition().incrementTurn(&history()); m_positions.setCurrentLocation(m_positions.lastLocation()); @@ -186,7 +186,7 @@ void Game::commitMove(const Move &move) /////////// GamePosition::GamePosition(const PlayerList &players) - : m_players(players), m_currentPlayer(m_players.end()), m_playerOnTurn(m_players.end()), m_turnNumber(0), m_nestedness(0), m_scorelessTurnsInARow(0), m_gameOver(false) + : m_players(players), m_currentPlayer(m_players.end()), m_playerOnTurn(m_players.end()), m_turnNumber(0), m_nestedness(0), m_scorelessTurnsInARow(0), m_gameOver(false), m_tilesInBag(m_bag.fullBagTileCount() - (QUACKLE_PARAMETERS->rackSize() * m_players.size())), m_tilesOnRack(QUACKLE_PARAMETERS->rackSize()) { setEmptyBoard(); resetMoveMade(); @@ -194,7 +194,7 @@ GamePosition::GamePosition(const PlayerList &players) } GamePosition::GamePosition(const GamePosition &position) - : m_players(position.m_players), m_moves(position.m_moves), m_moveMade(position.m_moveMade), m_committedMove(position.m_committedMove), m_turnNumber(position.m_turnNumber), m_nestedness(position.m_nestedness), m_scorelessTurnsInARow(position.m_scorelessTurnsInARow), m_gameOver(position.m_gameOver), m_board(position.m_board), m_bag(position.m_bag), m_drawingOrder(position.m_drawingOrder), m_explanatoryNote(position.m_explanatoryNote) + : m_players(position.m_players), m_moves(position.m_moves), m_moveMade(position.m_moveMade), m_committedMove(position.m_committedMove), m_turnNumber(position.m_turnNumber), m_nestedness(position.m_nestedness), m_scorelessTurnsInARow(position.m_scorelessTurnsInARow), m_gameOver(position.m_gameOver), m_tilesInBag(position.m_tilesInBag), m_tilesOnRack(position.m_tilesOnRack), m_board(position.m_board), m_bag(position.m_bag), m_drawingOrder(position.m_drawingOrder), m_explanatoryNote(position.m_explanatoryNote) { // reset iterator if (position.turnNumber() == 0) @@ -219,6 +219,8 @@ const GamePosition &GamePosition::operator=(const GamePosition &position) m_nestedness = position.m_nestedness; m_scorelessTurnsInARow = position.m_scorelessTurnsInARow; m_gameOver = position.m_gameOver; + m_tilesInBag = position.m_tilesInBag; + m_tilesOnRack = position.m_tilesOnRack; m_board = position.m_board; m_bag = position.m_bag; m_drawingOrder = position.m_drawingOrder; @@ -747,7 +749,7 @@ void GamePosition::resetBag() m_bag.prepareFullBag(); } -bool GamePosition::incrementTurn() +bool GamePosition::incrementTurn(const History* history) { if (gameOver() || m_players.empty()) return false; @@ -767,6 +769,44 @@ bool GamePosition::incrementTurn() // now moveTiles is the tiles that are in play but not on rack removeLetters(moveTiles.tiles()); + if (history) + { + PlayerList::iterator nextCurrentPlayer(m_currentPlayer); + nextCurrentPlayer++; + if (nextCurrentPlayer == m_players.end()) + nextCurrentPlayer = m_players.begin(); + const Quackle::PositionList positions(history->positionsFacedBy((*nextCurrentPlayer).id())); + if (positions.size() > 0) + m_tilesOnRack = positions.back().m_tilesOnRack; + m_tilesOnRack -= m_moveMade.usedTiles().size(); + while (m_tilesInBag > 0 && m_tilesOnRack < QUACKLE_PARAMETERS->rackSize()) + { + m_tilesInBag--; + m_tilesOnRack++; + } + if (m_tilesInBag == 0) + { + // We can get off on our counting with unknown racks. + // Shift tiles around if that happens. + Rack otherPlayerRack = nextCurrentPlayer->rack(); + if (m_tilesOnRack == 0 && !remainingRack.empty()) + { + otherPlayerRack.load(remainingRack.tiles()); + remainingRack = remainingRack - remainingRack; + } + else if (m_tilesOnRack != 0 && remainingRack.empty()) + { + int tilesToMove = m_tilesOnRack; + while (otherPlayerRack.tiles().size() > 0 && tilesToMove > 0) + { + LetterString oneAtATime = otherPlayerRack.tiles().substr(0, 1); + otherPlayerRack.unload(oneAtATime); + remainingRack.load(oneAtATime); + } + } + nextCurrentPlayer->setRack(otherPlayerRack); + } + } // update our current player's score before possibly // adding endgame bonuses @@ -34,6 +34,7 @@ namespace Quackle { class ComputerPlayer; +class History; class HistoryLocation { @@ -380,7 +381,7 @@ public: // at start of game.) // If applicable, this player's score is also incremented by score of move // made. - bool incrementTurn(); + bool incrementTurn(const History* history = NULL); // Turn numbers in games start from 1. // A turn number of zero indicates a position that is pregame. @@ -426,6 +427,8 @@ protected: unsigned int m_nestedness; int m_scorelessTurnsInARow; bool m_gameOver; + int m_tilesInBag; + int m_tilesOnRack; Quackle::Board m_board; diff --git a/preendgame.cpp b/preendgame.cpp index 227598f..fe9a356 100644 --- a/preendgame.cpp +++ b/preendgame.cpp @@ -167,7 +167,7 @@ MoveList Preendgame::moves(int nmoves) tempPosition.setOppRack((*it).rack); tempPosition.setMoveMade(*moveIt); - tempPosition.incrementTurn(); + tempPosition.incrementTurn(NULL); tempPosition.makeMove(*moveIt); //tempPosition.incrementNestedness(); @@ -76,6 +76,15 @@ bool Rack::unload(const LetterString &used) return ret; } +void Rack::load(const LetterString &tiles) +{ + for (LetterString::const_iterator it = tiles.begin(); it != tiles.end(); ++it) + { + if (it != QUACKLE_NULL_MARK) + m_tiles += *it; + } +} + bool Rack::contains(const LetterString &used) const { return Rack(*this).unload(used); @@ -57,6 +57,8 @@ public: // in this rack and unloaded bool unload(const LetterString &used); + void load(const LetterString &tiles); + // same as above but nonmutating bool contains(const LetterString &used) const; @@ -99,7 +101,7 @@ inline bool Rack::empty() const } const Quackle::Rack operator-(const Quackle::Rack &rack, const Quackle::Move &move); -const Quackle::Rack operator-(const Quackle::Rack &rack1, const Quackle::Rack &rack); +const Quackle::Rack operator-(const Quackle::Rack &rack1, const Quackle::Rack &rack2); UVOStream &operator<<(UVOStream &o, const Quackle::Rack &rack); |