Skip to content

Commit

Permalink
Modernise and refactor reading and writing of savefiles.
Browse files Browse the repository at this point in the history
This does some general cleanups of routines to read and write savefiles:

* Creates a standardised ReadXXXFile/WriteXXXFile set of functions.
* Uses iterators to make the code, specifically the writing code, more generic.
* Removes uses of Gambit containers (and manually-managed linked lists!) in favour of STL equivalents.
  • Loading branch information
tturocy committed Oct 16, 2024
1 parent ad47d54 commit cbd942f
Show file tree
Hide file tree
Showing 14 changed files with 464 additions and 799 deletions.
755 changes: 240 additions & 515 deletions src/games/file.cc

Large diffs are not rendered by default.

57 changes: 19 additions & 38 deletions src/games/game.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <random>

#include "gambit.h"
#include "writer.h"

// The references to the table and tree representations violate the logic
// of separating implementation types. This will be fixed when we move
Expand Down Expand Up @@ -265,24 +266,6 @@ Array<GameStrategy> GameRep::GetStrategies() const
// GameRep: Writing data files
//------------------------------------------------------------------------

namespace {

std::string EscapeQuotes(const std::string &s)
{
std::string ret;

for (char c : s) {
if (c == '"') {
ret += '\\';
}
ret += c;
}

return ret;
}

} // end anonymous namespace

///
/// Write the game to a savefile in .nfg payoff format.
///
Expand All @@ -297,31 +280,29 @@ std::string EscapeQuotes(const std::string &s)
void GameRep::WriteNfgFile(std::ostream &p_file) const
{
auto players = GetPlayers();
p_file << "NFG 1 R";
p_file << " \"" << EscapeQuotes(GetTitle()) << "\" { ";
for (auto player : players) {
p_file << '"' << EscapeQuotes(player->GetLabel()) << "\" ";
}
p_file << "}\n\n{ ";

p_file << "NFG 1 R " << std::quoted(GetTitle()) << ' '
<< FormatList(players, [](const GamePlayer &p) { return QuoteString(p->GetLabel()); })
<< std::endl
<< std::endl;
p_file << "{ ";
for (auto player : players) {
p_file << "{ ";
for (auto strategy : player->GetStrategies()) {
p_file << '"' << EscapeQuotes(strategy->GetLabel()) << "\" ";
}
p_file << "}\n";
p_file << FormatList(player->GetStrategies(), [](const GameStrategy &s) {
return QuoteString(s->GetLabel());
}) << std::endl;
}
p_file << "}\n";
p_file << "\"" << EscapeQuotes(m_comment) << "\"\n\n";
p_file << "}" << std::endl;
p_file << std::quoted(GetComment()) << std::endl << std::endl;

for (StrategyProfileIterator iter(StrategySupportProfile(Game(const_cast<GameRep *>(this))));
!iter.AtEnd(); iter++) {
for (auto player : players) {
p_file << (*iter)->GetPayoff(player) << " ";
}
p_file << "\n";
}
p_file << '\n';
p_file << FormatList(
players,
[&iter](const GamePlayer &p) {
return lexical_cast<std::string>((*iter)->GetPayoff(p));
},
false, false)
<< std::endl;
};
}

//========================================================================
Expand Down
37 changes: 34 additions & 3 deletions src/games/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,10 @@ class GameRep : public BaseGameRep {
throw UndefinedException();
}
/// Write the game in .efg format to the specified stream
virtual void WriteEfgFile(std::ostream &) const { throw UndefinedException(); }
virtual void WriteEfgFile(std::ostream &, const GameNode &subtree = 0) const
{
throw UndefinedException();
}
/// Write the game to a file in .nfg payoff format.
virtual void WriteNfgFile(std::ostream &p_stream) const;
//@}
Expand Down Expand Up @@ -638,8 +641,36 @@ inline GameStrategy GamePlayerRep::GetStrategy(int st) const
Game NewTree();
/// Factory function to create new game table
Game NewTable(const Array<int> &p_dim, bool p_sparseOutcomes = false);
/// Reads a game in .efg or .nfg format from the input stream
Game ReadGame(std::istream &);

/// @brief Reads a game representation in .efg format
///
/// @param[in] p_stream An input stream, positioned at the start of the text in .efg format
/// @return A handle to the game representation constructed
/// @throw InvalidFileException If the stream does not contain a valid serialisation
/// of a game in .efg format.
/// @sa Game::WriteEfgFile, ReadNfgFile, ReadAggFile, ReadBaggFile
Game ReadEfgFile(std::istream &p_stream);

/// @brief Reads a game representation in .nfg format
/// @param[in] p_stream An input stream, positioned at the start of the text in .nfg format
/// @return A handle to the game representation constructed
/// @throw InvalidFileException If the stream does not contain a valid serialisation
/// of a game in .nfg format.
/// @sa Game::WriteNfgFile, ReadEfgFile, ReadAggFile, ReadBaggFile
Game ReadNfgFile(std::istream &p_stream);

/// @brief Reads a game representation from a graphical interface XML saveflie
/// @param[in] p_stream An input stream, positioned at the start of the text
/// @return A handle to the game representation constructed
/// @throw InvalidFileException If the stream does not contain a valid serialisation
/// of a game in an XML savefile
/// @sa ReadEfgFile, ReadNfgFile, ReadAggFile, ReadBaggFile
Game ReadGbtFile(std::istream &p_stream);

/// @brief Reads a game from the input stream, attempting to autodetect file format
/// @deprecated Deprecated in favour of the various ReadXXXGame functions.
/// @sa ReadEfgFile, ReadNfgFile, ReadGbtFile, ReadAggFile, ReadBaggFile
Game ReadGame(std::istream &p_stream);

/// @brief Generate a distribution over a simplex restricted to rational numbers of given
/// denominator
Expand Down
2 changes: 0 additions & 2 deletions src/games/gameagg.cc
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,4 @@ void GameAGGRep::WriteAggFile(std::ostream &s) const
}
}

Game GameAGGRep::ReadAggFile(std::istream &in) { return new GameAGGRep(agg::AGG::makeAGG(in)); }

} // end namespace Gambit
24 changes: 18 additions & 6 deletions src/games/gameagg.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,11 @@ class GameAGGRep : public GameRep {
std::shared_ptr<agg::AGG> aggPtr;
Array<GamePlayerRep *> m_players;

/// Constructor
explicit GameAGGRep(std::shared_ptr<agg::AGG>);

public:
/// @name Lifecycle
//@{
/// Create a game from a serialized file in AGG format
static Game ReadAggFile(std::istream &);
/// Constructor
explicit GameAGGRep(std::shared_ptr<agg::AGG>);
/// Destructor
~GameAGGRep() override
{
Expand Down Expand Up @@ -149,10 +146,25 @@ class GameAGGRep : public GameRep {
//@{
/// Write the game to a savefile in the specified format.
void Write(std::ostream &p_stream, const std::string &p_format = "native") const override;
virtual void WriteAggFile(std::ostream &) const;
void WriteAggFile(std::ostream &) const;
//@}
};

/// @brief Reads a game representation in .agg format
/// @param[in] p_stream An input stream, positioned at the start of the text in .agg format
/// @return A handle to the game representation constructed
/// @throw InvalidFileException If the stream does not contain a valid serialisation
/// of a game in .agg format.
inline Game ReadAggFile(std::istream &in)
{
try {
return new GameAGGRep(agg::AGG::makeAGG(in));
}
catch (std::runtime_error &ex) {
throw InvalidFileException(ex.what());
}
}

} // namespace Gambit

#endif // GAMEAGG_H
5 changes: 0 additions & 5 deletions src/games/gamebagg.cc
Original file line number Diff line number Diff line change
Expand Up @@ -307,9 +307,4 @@ void GameBAGGRep::Write(std::ostream &p_stream, const std::string &p_format /*="

void GameBAGGRep::WriteBaggFile(std::ostream &s) const { s << (*baggPtr); }

Game GameBAGGRep::ReadBaggFile(std::istream &in)
{
return new GameBAGGRep(agg::BAGG::makeBAGG(in));
}

} // end namespace Gambit
22 changes: 17 additions & 5 deletions src/games/gamebagg.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,11 @@ class GameBAGGRep : public GameRep {
Array<int> agent2baggPlayer;
Array<GamePlayerRep *> m_players;

/// Constructor
explicit GameBAGGRep(std::shared_ptr<agg::BAGG> _baggPtr);

public:
/// @name Lifecycle
//@{
/// Create a game from a serialized file in BAGG format
static Game ReadBaggFile(std::istream &);
/// Constructor
explicit GameBAGGRep(std::shared_ptr<agg::BAGG> _baggPtr);
/// Destructor
~GameBAGGRep() override
{
Expand Down Expand Up @@ -154,6 +151,21 @@ class GameBAGGRep : public GameRep {
//@}
};

/// @brief Reads a game representation in .bagg format
/// @param[in] p_stream An input stream, positioned at the start of the text in .bagg format
/// @return A handle to the game representation constructed
/// @throw InvalidFileException If the stream does not contain a valid serialisation
/// of a game in .bagg format.
inline Game ReadBaggFile(std::istream &in)
{
try {
return new GameBAGGRep(agg::BAGG::makeBAGG(in));
}
catch (std::runtime_error &ex) {
throw InvalidFileException(ex.what());
}
}

} // end namespace Gambit

#endif // GAMEBAGG_H
92 changes: 26 additions & 66 deletions src/games/gametable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include "gambit.h"
#include "gametable.h"
#include "writer.h"

namespace Gambit {

Expand Down Expand Up @@ -343,24 +344,6 @@ bool GameTableRep::IsConstSum() const
// GameTableRep: Writing data files
//------------------------------------------------------------------------

namespace {

std::string EscapeQuotes(const std::string &s)
{
std::string ret;

for (char c : s) {
if (c == '"') {
ret += '\\';
}
ret += c;
}

return ret;
}

} // end anonymous namespace

///
/// Write the game to a savefile in .nfg outcome format.
///
Expand All @@ -372,58 +355,35 @@ std::string EscapeQuotes(const std::string &s)
///
void GameTableRep::WriteNfgFile(std::ostream &p_file) const
{
p_file << "NFG 1 R";
p_file << " \"" << EscapeQuotes(GetTitle()) << "\" { ";

for (int i = 1; i <= NumPlayers(); i++) {
p_file << '"' << EscapeQuotes(GetPlayer(i)->GetLabel()) << "\" ";
}

p_file << "}\n\n{ ";

for (int i = 1; i <= NumPlayers(); i++) {
GamePlayerRep *player = GetPlayer(i);
p_file << "{ ";
for (int j = 1; j <= player->NumStrategies(); j++) {
p_file << '"' << EscapeQuotes(player->GetStrategy(j)->GetLabel()) << "\" ";
}
p_file << "}\n";
}

p_file << "}\n";

p_file << "\"" << EscapeQuotes(m_comment) << "\"\n\n";

int ncont = 1;
for (int i = 1; i <= NumPlayers(); i++) {
ncont *= m_players[i]->m_strategies.Length();
}

p_file << "{\n";
auto players = GetPlayers();
p_file << "NFG 1 R " << std::quoted(GetTitle()) << ' '
<< FormatList(players, [](const GamePlayer &p) { return QuoteString(p->GetLabel()); })
<< std::endl
<< std::endl;
p_file << "{ ";
for (auto player : players) {
p_file << FormatList(player->GetStrategies(), [](const GameStrategy &s) {
return QuoteString(s->GetLabel());
}) << std::endl;
}
p_file << "}" << std::endl;
p_file << std::quoted(GetComment()) << std::endl << std::endl;

p_file << "{" << std::endl;
for (auto outcome : m_outcomes) {
p_file << "{ \"" << EscapeQuotes(outcome->m_label) << "\" ";
for (int pl = 1; pl <= m_players.Length(); pl++) {
p_file << (const std::string &)outcome->m_payoffs[pl];
if (pl < m_players.Length()) {
p_file << ", ";
}
else {
p_file << " }\n";
}
}
p_file << "{ " + QuoteString(outcome->GetLabel()) << ' '
<< FormatList(
players,
[outcome](const GamePlayer &p) { return std::string(outcome->GetPayoff(p)); },
true, false)
<< " }" << std::endl;
}
p_file << "}\n";
p_file << "}" << std::endl;

for (int cont = 1; cont <= ncont; cont++) {
if (m_results[cont] != 0) {
p_file << m_results[cont]->m_number << ' ';
}
else {
p_file << "0 ";
}
for (auto result : m_results) {
p_file << ((result) ? result->m_number : 0) << ' ';
}

p_file << '\n';
p_file << std::endl;
}

//------------------------------------------------------------------------
Expand Down
Loading

0 comments on commit cbd942f

Please sign in to comment.