diff --git a/ChangeLog b/ChangeLog index 15396d422..d745cf1fb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,9 @@ ### Removed - Eliminating dominated actions has been removed from the GUI as it was implementing a non-standard formulation of dominance. (#612) +- The C++ method for computing subgame perfect equilibria with selected methods has been removed + (and as a result from the `enumpure`, `lp`, and `lcp` command-line tools); this will be replaced + with new and more customisable approaches. (#639) ## [16.4.1] - unreleased diff --git a/Makefile.am b/Makefile.am index 681edc9ca..efa5463ba 100644 --- a/Makefile.am +++ b/Makefile.am @@ -309,7 +309,6 @@ game_SOURCES = \ src/games/stratpure.cc \ src/games/stratpure.h \ src/games/stratmixed.h \ - src/games/nash.cc \ src/games/file.cc \ src/games/writer.cc \ src/games/writer.h \ diff --git a/doc/tools.enumpure.rst b/doc/tools.enumpure.rst index 407461fd0..f9dfafc9e 100644 --- a/doc/tools.enumpure.rst +++ b/doc/tools.enumpure.rst @@ -43,14 +43,6 @@ pure-strategy Nash equilibria. extensive games, as strategic games have only one information set per player. -.. cmdoption:: -P - - By default, the program computes all pure-strategy Nash - equilibria in an extensive game. This switch instructs the program to - find only pure-strategy Nash equilibria which are subgame perfect. - (This has no effect for strategic games, since there are no proper - subgames of a strategic game.) - .. cmdoption:: -h Prints a help message listing the available options. diff --git a/doc/tools.lcp.rst b/doc/tools.lcp.rst index 486ce6c20..62eb424f5 100644 --- a/doc/tools.lcp.rst +++ b/doc/tools.lcp.rst @@ -51,13 +51,6 @@ game. causes the program to output greater detail on each equilbrium profile computed. -.. cmdoption:: -P - - By default, the program computes Nash equilibria in an extensive - game. This switch instructs the program to find only equilibria - which are subgame perfect. (This has no effect for strategic - games, since there are no proper subgames of a strategic game.) - .. cmdoption:: -e EQA By default, the program will find all equilibria accessible from diff --git a/doc/tools.lp.rst b/doc/tools.lp.rst index f032fee9e..efa47563d 100644 --- a/doc/tools.lp.rst +++ b/doc/tools.lp.rst @@ -44,13 +44,6 @@ points of that set. causes the program to output greater detail on each equilbrium profile computed. -.. cmdoption:: -P - - By default, the program computes Nash equilibria in an extensive - game. This switch instructs the program to find only equilibria - which are subgame perfect. (This has no effect for strategic - games, since there are no proper subgames of a strategic game.) - .. cmdoption:: -h Prints a help message listing the available options. diff --git a/src/games/nash.cc b/src/games/nash.cc deleted file mode 100644 index 74596078a..000000000 --- a/src/games/nash.cc +++ /dev/null @@ -1,204 +0,0 @@ -// -// This file is part of Gambit -// Copyright (c) 1994-2025, The Gambit Project (https://www.gambit-project.org) -// -// FILE: src/libgambit/nash.cc -// Framework for computing (sub)sets of Nash equilibria. -// -// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// - -#include "nash.h" - -namespace Gambit::Nash { - -// A nested anonymous namespace to privatize these functions - -namespace { - -/// -/// Returns a list of the root nodes of all the immediate proper subgames -/// in the subtree rooted at 'p_node'. -/// -std::list ChildSubgames(const GameNode &p_node, const bool p_root = true) -{ - if (!p_root && p_node->IsSubgameRoot()) { - return {p_node}; - } - std::list ret; - for (const auto &child : p_node->GetChildren()) { - ret.splice(ret.end(), ChildSubgames(child, false)); - } - return ret; -} - -} // namespace - -// -// Some general notes on the strategy for solving by subgames: -// -// * We work with a *copy* of the original game, which is destroyed -// as we go. -// * Before solving, information set labels on the copy game are -// set to unique IDs. These are used to match up information -// sets in the subgames (which are themselves copies) to the -// original game. -// - -template class SubgameSolution { -private: - std::map profile; - std::map node_values; - -public: - SubgameSolution(const std::map &p_profile, - const std::map &p_nodeValues) - : profile(p_profile), node_values(p_nodeValues) - { - } - - const std::map &GetProfile() const { return profile; } - const std::map &GetNodeValues() const { return node_values; } - - SubgameSolution Combine(const SubgameSolution &other) const - { - auto combined = *this; - combined.profile.insert(other.profile.cbegin(), other.profile.cend()); - combined.node_values.insert(other.node_values.cbegin(), other.node_values.cend()); - return combined; - } - - SubgameSolution Update(const GameNode &p_subroot, const MixedBehaviorProfile &p_profile, - const std::map &p_infosetMap) - { - SubgameSolution solution = {profile, {{p_subroot, p_subroot->GetGame()->NewOutcome()}}}; - - for (const auto &subplayer : p_profile.GetGame()->GetPlayers()) { - for (const auto &subinfoset : subplayer->GetInfosets()) { - const GameInfoset infoset = p_infosetMap.at(subinfoset->GetLabel()); - const auto &subactions = subinfoset->GetActions(); - auto subaction = subactions.begin(); - for (const auto &action : infoset->GetActions()) { - solution.profile[action] = p_profile[*subaction]; - ++subaction; - } - } - } - - const GameOutcome outcome = p_subroot->GetOutcome(); - const auto &subplayers = p_profile.GetGame()->GetPlayers(); - auto subplayer = subplayers.begin(); - for (const auto &player : p_subroot->GetGame()->GetPlayers()) { - T value = p_profile.GetPayoff(*subplayer); - if (outcome) { - value += outcome->GetPayoff(*subplayer); - } - solution.node_values[p_subroot]->SetPayoff(player, Number(static_cast(value))); - ++subplayer; - } - return solution; - } -}; - -template -std::list> SolveSubgames(const GameNode &p_root, - const std::map &p_infosetMap, - BehaviorSolverType p_solver) -{ - std::list> subsolutions = {{{}, {}}}; - for (const auto &subroot : ChildSubgames(p_root)) { - std::list> combined_solutions; - for (const auto &solution : SolveSubgames(subroot, p_infosetMap, p_solver)) { - for (const auto &subsolution : subsolutions) { - combined_solutions.push_back(subsolution.Combine(solution)); - } - } - if (combined_solutions.empty()) { - return {}; - } - subsolutions = combined_solutions; - } - - std::list> solutions; - for (auto subsolution : subsolutions) { - for (auto [subroot, outcome] : subsolution.GetNodeValues()) { - subroot->GetGame()->SetOutcome(subroot, outcome); - } - // This prevents double-counting of outcomes at roots of subgames. - // By convention, we will just put the payoffs in the parent subgame. - const Game subgame = p_root->GetGame()->CopySubgame(p_root); - subgame->SetOutcome(subgame->GetRoot(), nullptr); - - for (const auto &solution : p_solver(subgame)) { - solutions.push_back(subsolution.Update(p_root, solution, p_infosetMap)); - } - } - - p_root->GetGame()->DeleteTree(p_root); - return solutions; -} - -template -MixedBehaviorProfile BuildProfile(const Game &p_game, const SubgameSolution &p_solution) -{ - MixedBehaviorProfile profile(p_game); - for (const auto &player : p_game->GetPlayers()) { - for (const auto &infoset : player->GetInfosets()) { - for (const auto &action : infoset->GetActions()) { - profile[action] = p_solution.GetProfile().at(action); - } - } - } - return profile; -} - -template -std::list> SolveBySubgames(const Game &p_game, - BehaviorSolverType p_solver, - BehaviorCallbackType p_onEquilibrium) -{ - const Game efg = p_game->CopySubgame(p_game->GetRoot()); - - int index = 1; - std::map infoset_map; - for (const auto &player : p_game->GetPlayers()) { - for (const auto &infoset : player->GetInfosets()) { - infoset_map[std::to_string(index++)] = infoset; - } - } - index = 1; - for (const auto &player : efg->GetPlayers()) { - for (const auto &infoset : player->GetInfosets()) { - infoset->SetLabel(std::to_string(index++)); - } - } - - auto results = SolveSubgames(efg->GetRoot(), infoset_map, p_solver); - std::list> solutions; - for (const auto &result : results) { - solutions.push_back(BuildProfile(p_game, result)); - p_onEquilibrium(solutions.back(), "NE"); - } - return solutions; -} - -template std::list> -SolveBySubgames(const Game &p_game, BehaviorSolverType p_solver, - BehaviorCallbackType p_onEquilibrium); -template std::list> -SolveBySubgames(const Game &p_game, BehaviorSolverType p_solver, - BehaviorCallbackType p_onEquilibrium); - -} // namespace Gambit::Nash diff --git a/src/games/nash.h b/src/games/nash.h index a22c3166b..ac708f35f 100644 --- a/src/games/nash.h +++ b/src/games/nash.h @@ -60,10 +60,6 @@ ToMixedBehaviorProfile(const std::list> &p_list) template using BehaviorSolverType = std::function>(const Game &)>; -template -std::list> SolveBySubgames(const Game &, BehaviorSolverType p_solver, - BehaviorCallbackType p_onEquilibrium); - // // Exception raised when maximum number of equilibria to compute // has been reached. A convenience for unraveling a potentially diff --git a/src/tools/convert/convert.cc b/src/tools/convert/convert.cc index ea4497c24..0b375fdd4 100644 --- a/src/tools/convert/convert.cc +++ b/src/tools/convert/convert.cc @@ -22,12 +22,13 @@ #include #include -#include #include #include "gambit.h" #include "games/writer.h" +using namespace Gambit; + void PrintBanner(std::ostream &p_stream) { p_stream << "Convert games among various file formats\n"; @@ -79,7 +80,7 @@ int main(int argc, char *argv[]) break; case '?': if (isprint(optopt)) { - std::cerr << argv[0] << ": Unknown option `-" << ((char)optopt) << "'.\n"; + std::cerr << argv[0] << ": Unknown option `-" << static_cast(optopt) << "'.\n"; } else { std::cerr << argv[0] << ": Unknown option character `\\x" << optopt << "`.\n"; @@ -122,7 +123,7 @@ int main(int argc, char *argv[]) } try { - const Gambit::Game game = Gambit::ReadGame(*input_stream); + const Game game = ReadGame(*input_stream); if (rowPlayer < 1 || rowPlayer > static_cast(game->NumPlayers())) { std::cerr << argv[0] << ": Player " << rowPlayer << " does not exist.\n"; diff --git a/src/tools/enummixed/enummixed.cc b/src/tools/enummixed/enummixed.cc index 2212bc104..fbe035764 100644 --- a/src/tools/enummixed/enummixed.cc +++ b/src/tools/enummixed/enummixed.cc @@ -20,7 +20,6 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // -#include #include #include #include @@ -34,7 +33,7 @@ using namespace Gambit::Nash; template void PrintCliques(const List>> &p_cliques, - std::shared_ptr> p_renderer) + std::shared_ptr> p_renderer) { for (size_t cl = 1; cl <= p_cliques.size(); cl++) { for (size_t i = 1; i <= p_cliques[cl].size(); i++) { @@ -75,7 +74,7 @@ int main(int argc, char *argv[]) int numDecimals = 6; int long_opt_index = 0; - struct option long_options[] = { + option long_options[] = { {"help", 0, nullptr, 'h'}, {"version", 0, nullptr, 'v'}, {nullptr, 0, nullptr, 0}}; while ((c = getopt_long(argc, argv, "d:vhqcS", long_options, &long_opt_index)) != -1) { switch (c) { @@ -99,7 +98,7 @@ int main(int argc, char *argv[]) break; case '?': if (isprint(optopt)) { - std::cerr << argv[0] << ": Unknown option `-" << ((char)optopt) << "'.\n"; + std::cerr << argv[0] << ": Unknown option `-" << static_cast(optopt) << "'.\n"; } else { std::cerr << argv[0] << ": Unknown option character `\\x" << optopt << "`.\n"; @@ -130,8 +129,7 @@ int main(int argc, char *argv[]) try { const Game game = ReadGame(*input_stream); if (useFloat) { - std::shared_ptr> renderer = - std::make_shared>(std::cout, numDecimals); + auto renderer = MakeMixedStrategyProfileRenderer(std::cout, numDecimals, false); auto solution = EnumMixedStrategySolveDetailed( game, [&](const MixedStrategyProfile &p, const std::string &label) { renderer->Render(p, label); @@ -141,8 +139,7 @@ int main(int argc, char *argv[]) } } else { - std::shared_ptr> renderer( - new MixedStrategyCSVRenderer(std::cout)); + auto renderer = MakeMixedStrategyProfileRenderer(std::cout, numDecimals, false); auto solution = EnumMixedStrategySolveDetailed( game, [&](const MixedStrategyProfile &p, const std::string &label) { renderer->Render(p, label); diff --git a/src/tools/enumpoly/enumpoly.cc b/src/tools/enumpoly/enumpoly.cc index d5e7d887b..b1add8c80 100644 --- a/src/tools/enumpoly/enumpoly.cc +++ b/src/tools/enumpoly/enumpoly.cc @@ -22,13 +22,12 @@ #include #include -#include #include #include "gambit.h" #include "solvers/enumpoly/enumpoly.h" using namespace Gambit; -using namespace Gambit::Nash; +using namespace Nash; int g_numDecimals = 6; bool g_verbose = false; @@ -128,10 +127,10 @@ int main(int argc, char *argv[]) int stopAfter = 0; int long_opt_index = 0; - struct option long_options[] = {{"help", 0, nullptr, 'h'}, - {"version", 0, nullptr, 'v'}, - {"verbose", 0, nullptr, 'V'}, - {nullptr, 0, nullptr, 0}}; + option long_options[] = {{"help", 0, nullptr, 'h'}, + {"version", 0, nullptr, 'v'}, + {"verbose", 0, nullptr, 'V'}, + {nullptr, 0, nullptr, 0}}; int c; while ((c = getopt_long(argc, argv, "d:hSm:e:qvV", long_options, &long_opt_index)) != -1) { switch (c) { @@ -161,7 +160,7 @@ int main(int argc, char *argv[]) break; case '?': if (isprint(optopt)) { - std::cerr << argv[0] << ": Unknown option `-" << ((char)optopt) << "'.\n"; + std::cerr << argv[0] << ": Unknown option `-" << static_cast(optopt) << "'.\n"; } else { std::cerr << argv[0] << ": Unknown option character `\\x" << optopt << "`.\n"; @@ -190,9 +189,9 @@ int main(int argc, char *argv[]) } try { - const Gambit::Game game = Gambit::ReadGame(*input_stream); + const Game game = ReadGame(*input_stream); if (!game->IsPerfectRecall()) { - throw Gambit::UndefinedException( + throw UndefinedException( "Computing equilibria of games with imperfect recall is not supported."); } diff --git a/src/tools/enumpure/enumpure.cc b/src/tools/enumpure/enumpure.cc index 5ace4e5ca..8258e62a5 100644 --- a/src/tools/enumpure/enumpure.cc +++ b/src/tools/enumpure/enumpure.cc @@ -20,7 +20,6 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // -#include #include #include #include @@ -49,7 +48,6 @@ void PrintHelp(char *progname) std::cerr << "Options:\n"; std::cerr << " -S report equilibria in strategies even for extensive games\n"; std::cerr << " -A compute agent form equilibria\n"; - std::cerr << " -P find only subgame-perfect equilibria\n"; std::cerr << " -h, --help print this help message\n"; std::cerr << " -q quiet mode (suppresses banner)\n"; std::cerr << " -v, --version print version information\n"; @@ -59,14 +57,14 @@ void PrintHelp(char *progname) int main(int argc, char *argv[]) { opterr = 0; - bool quiet = false, reportStrategic = false, solveAgent = false, bySubgames = false; + bool quiet = false, reportStrategic = false, solveAgent = false; bool printDetail = false; int long_opt_index = 0; - struct option long_options[] = { + option long_options[] = { {"help", 0, nullptr, 'h'}, {"version", 0, nullptr, 'v'}, {nullptr, 0, nullptr, 0}}; int c; - while ((c = getopt_long(argc, argv, "DvhqASP", long_options, &long_opt_index)) != -1) { + while ((c = getopt_long(argc, argv, "DvhqAS", long_options, &long_opt_index)) != -1) { switch (c) { case 'v': PrintBanner(std::cerr); @@ -80,9 +78,6 @@ int main(int argc, char *argv[]) case 'A': solveAgent = true; break; - case 'P': - bySubgames = true; - break; case 'h': PrintHelp(argv[0]); break; @@ -91,7 +86,7 @@ int main(int argc, char *argv[]) break; case '?': if (isprint(optopt)) { - std::cerr << argv[0] << ": Unknown option `-" << ((char)optopt) << "'.\n"; + std::cerr << argv[0] << ": Unknown option `-" << static_cast(optopt) << "'.\n"; } else { std::cerr << argv[0] << ": Unknown option character `\\x" << optopt << "`.\n"; @@ -121,50 +116,17 @@ int main(int argc, char *argv[]) try { const Game game = ReadGame(*input_stream); - std::shared_ptr> renderer; + std::shared_ptr> renderer; if (reportStrategic || !game->IsTree()) { - if (printDetail) { - renderer = std::make_shared>(std::cout); - } - else { - renderer = std::make_shared>(std::cout); - } + renderer = MakeMixedStrategyProfileRenderer(std::cout, 0, printDetail); } else { - if (printDetail) { - renderer = std::make_shared>(std::cout); - } - else { - renderer = std::make_shared>(std::cout); - } + renderer = MakeMixedBehaviorProfileRenderer(std::cout, 0, printDetail); } - if (game->IsTree()) { - if (bySubgames) { - BehaviorSolverType func; - if (solveAgent) { - func = [&](const Game &g) { return EnumPureAgentSolve(g); }; - } - else { - func = [&](const Game &g) { - return ToMixedBehaviorProfile(EnumPureStrategySolve(g)); - }; - } - SolveBySubgames(game, func, - [&](const MixedBehaviorProfile &p, - const std::string &label) { renderer->Render(p, label); }); - } - else { - if (solveAgent) { - EnumPureAgentSolve(game, [&](const MixedBehaviorProfile &p, - const std::string &label) { renderer->Render(p, label); }); - } - else { - EnumPureStrategySolve(game, - [&](const MixedStrategyProfile &p, - const std::string &label) { renderer->Render(p, label); }); - } - } + if (game->IsTree() && solveAgent) { + EnumPureAgentSolve(game, [&](const MixedBehaviorProfile &p, + const std::string &label) { renderer->Render(p, label); }); } else { EnumPureStrategySolve(game, [&](const MixedStrategyProfile &p, diff --git a/src/tools/gt/nfggnm.cc b/src/tools/gt/nfggnm.cc index 1e449eea5..4f1eb3623 100644 --- a/src/tools/gt/nfggnm.cc +++ b/src/tools/gt/nfggnm.cc @@ -81,10 +81,10 @@ int main(int argc, char *argv[]) std::string startFile; int long_opt_index = 0; - struct option long_options[] = {{"help", 0, nullptr, 'h'}, - {"version", 0, nullptr, 'v'}, - {"verbose", 0, nullptr, 'V'}, - {nullptr, 0, nullptr, 0}}; + option long_options[] = {{"help", 0, nullptr, 'h'}, + {"version", 0, nullptr, 'v'}, + {"verbose", 0, nullptr, 'V'}, + {nullptr, 0, nullptr, 0}}; int c; while ((c = getopt_long(argc, argv, "d:n:s:m:f:i:c:qvVhS", long_options, &long_opt_index)) != -1) { @@ -126,7 +126,7 @@ int main(int argc, char *argv[]) break; case '?': if (isprint(optopt)) { - std::cerr << argv[0] << ": Unknown option `-" << ((char)optopt) << "'.\n"; + std::cerr << argv[0] << ": Unknown option `-" << static_cast(optopt) << "'.\n"; } else { std::cerr << argv[0] << ": Unknown option character `\\x" << optopt << "`.\n"; @@ -173,9 +173,7 @@ int main(int argc, char *argv[]) try { const Game game = ReadGame(*input_stream); - const std::shared_ptr> renderer( - new MixedStrategyCSVRenderer(std::cout, numDecimals)); - + auto renderer = MakeMixedStrategyProfileRenderer(std::cout, numDecimals, false); List> perts; if (!startFile.empty()) { std::ifstream startPerts(startFile.c_str()); diff --git a/src/tools/gt/nfgipa.cc b/src/tools/gt/nfgipa.cc index efc648c07..18954dedf 100644 --- a/src/tools/gt/nfgipa.cc +++ b/src/tools/gt/nfgipa.cc @@ -67,7 +67,7 @@ int main(int argc, char *argv[]) const std::string startFile; int long_opt_index = 0; - struct option long_options[] = { + option long_options[] = { {"help", 0, nullptr, 'h'}, {"version", 0, nullptr, 'v'}, {nullptr, 0, nullptr, 0}}; int c; while ((c = getopt_long(argc, argv, "d:n:vqh", long_options, &long_opt_index)) != -1) { @@ -89,7 +89,7 @@ int main(int argc, char *argv[]) break; case '?': if (isprint(optopt)) { - std::cerr << argv[0] << ": Unknown option `-" << ((char)optopt) << "'.\n"; + std::cerr << argv[0] << ": Unknown option `-" << static_cast(optopt) << "'.\n"; } else { std::cerr << argv[0] << ": Unknown option character `\\x" << optopt << "`.\n"; @@ -119,8 +119,7 @@ int main(int argc, char *argv[]) try { const Game game = ReadGame(*input_stream); - const std::shared_ptr> renderer( - new MixedStrategyCSVRenderer(std::cout, numDecimals)); + auto renderer = MakeMixedStrategyProfileRenderer(std::cout, numDecimals, false); List> perts; if (!startFile.empty()) { diff --git a/src/tools/lcp/lcp.cc b/src/tools/lcp/lcp.cc index 76eba3aec..b67e376c1 100644 --- a/src/tools/lcp/lcp.cc +++ b/src/tools/lcp/lcp.cc @@ -22,7 +22,6 @@ #include #include -#include #include #include #include "gambit.h" @@ -50,7 +49,6 @@ void PrintHelp(char *progname) std::cerr << " -d DECIMALS compute using floating-point arithmetic;\n"; std::cerr << " display results with DECIMALS digits\n"; std::cerr << " -S use strategic game\n"; - std::cerr << " -P find only subgame-perfect equilibria\n"; std::cerr << " -e EQA terminate after finding EQA equilibria\n"; std::cerr << " (default is to find all accessible equilbria)\n"; std::cerr << " -r DEPTH terminate recursion at DEPTH\n"; @@ -65,14 +63,14 @@ void PrintHelp(char *progname) int main(int argc, char *argv[]) { int c; - bool useFloat = false, useStrategic = false, bySubgames = false, quiet = false; + bool useFloat = false, useStrategic = false, quiet = false; bool printDetail = false; int numDecimals = 6, stopAfter = 0, maxDepth = 0; int long_opt_index = 0; - struct option long_options[] = { + option long_options[] = { {"help", 0, nullptr, 'h'}, {"version", 0, nullptr, 'v'}, {nullptr, 0, nullptr, 0}}; - while ((c = getopt_long(argc, argv, "d:DvhqSPe:r:", long_options, &long_opt_index)) != -1) { + while ((c = getopt_long(argc, argv, "d:DvhqSe:r:", long_options, &long_opt_index)) != -1) { switch (c) { case 'v': PrintBanner(std::cerr); @@ -99,12 +97,9 @@ int main(int argc, char *argv[]) case 'S': useStrategic = true; break; - case 'P': - bySubgames = true; - break; case '?': if (isprint(optopt)) { - std::cerr << argv[0] << ": Unknown option `-" << ((char)optopt) << "'.\n"; + std::cerr << argv[0] << ": Unknown option `-" << static_cast(optopt) << "'.\n"; } else { std::cerr << argv[0] << ": Unknown option character `\\x" << optopt << "`.\n"; @@ -136,102 +131,34 @@ int main(int argc, char *argv[]) const Game game = ReadGame(*input_stream); if (!game->IsTree() || useStrategic) { if (useFloat) { - std::shared_ptr> renderer; - if (printDetail) { - renderer = std::make_shared>(std::cout, numDecimals); - } - else { - renderer = std::make_shared>(std::cout, numDecimals); - } + auto renderer = + MakeMixedStrategyProfileRenderer(std::cout, numDecimals, printDetail); LcpStrategySolve(game, stopAfter, maxDepth, [&](const MixedStrategyProfile &p, const std::string &label) { renderer->Render(p, label); }); } else { - std::shared_ptr> renderer; - if (printDetail) { - renderer = std::make_shared>(std::cout); - } - else { - renderer = std::make_shared>(std::cout); - } + auto renderer = + MakeMixedStrategyProfileRenderer(std::cout, numDecimals, printDetail); LcpStrategySolve(game, stopAfter, maxDepth, [&](const MixedStrategyProfile &p, const std::string &label) { renderer->Render(p, label); }); } } else { - if (!bySubgames) { - if (useFloat) { - std::shared_ptr> renderer; - if (printDetail) { - renderer = - std::make_shared>(std::cout, numDecimals); - } - else { - renderer = std::make_shared>(std::cout, numDecimals); - } - LcpBehaviorSolve(game, stopAfter, maxDepth, - [&](const MixedBehaviorProfile &p, - const std::string &label) { renderer->Render(p, label); }); - } - else { - std::shared_ptr> renderer; - if (printDetail) { - renderer = std::make_shared>(std::cout); - } - else { - renderer = std::make_shared>(std::cout); - } - LcpBehaviorSolve( - game, stopAfter, maxDepth, - [&](const MixedBehaviorProfile &p, const std::string &label) { - renderer->Render(p, label); - }); - } + if (useFloat) { + auto renderer = + MakeMixedBehaviorProfileRenderer(std::cout, numDecimals, printDetail); + LcpBehaviorSolve(game, stopAfter, maxDepth, + [&](const MixedBehaviorProfile &p, + const std::string &label) { renderer->Render(p, label); }); } else { - if (useFloat) { - std::shared_ptr> renderer; - if (printDetail) { - renderer = - std::make_shared>(std::cout, numDecimals); - } - else { - renderer = std::make_shared>(std::cout, numDecimals); - } - const BehaviorSolverType func = [&](const Game &g) { - return LcpBehaviorSolve( - g, stopAfter, maxDepth, - [&](const MixedBehaviorProfile &p, const std::string &label) { - renderer->Render(p, label); - }); - }; - SolveBySubgames(game, func, - [&](const MixedBehaviorProfile &p, - const std::string &label) { renderer->Render(p, label); }); - } - else { - std::shared_ptr> renderer; - if (printDetail) { - renderer = - std::make_shared>(std::cout, numDecimals); - } - else { - renderer = - std::make_shared>(std::cout, numDecimals); - } - const BehaviorSolverType func = [&](const Game &g) { - return LcpBehaviorSolve( - g, stopAfter, maxDepth, - [&](const MixedBehaviorProfile &p, const std::string &label) { - renderer->Render(p, label); - }); - }; - SolveBySubgames(game, func, - [&](const MixedBehaviorProfile &p, - const std::string &label) { renderer->Render(p, label); }); - } + auto renderer = + MakeMixedBehaviorProfileRenderer(std::cout, numDecimals, printDetail); + LcpBehaviorSolve(game, stopAfter, maxDepth, + [&](const MixedBehaviorProfile &p, + const std::string &label) { renderer->Render(p, label); }); } } return 0; diff --git a/src/tools/liap/liap.cc b/src/tools/liap/liap.cc index 4baebb0cc..ca4e0c186 100644 --- a/src/tools/liap/liap.cc +++ b/src/tools/liap/liap.cc @@ -22,7 +22,6 @@ #include #include -#include #include #include "gambit.h" #include "tools/util.h" @@ -137,10 +136,10 @@ int main(int argc, char *argv[]) std::string startFile; int long_opt_index = 0; - struct option long_options[] = {{"help", 0, nullptr, 'h'}, - {"version", 0, nullptr, 'v'}, - {"verbose", 0, nullptr, 'V'}, - {nullptr, 0, nullptr, 0}}; + option long_options[] = {{"help", 0, nullptr, 'h'}, + {"version", 0, nullptr, 'v'}, + {"verbose", 0, nullptr, 'V'}, + {nullptr, 0, nullptr, 0}}; int c; while ((c = getopt_long(argc, argv, "d:n:i:s:m:hqVvS", long_options, &long_opt_index)) != -1) { switch (c) { @@ -173,7 +172,7 @@ int main(int argc, char *argv[]) break; case '?': if (isprint(optopt)) { - std::cerr << argv[0] << ": Unknown option `-" << ((char)optopt) << "'.\n"; + std::cerr << argv[0] << ": Unknown option `-" << static_cast(optopt) << "'.\n"; } else { std::cerr << argv[0] << ": Unknown option character `\\x" << optopt << "`.\n"; @@ -215,9 +214,7 @@ int main(int argc, char *argv[]) } for (size_t i = 1; i <= starts.size(); i++) { - const std::shared_ptr> renderer( - new MixedStrategyCSVRenderer(std::cout, numDecimals)); - + auto renderer = MakeMixedStrategyProfileRenderer(std::cout, numDecimals, false); LiapStrategySolve(starts[i], maxregret, maxitsN, [renderer, verbose](const MixedStrategyProfile &p_profile, const std::string &p_label) { @@ -239,8 +236,7 @@ int main(int argc, char *argv[]) } for (size_t i = 1; i <= starts.size(); i++) { - const std::shared_ptr> renderer( - new BehavStrategyCSVRenderer(std::cout, numDecimals)); + auto renderer = MakeMixedBehaviorProfileRenderer(std::cout, numDecimals, false); LiapBehaviorSolve(starts[i], maxregret, maxitsN, [renderer, verbose](const MixedBehaviorProfile &p_profile, const std::string &p_label) { diff --git a/src/tools/logit/logit.cc b/src/tools/logit/logit.cc index b8663ef7d..217750185 100644 --- a/src/tools/logit/logit.cc +++ b/src/tools/logit/logit.cc @@ -23,7 +23,6 @@ #include #include -#include #include #include "gambit.h" #include "solvers/logit/logit.h" @@ -120,7 +119,7 @@ int main(int argc, char *argv[]) int decimals = 6; int long_opt_index = 0; - struct option long_options[] = { + option long_options[] = { {"help", 0, nullptr, 'h'}, {"version", 0, nullptr, 'v'}, {nullptr, 0, nullptr, 0}}; int c; while ((c = getopt_long(argc, argv, "d:s:a:m:vqehSL:p:l:", long_options, &long_opt_index)) != @@ -161,7 +160,7 @@ int main(int argc, char *argv[]) break; case '?': if (isprint(optopt)) { - std::cerr << argv[0] << ": Unknown option `-" << ((char)optopt) << "'.\n"; + std::cerr << argv[0] << ": Unknown option `-" << static_cast(optopt) << "'.\n"; } else { std::cerr << argv[0] << ": Unknown option character `\\x" << optopt << "`.\n"; diff --git a/src/tools/lp/lp.cc b/src/tools/lp/lp.cc index a567e1de6..0e13f7c9f 100644 --- a/src/tools/lp/lp.cc +++ b/src/tools/lp/lp.cc @@ -23,9 +23,9 @@ #include #include -#include #include #include + #include "gambit.h" #include "tools/util.h" #include "solvers/lp/lp.h" @@ -51,7 +51,6 @@ void PrintHelp(char *progname) std::cerr << " -d DECIMALS compute using floating-point arithmetic;\n"; std::cerr << " display results with DECIMALS digits\n"; std::cerr << " -S use strategic game\n"; - std::cerr << " -P find only subgame-perfect equilibria\n"; std::cerr << " -h, --help print this help message\n"; std::cerr << " -q quiet mode (suppresses banner)\n"; std::cerr << " -v, --version print version information\n"; @@ -63,12 +62,11 @@ int main(int argc, char *argv[]) int c; int numDecimals = 6; bool useFloat = false, useStrategic = false, quiet = false, printDetail = false; - bool bySubgames = false; int long_opt_index = 0; - struct option long_options[] = { + option long_options[] = { {"help", 0, nullptr, 'h'}, {"version", 0, nullptr, 'v'}, {nullptr, 0, nullptr, 0}}; - while ((c = getopt_long(argc, argv, "d:DvqhSP", long_options, &long_opt_index)) != -1) { + while ((c = getopt_long(argc, argv, "d:DvqhS", long_options, &long_opt_index)) != -1) { switch (c) { case 'v': PrintBanner(std::cerr); @@ -89,12 +87,9 @@ int main(int argc, char *argv[]) case 'S': useStrategic = true; break; - case 'P': - bySubgames = true; - break; case '?': if (isprint(optopt)) { - std::cerr << argv[0] << ": Unknown option `-" << ((char)optopt) << "'.\n"; + std::cerr << argv[0] << ": Unknown option `-" << static_cast(optopt) << "'.\n"; } else { std::cerr << argv[0] << ": Unknown option character `\\x" << optopt << "`.\n"; @@ -123,101 +118,37 @@ int main(int argc, char *argv[]) } try { - const Gambit::Game game = Gambit::ReadGame(*input_stream); + const Game game = ReadGame(*input_stream); if (!game->IsTree() || useStrategic) { if (useFloat) { - std::shared_ptr> renderer; - if (printDetail) { - renderer = std::make_shared>(std::cout, numDecimals); - } - else { - renderer = std::make_shared>(std::cout, numDecimals); - } + auto renderer = + MakeMixedStrategyProfileRenderer(std::cout, numDecimals, printDetail); LpStrategySolve(game, [&](const MixedStrategyProfile &p, const std::string &label) { renderer->Render(p, label); }); } else { - std::shared_ptr> renderer; - if (printDetail) { - renderer = std::make_shared>(std::cout); - } - else { - renderer = std::make_shared>(std::cout); - } + auto renderer = + MakeMixedStrategyProfileRenderer(std::cout, numDecimals, printDetail); LpStrategySolve(game, [&](const MixedStrategyProfile &p, const std::string &label) { renderer->Render(p, label); }); } } else { - if (!bySubgames) { - if (useFloat) { - std::shared_ptr> renderer; - if (printDetail) { - renderer = - std::make_shared>(std::cout, numDecimals); - } - else { - renderer = std::make_shared>(std::cout, numDecimals); - } - LpBehaviorSolve(game, - [&](const MixedBehaviorProfile &p, - const std::string &label) { renderer->Render(p, label); }); - } - else { - std::shared_ptr> renderer; - if (printDetail) { - renderer = std::make_shared>(std::cout); - } - else { - renderer = std::make_shared>(std::cout); - } - LpBehaviorSolve(game, - [&](const MixedBehaviorProfile &p, - const std::string &label) { renderer->Render(p, label); }); - } + if (useFloat) { + auto renderer = + MakeMixedBehaviorProfileRenderer(std::cout, numDecimals, printDetail); + LpBehaviorSolve(game, + [&](const MixedBehaviorProfile &p, + const std::string &label) { renderer->Render(p, label); }); } else { - if (useFloat) { - std::shared_ptr> renderer; - if (printDetail) { - renderer = - std::make_shared>(std::cout, numDecimals); - } - else { - renderer = std::make_shared>(std::cout, numDecimals); - } - const BehaviorSolverType func = [&](const Game &g) { - return LpBehaviorSolve( - g, [&](const MixedBehaviorProfile &p, const std::string &label) { - renderer->Render(p, label); - }); - }; - SolveBySubgames(game, func, - [&](const MixedBehaviorProfile &p, + auto renderer = + MakeMixedBehaviorProfileRenderer(std::cout, numDecimals, printDetail); + LpBehaviorSolve(game, + [&](const MixedBehaviorProfile &p, const std::string &label) { renderer->Render(p, label); }); - } - else { - std::shared_ptr> renderer; - if (printDetail) { - renderer = - std::make_shared>(std::cout, numDecimals); - } - else { - renderer = - std::make_shared>(std::cout, numDecimals); - } - const BehaviorSolverType func = [&](const Game &g) { - return LpBehaviorSolve( - g, [&](const MixedBehaviorProfile &p, const std::string &label) { - renderer->Render(p, label); - }); - }; - SolveBySubgames(game, func, - [&](const MixedBehaviorProfile &p, - const std::string &label) { renderer->Render(p, label); }); - } } } return 0; diff --git a/src/tools/simpdiv/nfgsimpdiv.cc b/src/tools/simpdiv/nfgsimpdiv.cc index 60239929e..3105486f8 100644 --- a/src/tools/simpdiv/nfgsimpdiv.cc +++ b/src/tools/simpdiv/nfgsimpdiv.cc @@ -21,7 +21,6 @@ // #include -#include #include #include #include "gambit.h" @@ -66,7 +65,7 @@ List> RandomProfiles(const Game &p_game, int p_co return profiles; } -class MixedStrategyCSVAsFloatRenderer : public MixedStrategyRenderer { +class MixedStrategyCSVAsFloatRenderer final : public MixedStrategyProfileRenderer { public: explicit MixedStrategyCSVAsFloatRenderer(std::ostream &p_stream, int p_numDecimals = 6) : m_stream(p_stream), m_numDecimals(p_numDecimals) @@ -174,7 +173,7 @@ int main(int argc, char *argv[]) break; case '?': if (isprint(optopt)) { - std::cerr << argv[0] << ": Unknown option `-" << ((char)optopt) << "'.\n"; + std::cerr << argv[0] << ": Unknown option `-" << static_cast(optopt) << "'.\n"; } else { std::cerr << argv[0] << ": Unknown option character `\\x" << optopt << "`.\n"; @@ -231,7 +230,7 @@ int main(int argc, char *argv[]) }); } else { - auto renderer = std::make_shared>(std::cout); + auto renderer = std::make_shared>(std::cout); SimpdivStrategySolve( start, maxregret, gridResize, 0, [&](const MixedStrategyProfile &p, const std::string &label) { diff --git a/src/tools/util.h b/src/tools/util.h index 56419b188..fd3a82a4d 100644 --- a/src/tools/util.h +++ b/src/tools/util.h @@ -27,9 +27,9 @@ namespace Gambit { -template class StrategyProfileRenderer { +template class MixedProfileRenderer { public: - virtual ~StrategyProfileRenderer() = default; + virtual ~MixedProfileRenderer() = default; virtual void Render(const MixedStrategyProfile &p_profile, const std::string &p_label = "NE") const = 0; virtual void Render(const MixedBehaviorProfile &p_profile, @@ -41,9 +41,9 @@ template class StrategyProfileRenderer { // Implements automatic conversion of behavior strategy profiles to mixed // strategy profiles. // -template class MixedStrategyRenderer : public StrategyProfileRenderer { +template class MixedStrategyProfileRenderer : public MixedProfileRenderer { public: - ~MixedStrategyRenderer() override = default; + ~MixedStrategyProfileRenderer() override = default; void Render(const MixedStrategyProfile &p_profile, const std::string &p_label = "NE") const override = 0; void Render(const MixedBehaviorProfile &p_profile, @@ -53,13 +53,14 @@ template class MixedStrategyRenderer : public StrategyProfileRenderer< } }; -template class MixedStrategyCSVRenderer : public MixedStrategyRenderer { +template +class MixedStrategyProfileCSVRenderer final : public MixedStrategyProfileRenderer { public: - explicit MixedStrategyCSVRenderer(std::ostream &p_stream, int p_numDecimals = 6) + explicit MixedStrategyProfileCSVRenderer(std::ostream &p_stream, int p_numDecimals = 6) : m_stream(p_stream), m_numDecimals(p_numDecimals) { } - ~MixedStrategyCSVRenderer() override = default; + ~MixedStrategyProfileCSVRenderer() override = default; void Render(const MixedStrategyProfile &p_profile, const std::string &p_label = "NE") const override; @@ -68,13 +69,14 @@ template class MixedStrategyCSVRenderer : public MixedStrategyRenderer int m_numDecimals; }; -template class MixedStrategyDetailRenderer : public MixedStrategyRenderer { +template +class MixedStrategyProfileDetailRenderer final : public MixedStrategyProfileRenderer { public: - explicit MixedStrategyDetailRenderer(std::ostream &p_stream, int p_numDecimals = 6) + explicit MixedStrategyProfileDetailRenderer(std::ostream &p_stream, int p_numDecimals = 6) : m_stream(p_stream), m_numDecimals(p_numDecimals) { } - ~MixedStrategyDetailRenderer() override = default; + ~MixedStrategyProfileDetailRenderer() override = default; void Render(const MixedStrategyProfile &p_profile, const std::string &p_label = "NE") const override; @@ -83,12 +85,22 @@ template class MixedStrategyDetailRenderer : public MixedStrategyRende int m_numDecimals; }; +template +std::shared_ptr> +MakeMixedStrategyProfileRenderer(std::ostream &p_stream, int p_numDecimals, bool p_printDetail) +{ + if (p_printDetail) { + return std::make_shared>(p_stream, p_numDecimals); + } + return std::make_shared>(p_stream, p_numDecimals); +} + // -// Encapsulates the rendering of a behavior profile to various text formats. +// Encapsulates the rendering of a mixed behavior profile to various text formats. // -template class BehavStrategyRenderer : public StrategyProfileRenderer { +template class MixedBehaviorProfileRenderer : public MixedProfileRenderer { public: - ~BehavStrategyRenderer() override = default; + ~MixedBehaviorProfileRenderer() override = default; void Render(const MixedStrategyProfile &p_profile, const std::string &p_label = "NE") const override { @@ -98,13 +110,14 @@ template class BehavStrategyRenderer : public StrategyProfileRenderer< const std::string &p_label = "NE") const override = 0; }; -template class BehavStrategyCSVRenderer : public BehavStrategyRenderer { +template +class MixedBehaviorProfileCSVRenderer final : public MixedBehaviorProfileRenderer { public: - explicit BehavStrategyCSVRenderer(std::ostream &p_stream, int p_numDecimals = 6) + explicit MixedBehaviorProfileCSVRenderer(std::ostream &p_stream, int p_numDecimals = 6) : m_stream(p_stream), m_numDecimals(p_numDecimals) { } - ~BehavStrategyCSVRenderer() override = default; + ~MixedBehaviorProfileCSVRenderer() override = default; void Render(const MixedBehaviorProfile &p_profile, const std::string &p_label = "NE") const override; @@ -113,13 +126,14 @@ template class BehavStrategyCSVRenderer : public BehavStrategyRenderer int m_numDecimals; }; -template class BehavStrategyDetailRenderer : public BehavStrategyRenderer { +template +class MixedBehaviorProfileDetailRenderer final : public MixedBehaviorProfileRenderer { public: - explicit BehavStrategyDetailRenderer(std::ostream &p_stream, int p_numDecimals = 6) + explicit MixedBehaviorProfileDetailRenderer(std::ostream &p_stream, int p_numDecimals = 6) : m_stream(p_stream), m_numDecimals(p_numDecimals) { } - ~BehavStrategyDetailRenderer() override = default; + ~MixedBehaviorProfileDetailRenderer() override = default; void Render(const MixedBehaviorProfile &p_profile, const std::string &p_label = "NE") const override; @@ -129,8 +143,18 @@ template class BehavStrategyDetailRenderer : public BehavStrategyRende }; template -void MixedStrategyCSVRenderer::Render(const MixedStrategyProfile &p_profile, - const std::string &p_label) const +std::shared_ptr> +MakeMixedBehaviorProfileRenderer(std::ostream &p_stream, int p_numDecimals, bool p_printDetail) +{ + if (p_printDetail) { + return std::make_shared>(p_stream, p_numDecimals); + } + return std::make_shared>(p_stream, p_numDecimals); +} + +template +void MixedStrategyProfileCSVRenderer::Render(const MixedStrategyProfile &p_profile, + const std::string &p_label) const { m_stream << p_label; for (size_t i = 1; i <= p_profile.MixedProfileLength(); i++) { @@ -140,8 +164,8 @@ void MixedStrategyCSVRenderer::Render(const MixedStrategyProfile &p_profil } template -void MixedStrategyDetailRenderer::Render(const MixedStrategyProfile &p_profile, - const std::string &p_label) const +void MixedStrategyProfileDetailRenderer::Render(const MixedStrategyProfile &p_profile, + const std::string &p_label) const { for (auto player : p_profile.GetGame()->GetPlayers()) { m_stream << "Strategy profile for player " << player->GetNumber() << ":\n"; @@ -167,8 +191,8 @@ void MixedStrategyDetailRenderer::Render(const MixedStrategyProfile &p_pro } template -void BehavStrategyCSVRenderer::Render(const MixedBehaviorProfile &p_profile, - const std::string &p_label) const +void MixedBehaviorProfileCSVRenderer::Render(const MixedBehaviorProfile &p_profile, + const std::string &p_label) const { m_stream << p_label; for (size_t i = 1; i <= p_profile.BehaviorProfileLength(); i++) { @@ -178,8 +202,8 @@ void BehavStrategyCSVRenderer::Render(const MixedBehaviorProfile &p_profil } template -void BehavStrategyDetailRenderer::Render(const MixedBehaviorProfile &p_profile, - const std::string &p_label) const +void MixedBehaviorProfileDetailRenderer::Render(const MixedBehaviorProfile &p_profile, + const std::string &p_label) const { for (auto player : p_profile.GetGame()->GetPlayers()) { m_stream << "Behavior profile for player " << player->GetNumber() << ":\n";