summaryrefslogtreecommitdiff
path: root/reporter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'reporter.cpp')
-rw-r--r--reporter.cpp310
1 files changed, 310 insertions, 0 deletions
diff --git a/reporter.cpp b/reporter.cpp
new file mode 100644
index 0000000..62fce18
--- /dev/null
+++ b/reporter.cpp
@@ -0,0 +1,310 @@
+/*
+ * Quackle -- Crossword game artificial intelligence and analysis tool
+ * Copyright (C) 2005-2006 Jason Katz-Brown and John O'Laughlin.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <iomanip>
+#include <iostream>
+
+#include "alphabetparameters.h"
+#include "computerplayer.h"
+#include "datamanager.h"
+#include "game.h"
+
+#include "reporter.h"
+
+using namespace Quackle;
+
+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 Reporter::reportHeader(const Game & /* game */, UVString *report)
+{
+ UVOStringStream s;
+ s << MARK_UV("Game Report\nGenerated by Quackle crossword game AI and analysis software\nhttp://quackle.org\n\n");
+ *report = s.str();
+}
+
+void Reporter::reportGameStatistics(const Game &game, UVString *report)
+{
+ (void) game;
+ UVOStringStream s;
+ *report = s.str();
+}
+
+void Reporter::reportGame(const Game &game, ComputerPlayer *computerPlayer, UVString *report)
+{
+ UVOStringStream s;
+
+ UVString header;
+ reportHeader(game, &header);
+ s << header;
+
+ const PositionList::const_iterator end(game.history().end());
+ for (PositionList::const_iterator it = game.history().begin(); it != end; ++it)
+ {
+ UVString subreport;
+ reportPosition(*it, computerPlayer, &subreport);
+
+ s << subreport << MARK_UV('\n');
+ }
+
+ UVString stats;
+ reportGameStatistics(game, &stats);
+ s << stats;
+
+ *report = s.str();
+}