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")); } }