void Dashboard::historyChanged(const Quackle::History &history) { const Quackle::PlayerList players(history.currentPosition().endgameAdjustedScores()); const int numberOfPlayers = players.size(); const bool gameOver = history.currentPosition().gameOver(); while (m_briefs.size() > numberOfPlayers) { delete m_briefs.back(); m_briefs.pop_back(); } while (m_briefs.size() < numberOfPlayers) { m_briefs.push_back(new PlayerBrief); m_hlayout->addWidget(m_briefs.back()); m_briefs.back()->show(); } Quackle::PlayerList winners; if (gameOver) winners = history.currentPosition().leadingPlayers(); int playerCountFromZero = 0; QList<PlayerBrief *>::iterator briefIt = m_briefs.begin(); for (Quackle::PlayerList::const_iterator playerIt = players.begin(); playerIt != players.end(); ++playerIt, ++briefIt, ++playerCountFromZero) { const bool isCurrentPlayer = !gameOver && *playerIt == history.currentPosition().playerOnTurn(); (*briefIt)->setCurrentPlayer(isCurrentPlayer); (*briefIt)->setWinnerStatus(Nonwinner); if (gameOver) { Quackle::PlayerList::const_iterator winnersIt; for (winnersIt = winners.begin(); winnersIt != winners.end(); ++winnersIt) { if (*playerIt == *winnersIt) { (*briefIt)->setWinnerStatus(winners.size() > 1? Cowinner : Winner); break; } } } (*briefIt)->setPlayer(*playerIt); } }
void Reporter::reportPosition(const GamePosition &position, ComputerPlayer *computerPlayer, UVString *report) { UVOStringStream s; UVOStringStream titleStream; if (!position.gameOver()) titleStream << position.currentPlayer().name() << MARK_UV(": Turn ") << position.turnNumber() << MARK_UV('\n'); const Quackle::PlayerList players(position.endgameAdjustedScores()); for (PlayerList::const_iterator it = players.begin(); it != players.end(); ++it) { s.width(3); s << right << ((*it) == position.currentPlayer()? MARK_UV("->") : MARK_UV(" ")); s << MARK_UV(' '); s.width(24); s << left << (*it).name() << MARK_UV(' '); s.width(9); s << (*it).rack().toString() << MARK_UV(' '); s.width(4); s << (*it).score(); s << MARK_UV('\n'); } if (computerPlayer && !position.gameOver()) { computerPlayer->setPosition(position); if (position.committedMove().isAMove()) computerPlayer->considerMove(position.committedMove()); const unsigned int movesToShow = 10; MoveList moves = computerPlayer->moves(movesToShow); int ourMoveIndex = 0; int i = 1; for (Quackle::MoveList::const_iterator it = moves.begin(); it != moves.end(); ++it, ++i) { if ((*it) == position.committedMove()) { ourMoveIndex = i; break; } } bool isUrp = false; if (position.committedMove().isAMove()) { // our move not in list if (ourMoveIndex == 0) { if (moves.size() == movesToShow) moves.pop_back(); isUrp = true; ourMoveIndex = movesToShow; moves.push_back(position.committedMove()); } } int highestScore = 0; double highestEquity = 0; unsigned int widestPositionString = 0; unsigned int widestMove = 0; bool hasWinPercentages = false; const Quackle::MoveList::const_iterator end(moves.end()); for (Quackle::MoveList::const_iterator it = moves.begin(); it != end; ++it) { if ((*it).prettyTiles().length() > widestMove) widestMove = (*it).prettyTiles().length(); if ((*it).positionString().length() > widestPositionString) widestPositionString = (*it).positionString().length(); if ((*it).win > 0) hasWinPercentages = true; if ((*it).equity > highestEquity) highestEquity = (*it).equity; if ((*it).score > highestScore) highestScore = (*it).score; } s << MARK_UV("--"); UVOStringStream headerStream; headerStream << computerPlayer->name(); headerStream << "'s choices (your play: "; if (isUrp) headerStream << "urp"; else headerStream << ourMoveIndex; headerStream << ")"; s.width(43); s << setfill(MARK_UV('-')); s << left << headerStream.str() << MARK_UV('\n'); s << setfill(MARK_UV(' ')); i = 1; for (Quackle::MoveList::const_iterator it = moves.begin(); it != end; ++it, ++i) { // column 2, the valuation. s.width(5); if ((*it).equity > (highestEquity - .01) && (*it).equity < (highestEquity + .01)) { s << MARK_UV("best"); } else { s << right << showpoint; s.precision(3); s << (highestEquity - (*it).equity); } s << (i == ourMoveIndex? MARK_UV("*") : MARK_UV(" ")); // column 3, the position string. s << left; s.width(widestPositionString); switch ((*it).action) { case Move::Place: s << (*it).positionString(); break; case Move::Exchange: s << MARK_UV("xch"); break; case Move::Pass: s << MARK_UV("pas"); break; case Move::UnusedTilesBonus: case Move::TimePenalty: case Move::Nonmove: break; } s << MARK_UV(" "); // column 4, the word s.width(widestMove); s << left << QUACKLE_ALPHABET_PARAMETERS->userVisible((*it).prettyTiles()) << MARK_UV(" "); // column 5, the score s.width(highestScore >= 100? 3 : (highestScore >= 10? 2 : 1)); s << left << (*it).score << MARK_UV(" "); // column 6, the win percentage if (hasWinPercentages) { s.precision(4); s.width(5); s << showpoint << ((*it).win * 100.0) << MARK_UV("% "); } // column 7, the leave s << (position.currentPlayer().rack() - (*it)).toString() << MARK_UV('\n'); } } if (position.gameOver()) { s << MARK_UV("\n Game over.\n\n"); } int j = 0; UVString wrappedTiles; LongLetterString unseenTiles = position.unseenBag().tiles(); for (Quackle::LongLetterString::const_iterator it = unseenTiles.begin(); it != unseenTiles.end(); ++it, ++j) { if (j >= 44) { wrappedTiles += MARK_UV('\n'); j = 0; } wrappedTiles += QUACKLE_ALPHABET_PARAMETERS->userVisible(*it); } s << MARK_UV("--"); s.width(43); s << setfill(MARK_UV('-')); s << MARK_UV("Tracking") << MARK_UV('\n'); s << wrappedTiles << MARK_UV(" ") << unseenTiles.size() << MARK_UV('\n'); UVString reportString = s.str(); UVString boardString = position.board().toString(); *report = titleStream.str(); // Ensure that the board ends with a newline. boardString += MARK_UV("\n"); // Put board and report side by side. UVString::const_iterator boardIt = boardString.begin(); UVString::const_iterator reportIt = reportString.begin(); UVString::const_iterator reportLineBeginning = reportString.begin(); UVString::const_iterator boardLineBeginning = boardString.begin(); const UVString::const_iterator reportEnd = reportString.end(); const UVString::const_iterator boardEnd = boardString.end(); while (true) { if (boardIt == boardEnd && reportIt == reportEnd) break; if (boardIt != boardEnd && (*boardIt) != MARK_UV('\n')) { ++boardIt; continue; } if (reportIt != reportEnd && (*reportIt) != MARK_UV('\n')) { ++reportIt; continue; } if (boardIt != boardEnd) { report->append(boardLineBeginning, boardIt); } if (reportIt != reportEnd) { report->append(MARK_UV(" ")); report->append(reportLineBeginning, reportIt); } boardLineBeginning = boardIt + 1; reportLineBeginning = reportIt + 1; if (boardIt != boardEnd) ++boardIt; if (reportIt != reportEnd) ++reportIt; report->append(MARK_UV("\n")); } }
void GraphicalReporter::reportPosition(const Quackle::GamePosition &position, Quackle::ComputerPlayer *computerPlayer) { openIndex(); const QSize pictureSize(500, 500); Quackle::GamePosition positionCopy = position; { QString title; if (!position.gameOver()) { title = GraphicalBoard::tr("<h2>%1: Turn %2</h2>").arg(QuackleIO::Util::uvStringToQString(position.currentPlayer().name())).arg(position.turnNumber()); } else { title = GraphicalBoard::tr("<h2>Game over.</h2>"); } if (m_generateImages) { QPixmap pixmap; positionCopy.resetMoveMade(); GraphicalBoardFrame::staticDrawPosition(positionCopy, pictureSize, &pixmap); QImage image = pixmap.toImage(); const QString filebasename = QString("%1-%2-position.png").arg(position.turnNumber()).arg(QuackleIO::Util::uvStringToQString(position.currentPlayer().name())); const QString filename = makeFilename(filebasename); if (image.save(filename, "PNG")) { m_indexStream << QString("<a href=\"%1\">%2</a>").arg(filebasename).arg(title) << endl; } else { QMessageBox::critical(0, GraphicalBoard::tr("Error Writing File - Quacker"), GraphicalBoard::tr("Could not write image %1.").arg(filename)); } m_indexStream << "<p><img src=\"" << filebasename << "\"></p>" << endl; } else { m_indexStream << title; const int boardTileSize = position.gameOver()? 45 : 25; m_indexStream << QuackleIO::Util::sanitizeUserVisibleLetterString(QuackleIO::Util::uvStringToQString(position.board().htmlBoard(boardTileSize))) << endl; } } const Quackle::PlayerList players(position.endgameAdjustedScores()); m_indexStream << "<table cellspacing=6>" << endl; for (Quackle::PlayerList::const_iterator it = players.begin(); it != players.end(); ++it) { m_indexStream << "<tr>"; m_indexStream << "<td>"; if ((*it) == position.currentPlayer()) m_indexStream << "→"; else m_indexStream << " "; m_indexStream << "</td>"; m_indexStream << "<td>" << QuackleIO::Util::uvStringToQString((*it).name()) << "</td>" << "<td>" << QuackleIO::Util::sanitizeUserVisibleLetterString(QuackleIO::Util::uvStringToQString((*it).rack().toString())) << "</td>" << "<td>" << (*it).score() << "</td>" << "</tr>" << endl; } m_indexStream << "</table>" << endl; if (computerPlayer && !position.gameOver()) { computerPlayer->setPosition(position); if (position.committedMove().isAMove()) computerPlayer->considerMove(position.committedMove()); const unsigned int movesToShow = 5; Quackle::MoveList moves = computerPlayer->moves(movesToShow); if (!moves.contains(position.committedMove())) { if (moves.size() == movesToShow) moves.pop_back(); moves.push_back(position.committedMove()); } m_indexStream << "<ol>" << endl; for (Quackle::MoveList::const_iterator it = moves.begin(); it != moves.end(); ++it) { QString item; switch ((*it).action) { case Quackle::Move::Place: case Quackle::Move::PlaceError: { if (m_generateImages) { QPixmap pixmap; positionCopy.setMoveMade(*it); GraphicalBoardFrame::staticDrawPosition(positionCopy, pictureSize, &pixmap); QImage image = pixmap.toImage(); const QString filebasename = QString("%1-%2-%3-%4.png").arg(position.turnNumber()).arg(QuackleIO::Util::uvStringToQString(position.currentPlayer().name())).arg(QuackleIO::Util::letterStringToQString((*it).prettyTiles())).arg(QuackleIO::Util::uvStringToQString((*it).positionString())); const QString filename = makeFilename(filebasename); if (image.save(filename, "PNG")) { item = QString("<a href=\"%1\">%2</a> %3").arg(filebasename); } else { QMessageBox::critical(0, GraphicalBoard::tr("Error Writing File - Quacker"), GraphicalBoard::tr("Could not write image %1.").arg(filename)); } } else { item = "%1 %2"; } item = item.arg(QuackleIO::Util::sanitizeUserVisibleLetterString(QuackleIO::Util::moveToDetailedString(*it))).arg((*it).score); break; } case Quackle::Move::Exchange: case Quackle::Move::BlindExchange: default: item = QuackleIO::Util::moveToDetailedString(*it); break; } if (*it == position.committedMove()) item += QString(" ←"); if (!item.isEmpty()) m_indexStream << "<li>" << item << "</li>" << endl; } m_indexStream << "</ol>" << endl; } m_indexStream << "\n\n"; }