diff options
Diffstat (limited to 'game.cpp')
-rw-r--r-- | game.cpp | 255 |
1 files changed, 160 insertions, 95 deletions
@@ -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()); @@ -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); } } } @@ -186,15 +185,16 @@ 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_tilesOnRack(QUACKLE_PARAMETERS->rackSize()) { setEmptyBoard(); resetMoveMade(); resetBag(); + m_tilesInBag = m_bag.fullBagTileCount() - (QUACKLE_PARAMETERS->rackSize() * m_players.size()); } 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; @@ -253,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() @@ -308,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) @@ -357,7 +358,13 @@ int GamePosition::validateMove(const Move &move) const } break; + case Move::BlindExchange: + if (!exchangeAllowed()) + ret |= TooLateExchange; + break; + case Move::UnusedTilesBonus: + case Move::UnusedTilesBonusError: case Move::TimePenalty: ret = InvalidAction; break; @@ -379,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; } @@ -419,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; } } @@ -498,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()); } } @@ -520,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()); @@ -617,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; } @@ -634,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(); } } @@ -651,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); } } } @@ -679,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()); @@ -719,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()); @@ -742,7 +769,7 @@ void GamePosition::resetBag() m_bag.prepareFullBag(); } -bool GamePosition::incrementTurn() +bool GamePosition::incrementTurn(const History* history) { if (gameOver() || m_players.empty()) return false; @@ -762,6 +789,51 @@ 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; + else if (m_turnNumber > 1) + { + // this can happen inside of a simming player engine + // which doesn't have a full history list + m_tilesOnRack = currentPlayer().rack().tiles().size(); + } + if (m_moveMade.action == Move::Place && !m_moveMade.isChallengedPhoney()) + m_tilesOnRack -= m_moveMade.usedTiles().size(); + 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); + } + while (m_tilesInBag > 0 && m_tilesOnRack < QUACKLE_PARAMETERS->rackSize() && m_moveMade.action == Move::Place) + { + m_tilesInBag--; + m_tilesOnRack++; + } + } // update our current player's score before possibly // adding endgame bonuses @@ -783,7 +855,7 @@ bool GamePosition::incrementTurn() } } - if ((m_moveMade.action == Move::Place && m_moveMade.effectiveScore() == 0) || m_moveMade.action == Move::Exchange || m_moveMade.action == Move::Pass) + if ((m_moveMade.action == Move::Place && m_moveMade.effectiveScore() == 0) || m_moveMade.action == Move::Exchange || m_moveMade.action == Move::BlindExchange || m_moveMade.action == Move::Pass) ++m_scorelessTurnsInARow; else m_scorelessTurnsInARow = 0; @@ -823,10 +895,9 @@ bool GamePosition::incrementTurn() // 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()) @@ -838,7 +909,8 @@ bool GamePosition::incrementTurn() m_drawingOrder = LetterString(); // reset note to a clean slate - m_explanatoryNote = UVString(); + if (m_moveMade.action != Quackle::Move::UnusedTilesBonusError) + m_explanatoryNote = UVString(); return ret; } @@ -847,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 @@ -864,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; } } @@ -911,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) @@ -919,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; @@ -961,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()) { @@ -982,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; @@ -1000,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; @@ -1017,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()); } } } @@ -1048,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; @@ -1074,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; @@ -1086,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; } |