diff --git a/Makefile.am b/Makefile.am index fe3fc232f..d698afb1c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -299,7 +299,6 @@ game_SOURCES = \ src/games/behavmixed.h \ src/games/stratspt.cc \ src/games/stratspt.h \ - src/games/stratpure.cc \ src/games/stratpure.h \ src/games/stratmixed.h \ src/games/file.cc \ diff --git a/src/games/behavmixed.cc b/src/games/behavmixed.cc index 7fb996ef0..36b0df22c 100644 --- a/src/games/behavmixed.cc +++ b/src/games/behavmixed.cc @@ -252,6 +252,7 @@ template MixedBehaviorProfile MixedBehaviorProfile::ToFullSuppor template T MixedBehaviorProfile::GetLiapValue() const { + m_support.GetGame()->BuildComputedValues(); return MixedStrategyProfile(*this).GetLiapValue(); } @@ -356,6 +357,7 @@ template T MixedBehaviorProfile::GetRegret(const GameInfoset &p_inf template T MixedBehaviorProfile::GetMaxRegret() const { + m_support.GetGame()->BuildComputedValues(); return MixedStrategyProfile(*this).GetMaxRegret(); } @@ -553,6 +555,7 @@ template bool MixedBehaviorProfile::IsDefinedAt(GameInfoset p_infos template MixedStrategyProfile MixedBehaviorProfile::ToMixedProfile() const { CheckVersion(); + m_support.GetGame()->BuildComputedValues(); return MixedStrategyProfile(*this); } diff --git a/src/games/file.cc b/src/games/file.cc index d9b374315..5ec77fec3 100644 --- a/src/games/file.cc +++ b/src/games/file.cc @@ -411,11 +411,10 @@ void ReadOutcomeList(GameFileLexer &p_parser, Game &p_nfg) void ParseOutcomeBody(GameFileLexer &p_parser, Game &p_nfg) { ReadOutcomeList(p_parser, p_nfg); - const StrategySupportProfile profile(p_nfg); - for (auto iter : StrategyContingencies(profile)) { + for (const auto &profile : StrategyContingencies(p_nfg)) { p_parser.ExpectCurrentToken(TOKEN_NUMBER, "outcome index"); if (const int outcomeId = std::stoi(p_parser.GetLastText())) { - iter->SetOutcome(p_nfg->GetOutcome(outcomeId)); + profile->SetOutcome(p_nfg->GetOutcome(outcomeId)); } p_parser.GetNextToken(); } @@ -423,11 +422,10 @@ void ParseOutcomeBody(GameFileLexer &p_parser, Game &p_nfg) void ParsePayoffBody(GameFileLexer &p_parser, Game &p_nfg) { - const StrategySupportProfile profile(p_nfg); - for (auto iter : StrategyContingencies(profile)) { + for (const auto &profile : StrategyContingencies(p_nfg)) { for (const auto &player : p_nfg->GetPlayers()) { p_parser.ExpectCurrentToken(TOKEN_NUMBER, "numerical payoff"); - iter->GetOutcome()->SetPayoff(player, Number(p_parser.GetLastText())); + profile->GetOutcome()->SetPayoff(player, Number(p_parser.GetLastText())); p_parser.GetNextToken(); } } diff --git a/src/games/game.cc b/src/games/game.cc index af6653395..c33e1840e 100644 --- a/src/games/game.cc +++ b/src/games/game.cc @@ -182,6 +182,24 @@ GameRep::~GameRep() } } +void GameRep::IndexStrategies() const +{ + const size_t n = m_players.size(); + m_pureStrategies.m_radices.resize(n); + m_pureStrategies.m_strides.resize(n); + + long stride = 1L; + for (size_t i = 0; i < n; ++i) { + const auto &player = m_players[i]; + m_pureStrategies.m_strides[i] = stride; + m_pureStrategies.m_radices[i] = player->m_strategies.size(); + for (auto [st, strategy] : enumerate(player->m_strategies)) { + strategy->m_number = st + 1; + } + stride *= m_pureStrategies.m_radices[i]; + } +} + //------------------------------------------------------------------------ // GameRep: Writing data files //------------------------------------------------------------------------ @@ -405,6 +423,16 @@ template T MixedStrategyProfile::GetMaxRegret() const [this](const auto &player) -> T { return this->GetRegret(player); }); } +MixedStrategyProfile PureStrategyProfileRep::ToMixedStrategyProfile() const +{ + auto temp = m_game->NewMixedStrategyProfile(Rational(0)); + temp = Rational(0); + for (const auto &player : m_game->GetPlayers()) { + temp[GetStrategy(player)] = Rational(1); + } + return temp; +} + template class MixedStrategyProfileRep; template class MixedStrategyProfileRep; diff --git a/src/games/game.h b/src/games/game.h index 66cf5ae00..420d072bd 100644 --- a/src/games/game.h +++ b/src/games/game.h @@ -267,6 +267,7 @@ class GameInfosetRep : public std::enable_shared_from_this { /// relatively efficient. class GameStrategyRep : public std::enable_shared_from_this { friend class GameExplicitRep; + friend class GameRep; friend class GameTreeRep; friend class GameTableRep; friend class GameAGGRep; @@ -283,7 +284,6 @@ class GameStrategyRep : public std::enable_shared_from_this { bool m_valid{true}; GamePlayerRep *m_player; int m_number; - long m_offset{-1L}; std::string m_label; std::map m_behav; @@ -360,6 +360,7 @@ class GamePlayerRep : public std::enable_shared_from_this { friend class GameInfosetRep; friend class GameNodeRep; friend class StrategySupportProfile; + friend class PureStrategyProfileRep; template friend class MixedBehaviorProfile; template friend class MixedStrategyProfile; @@ -594,6 +595,24 @@ inline GameNode GameNodeRep::Actions::iterator::GetOwner() const { return m_chil enum class TraversalOrder { Preorder, Postorder }; +class CartesianProductSpace { +public: + std::vector m_radices; + std::vector m_strides; +}; + +class CartesianSubset { +public: + const CartesianProductSpace *m_space{nullptr}; + std::vector> m_allowedDigits; +}; + +template class CartesianTensor { +public: + const CartesianProductSpace *m_space{nullptr}; + std::vector m_data; +}; + /// This is the class for representing an arbitrary finite game. class GameRep : public std::enable_shared_from_this { friend class GameOutcomeRep; @@ -602,6 +621,7 @@ class GameRep : public std::enable_shared_from_this { friend class GamePlayerRep; friend class PureStrategyProfileRep; friend class TablePureStrategyProfileRep; + friend class StrategySupportProfile; template friend class MixedBehaviorProfile; template friend class MixedStrategyProfile; template friend class TableMixedStrategyProfileRep; @@ -609,6 +629,7 @@ class GameRep : public std::enable_shared_from_this { protected: std::vector> m_players; std::vector> m_outcomes; + mutable CartesianProductSpace m_pureStrategies; std::string m_title, m_comment; unsigned int m_version{0}; @@ -618,6 +639,7 @@ class GameRep : public std::enable_shared_from_this { //@{ /// Mark that the content of the game has changed void IncrementVersion() { m_version++; } + void IndexStrategies() const; //@} /// Hooks for derived classes to update lazily-computed orderings if required diff --git a/src/games/gameagg.cc b/src/games/gameagg.cc index 37b5551d0..ccdda7d17 100644 --- a/src/games/gameagg.cc +++ b/src/games/gameagg.cc @@ -51,10 +51,10 @@ class AGGPureStrategyProfileRep : public PureStrategyProfileRep { Rational AGGPureStrategyProfileRep::GetPayoff(const GamePlayer &p_player) const { - const std::shared_ptr aggPtr = dynamic_cast(*m_nfg).aggPtr; + const std::shared_ptr aggPtr = dynamic_cast(*m_game).aggPtr; std::vector s(aggPtr->getNumPlayers()); for (int i = 1; i <= aggPtr->getNumPlayers(); i++) { - s[i - 1] = m_profile.at(m_nfg->GetPlayer(i))->GetNumber() - 1; + s[i - 1] = GetStrategy(m_game->GetPlayer(i))->GetNumber() - 1; } return Rational(aggPtr->getPurePayoff(p_player->GetNumber() - 1, s)); } @@ -62,10 +62,10 @@ Rational AGGPureStrategyProfileRep::GetPayoff(const GamePlayer &p_player) const Rational AGGPureStrategyProfileRep::GetStrategyValue(const GameStrategy &p_strategy) const { const int player = p_strategy->GetPlayer()->GetNumber(); - const std::shared_ptr aggPtr = dynamic_cast(*m_nfg).aggPtr; + const std::shared_ptr aggPtr = dynamic_cast(*m_game).aggPtr; std::vector s(aggPtr->getNumPlayers()); for (int i = 1; i <= aggPtr->getNumPlayers(); i++) { - s[i - 1] = m_profile.at(m_nfg->GetPlayer(i))->GetNumber() - 1; + s[i - 1] = GetStrategy(m_game->GetPlayer(i))->GetNumber() - 1; } s[player - 1] = p_strategy->GetNumber() - 1; return Rational(aggPtr->getPurePayoff(player - 1, s)); @@ -236,23 +236,14 @@ GameAGGRep::NewMixedStrategyProfile(const Rational &, const StrategySupportProfi bool GameAGGRep::IsConstSum() const { - auto profile = NewPureStrategyProfile(); - Rational sum(0); - for (const auto &player : m_players) { - sum += profile->GetPayoff(player); - } - - for (auto iter : StrategyContingencies(std::const_pointer_cast(shared_from_this()))) { - Rational newsum(0); - for (const auto &player : m_players) { - newsum += iter->GetPayoff(player); - } - if (newsum != sum) { - return false; - } - } - - return true; + auto payoff_sum = [&](const PureStrategyProfile &p) { + return sum_function(m_players, [&](const auto &player) { return p->GetPayoff(player); }); + }; + const Rational sum = payoff_sum(NewPureStrategyProfile()); + + auto contingencies = StrategyContingencies(std::const_pointer_cast(shared_from_this())); + return std::all_of(contingencies.begin(), contingencies.end(), + [&](const PureStrategyProfile &p) { return payoff_sum(p) == sum; }); } //------------------------------------------------------------------------ diff --git a/src/games/gamebagg.cc b/src/games/gamebagg.cc index e369a15a7..30ee5848f 100644 --- a/src/games/gamebagg.cc +++ b/src/games/gamebagg.cc @@ -50,12 +50,12 @@ class BAGGPureStrategyProfileRep : public PureStrategyProfileRep { Rational BAGGPureStrategyProfileRep::GetPayoff(const GamePlayer &p_player) const { - const std::shared_ptr baggPtr = dynamic_cast(*m_nfg).baggPtr; - std::vector s(m_nfg->NumPlayers()); - for (size_t i = 1; i <= m_nfg->NumPlayers(); i++) { - s[i - 1] = m_profile.at(m_nfg->GetPlayer(i))->GetNumber() - 1; + const std::shared_ptr baggPtr = dynamic_cast(*m_game).baggPtr; + std::vector s(m_game->NumPlayers()); + for (size_t i = 1; i <= m_game->NumPlayers(); i++) { + s[i - 1] = GetStrategy(m_game->GetPlayer(i))->GetNumber() - 1; } - const int bp = dynamic_cast(*m_nfg).agent2baggPlayer[p_player->GetNumber()]; + const int bp = dynamic_cast(*m_game).agent2baggPlayer[p_player->GetNumber()]; const int tp = p_player->GetNumber() - 1 - baggPtr->typeOffset[bp - 1]; return Rational(baggPtr->getPurePayoff(bp - 1, tp, s)); } @@ -63,13 +63,13 @@ Rational BAGGPureStrategyProfileRep::GetPayoff(const GamePlayer &p_player) const Rational BAGGPureStrategyProfileRep::GetStrategyValue(const GameStrategy &p_strategy) const { const int player = p_strategy->GetPlayer()->GetNumber(); - const std::shared_ptr baggPtr = dynamic_cast(*m_nfg).baggPtr; - std::vector s(m_nfg->NumPlayers()); - for (size_t i = 1; i <= m_nfg->NumPlayers(); i++) { - s[i - 1] = m_profile.at(m_nfg->GetPlayer(i))->GetNumber() - 1; + const std::shared_ptr baggPtr = dynamic_cast(*m_game).baggPtr; + std::vector s(m_game->NumPlayers()); + for (size_t i = 1; i <= m_game->NumPlayers(); i++) { + s[i - 1] = GetStrategy(m_game->GetPlayer(i))->GetNumber() - 1; } s[player - 1] = p_strategy->GetNumber() - 1; - const int bp = dynamic_cast(*m_nfg).agent2baggPlayer[player]; + const int bp = dynamic_cast(*m_game).agent2baggPlayer[player]; const int tp = player - 1 - baggPtr->typeOffset[bp - 1]; return Rational(baggPtr->getPurePayoff(bp - 1, tp, s)); } diff --git a/src/games/gametable.cc b/src/games/gametable.cc index 7087e1cb2..09356d501 100644 --- a/src/games/gametable.cc +++ b/src/games/gametable.cc @@ -34,36 +34,19 @@ namespace Gambit { class TablePureStrategyProfileRep : public PureStrategyProfileRep { protected: - long m_index{0L}; - - std::shared_ptr Copy() const override; + std::shared_ptr Copy() const override + { + return std::make_shared(*this); + } public: - explicit TablePureStrategyProfileRep(const Game &p_game); - void SetStrategy(const GameStrategy &) override; + explicit TablePureStrategyProfileRep(const Game &p_game) : PureStrategyProfileRep(p_game) {} GameOutcome GetOutcome() const override; void SetOutcome(GameOutcome p_outcome) override; Rational GetPayoff(const GamePlayer &) const override; Rational GetStrategyValue(const GameStrategy &) const override; }; -//------------------------------------------------------------------------ -// TablePureStrategyProfileRep: Lifecycle -//------------------------------------------------------------------------ - -TablePureStrategyProfileRep::TablePureStrategyProfileRep(const Game &p_nfg) - : PureStrategyProfileRep(p_nfg) -{ - for (auto [player, strategy] : m_profile) { - m_index += strategy->m_offset; - } -} - -std::shared_ptr TablePureStrategyProfileRep::Copy() const -{ - return std::make_shared(*this); -} - Game NewTable(const std::vector &p_dim, bool p_sparseOutcomes /*= false*/) { return std::make_shared(p_dim, p_sparseOutcomes); @@ -73,15 +56,9 @@ Game NewTable(const std::vector &p_dim, bool p_sparseOutcomes /*= false*/) // TablePureStrategyProfileRep: Data access and manipulation //------------------------------------------------------------------------ -void TablePureStrategyProfileRep::SetStrategy(const GameStrategy &s) -{ - m_index += s->m_offset - m_profile.at(s->GetPlayer())->m_offset; - m_profile[s->GetPlayer()] = s; -} - GameOutcome TablePureStrategyProfileRep::GetOutcome() const { - if (const auto outcome = dynamic_cast(*m_nfg).m_results[m_index]) { + if (const auto outcome = dynamic_cast(*m_game).m_results.at(m_index)) { return outcome->shared_from_this(); } return nullptr; @@ -89,12 +66,12 @@ GameOutcome TablePureStrategyProfileRep::GetOutcome() const void TablePureStrategyProfileRep::SetOutcome(GameOutcome p_outcome) { - dynamic_cast(*m_nfg).m_results[m_index] = p_outcome.get(); + dynamic_cast(*m_game).m_results[m_index] = p_outcome.get(); } Rational TablePureStrategyProfileRep::GetPayoff(const GamePlayer &p_player) const { - if (const auto outcome = dynamic_cast(*m_nfg).m_results[m_index]) { + if (const auto outcome = dynamic_cast(*m_game).m_results.at(m_index)) { return outcome->GetPayoff(p_player); } return Rational(0); @@ -103,15 +80,16 @@ Rational TablePureStrategyProfileRep::GetPayoff(const GamePlayer &p_player) cons Rational TablePureStrategyProfileRep::GetStrategyValue(const GameStrategy &p_strategy) const { const auto &player = p_strategy->GetPlayer(); - const GameOutcomeRep *outcome = - dynamic_cast(*m_nfg) - .m_results[m_index - m_profile.at(player)->m_offset + p_strategy->m_offset]; - if (outcome) { + const auto &[m_radices, m_strides] = m_game->m_pureStrategies; + const size_t index = player->GetNumber() - 1; + const long stride = m_strides[index]; + const long digit_old = (m_index / stride) % m_radices[index]; + const long digit_new = p_strategy->GetNumber() - 1; + const long new_index = m_index + (digit_new - digit_old) * stride; + if (const auto outcome = dynamic_cast(*m_game).m_results[new_index]) { return outcome->GetPayoff(player); } - else { - return Rational(0); - } + return Rational(0); } PureStrategyProfile GameTableRep::NewPureStrategyProfile() const @@ -137,6 +115,14 @@ template class TableMixedStrategyProfileRep : public MixedStrategyProf T &value) const; //@} + long StrategyOffset(const GameStrategy &s) const + { + const auto &space = this->GetSupport().GetGame()->m_pureStrategies; + const auto &player = s->GetPlayer(); + const size_t i = player->GetNumber() - 1; + return (s->GetNumber() - 1) * space.m_strides[i]; + } + public: explicit TableMixedStrategyProfileRep(const StrategySupportProfile &p_support) : MixedStrategyProfileRep(p_support) @@ -172,7 +158,7 @@ T TableMixedStrategyProfileRep::GetPayoff(int pl, int index, int current) con for (auto s : this->GetSupport().GetStrategies(this->GetSupport().GetGame()->GetPlayer(current))) { if ((*this)[s] != T(0)) { - sum += ((*this)[s] * GetPayoff(pl, index + s->m_offset, current + 1)); + sum += ((*this)[s] * GetPayoff(pl, index + StrategyOffset(s), current + 1)); } } return sum; @@ -201,7 +187,8 @@ void TableMixedStrategyProfileRep::GetPayoffDeriv(int pl, int const_pl, int c for (auto s : this->GetSupport().GetStrategies(this->GetSupport().GetGame()->GetPlayer(cur_pl))) { if ((*this)[s] > T(0)) { - GetPayoffDeriv(pl, const_pl, cur_pl + 1, index + s->m_offset, prob * (*this)[s], value); + GetPayoffDeriv(pl, const_pl, cur_pl + 1, index + StrategyOffset(s), prob * (*this)[s], + value); } } } @@ -211,7 +198,7 @@ template T TableMixedStrategyProfileRep::GetPayoffDeriv(int pl, const GameStrategy &strategy) const { T value = T(0); - GetPayoffDeriv(pl, strategy->GetPlayer()->GetNumber(), 1, strategy->m_offset, T(1), value); + GetPayoffDeriv(pl, strategy->GetPlayer()->GetNumber(), 1, StrategyOffset(strategy), T(1), value); return value; } @@ -234,7 +221,7 @@ void TableMixedStrategyProfileRep::GetPayoffDeriv(int pl, int const_pl1, int for (auto s : this->GetSupport().GetStrategies(this->GetSupport().GetGame()->GetPlayer(cur_pl))) { if ((*this)[s] > static_cast(0)) { - GetPayoffDeriv(pl, const_pl1, const_pl2, cur_pl + 1, index + s->m_offset, + GetPayoffDeriv(pl, const_pl1, const_pl2, cur_pl + 1, index + StrategyOffset(s), prob * (*this)[s], value); } } @@ -253,7 +240,7 @@ T TableMixedStrategyProfileRep::GetPayoffDeriv(int pl, const GameStrategy &st T value = T(0); GetPayoffDeriv(pl, player1->GetNumber(), player2->GetNumber(), 1, - strategy1->m_offset + strategy2->m_offset, T(1), value); + StrategyOffset(strategy1) + StrategyOffset(strategy2), T(1), value); return value; } @@ -304,23 +291,14 @@ Game GameTableRep::Copy() const bool GameTableRep::IsConstSum() const { - auto profile = NewPureStrategyProfile(); - Rational sum(0); - for (const auto &player : m_players) { - sum += profile->GetPayoff(player); - } + auto payoff_sum = [&](const PureStrategyProfile &p) { + return sum_function(m_players, [&](const auto &player) { return p->GetPayoff(player); }); + }; + const Rational sum = payoff_sum(NewPureStrategyProfile()); - for (const auto iter : - StrategyContingencies(std::const_pointer_cast(shared_from_this()))) { - Rational newsum(0); - for (const auto &player : m_players) { - newsum += iter->GetPayoff(player); - } - if (newsum != sum) { - return false; - } - } - return true; + auto contingencies = StrategyContingencies(std::const_pointer_cast(shared_from_this())); + return std::all_of(contingencies.begin(), contingencies.end(), + [&](const PureStrategyProfile &p) { return payoff_sum(p) == sum; }); } Rational GameTableRep::GetPlayerMinPayoff(const GamePlayer &p_player) const @@ -431,9 +409,13 @@ GameStrategy GameTableRep::NewStrategy(const GamePlayer &p_player, const std::st throw MismatchException(); } IncrementVersion(); + std::vector old_radices; + for (const auto &player : m_players) { + old_radices.push_back(player->m_strategies.size()); + } p_player->m_strategies.push_back(std::make_shared( p_player.get(), p_player->m_strategies.size() + 1, p_label)); - RebuildTable(); + RebuildTable(old_radices); return p_player->m_strategies.back(); } @@ -493,52 +475,37 @@ GameTableRep::NewMixedStrategyProfile(const Rational &, const StrategySupportPro /// This rebuilds a new table of outcomes after the game has been /// redimensioned (change in the number of strategies). Strategies /// numbered -1 are identified as the new strategies. -void GameTableRep::RebuildTable() +void GameTableRep::RebuildTable(const std::vector &old_radices) { - long size = 1L; - std::vector offsets; - for (const auto &player : m_players) { - offsets.push_back(size); - size *= player->m_strategies.size(); + std::vector old_strides(old_radices.size()); + long stride = 1; + for (size_t i = 0; i < old_radices.size(); ++i) { + old_strides[i] = stride; + stride *= old_radices[i]; } + const long old_size = stride; - std::vector newResults(size); - std::fill(newResults.begin(), newResults.end(), nullptr); - - for (auto iter : StrategyContingencies( - StrategySupportProfile(std::const_pointer_cast(shared_from_this())))) { - long newindex = 0L; - for (const auto &player : m_players) { - if (iter->GetStrategy(player)->m_offset < 0) { - // This is a contingency involving a new strategy... skip - newindex = -1L; - break; - } - else { - newindex += (iter->GetStrategy(player)->m_number - 1) * offsets[player->m_number - 1]; - } - } + long new_size = 1; + std::vector new_strides(m_players.size()); + for (size_t i = 0; i < m_players.size(); ++i) { + new_strides[i] = new_size; + new_size *= m_players[i]->m_strategies.size(); + } - if (newindex >= 0 && iter->GetOutcome() != nullptr) { - newResults[newindex] = iter->GetOutcome().get(); + std::vector newResults(new_size, nullptr); + for (long old_index = 0; old_index < old_size; ++old_index) { + if (m_results[old_index] == nullptr) { + continue; + } + long new_index = 0; + for (size_t i = 0; i < m_players.size(); ++i) { + const long digit = (old_index / old_strides[i]) % old_radices[i]; + new_index += digit * new_strides[i]; } + newResults[new_index] = m_results[old_index]; } m_results.swap(newResults); IndexStrategies(); } -void GameTableRep::IndexStrategies() const -{ - long offset = 1L; - for (auto player : m_players) { - int st = 1; - for (auto strategy : player->m_strategies) { - strategy->m_number = st; - strategy->m_offset = (st - 1) * offset; - st++; - } - offset *= player->m_strategies.size(); - } -} - } // end namespace Gambit diff --git a/src/games/gametable.h b/src/games/gametable.h index db7149cf7..04facee7d 100644 --- a/src/games/gametable.h +++ b/src/games/gametable.h @@ -40,8 +40,7 @@ class GameTableRep : public GameExplicitRep { /// @name Private auxiliary functions //@{ - void IndexStrategies() const; - void RebuildTable(); + void RebuildTable(const std::vector &old_radices); //@} public: diff --git a/src/games/gametree.cc b/src/games/gametree.cc index 3df2700c0..6092f138d 100644 --- a/src/games/gametree.cc +++ b/src/games/gametree.cc @@ -952,6 +952,7 @@ void GameTreeRep::BuildComputedValues() const std::map ptr, whichbranch; player->MakeReducedStrats(m_root.get(), nullptr, behav, ptr, whichbranch); } + IndexStrategies(); m_computedValues = true; } @@ -1384,7 +1385,7 @@ MixedStrategyProfile GameTreeRep::NewMixedStrategyProfile(double) const if (!IsPerfectRecall()) { throw UndefinedException("Mixed strategies not supported for games with imperfect recall."); } - EnsureInfosetOrdering(); + BuildComputedValues(); return StrategySupportProfile(std::const_pointer_cast(shared_from_this())) .NewMixedStrategyProfile(); } @@ -1394,7 +1395,7 @@ MixedStrategyProfile GameTreeRep::NewMixedStrategyProfile(const Ration if (!IsPerfectRecall()) { throw UndefinedException("Mixed strategies not supported for games with imperfect recall."); } - EnsureInfosetOrdering(); + BuildComputedValues(); return StrategySupportProfile(std::const_pointer_cast(shared_from_this())) .NewMixedStrategyProfile(); } @@ -1405,7 +1406,7 @@ GameTreeRep::NewMixedStrategyProfile(double, const StrategySupportProfile &spt) if (!IsPerfectRecall()) { throw UndefinedException("Mixed strategies not supported for games with imperfect recall."); } - EnsureInfosetOrdering(); + BuildComputedValues(); return MixedStrategyProfile(std::make_unique>(spt)); } @@ -1415,7 +1416,7 @@ GameTreeRep::NewMixedStrategyProfile(const Rational &, const StrategySupportProf if (!IsPerfectRecall()) { throw UndefinedException("Mixed strategies not supported for games with imperfect recall."); } - EnsureInfosetOrdering(); + BuildComputedValues(); return MixedStrategyProfile( std::make_unique>(spt)); } @@ -1445,7 +1446,7 @@ class TreePureStrategyProfileRep : public PureStrategyProfileRep { PureStrategyProfile GameTreeRep::NewPureStrategyProfile() const { - EnsureInfosetOrdering(); + BuildComputedValues(); return PureStrategyProfile(std::make_shared( std::const_pointer_cast(shared_from_this()))); } @@ -1456,11 +1457,11 @@ PureStrategyProfile GameTreeRep::NewPureStrategyProfile() const Rational TreePureStrategyProfileRep::GetPayoff(const GamePlayer &p_player) const { - PureBehaviorProfile behav(m_nfg); - for (const auto &player : m_nfg->GetPlayers()) { + PureBehaviorProfile behav(m_game); + for (const auto &player : m_game->GetPlayers()) { for (const auto &infoset : player->GetInfosets()) { try { - behav.SetAction(infoset->GetAction(m_profile.at(player)->m_behav[infoset.get()])); + behav.SetAction(infoset->GetAction(GetStrategy(player)->m_behav[infoset.get()])); } catch (std::out_of_range &) { } diff --git a/src/games/stratpure.cc b/src/games/stratpure.cc deleted file mode 100644 index 0cedff018..000000000 --- a/src/games/stratpure.cc +++ /dev/null @@ -1,95 +0,0 @@ -// -// This file is part of Gambit -// Copyright (c) 1994-2026, The Gambit Project (https://www.gambit-project.org) -// -// FILE: src/games/stratpure.cc -// Implementation of pure strategy profile -// -// 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 "gambit.h" -#include "stratpure.h" - -namespace Gambit { - -//======================================================================== -// class PureStrategyProfileRep -//======================================================================== - -PureStrategyProfileRep::PureStrategyProfileRep(const Game &p_game) : m_nfg(p_game) -{ - for (const auto &player : m_nfg->GetPlayers()) { - m_profile[player] = player->GetStrategies().front(); - } -} - -MixedStrategyProfile PureStrategyProfileRep::ToMixedStrategyProfile() const -{ - auto temp = m_nfg->NewMixedStrategyProfile(Rational(0)); - temp = Rational(0); - for (auto [player, strategy] : m_profile) { - temp[strategy] = Rational(1); - } - return temp; -} - -//=========================================================================== -// class StrategyContingencies -//=========================================================================== - -StrategyContingencies::StrategyContingencies(const StrategySupportProfile &p_support, - const std::vector &p_frozen) - : m_support(p_support), m_frozen(p_frozen) -{ - for (auto player : m_support.GetGame()->GetPlayers()) { - auto frozen = std::find_if(m_frozen.begin(), m_frozen.end(), [player](const GameStrategy &s) { - return s->GetPlayer() == player; - }); - if (frozen == m_frozen.end()) { - m_unfrozen.push_back(player); - } - } -} - -StrategyContingencies::iterator::iterator(StrategyContingencies *p_cont, bool p_end) - : m_cont(p_cont), m_atEnd(p_end), - m_profile(p_cont->m_support.GetGame()->NewPureStrategyProfile()) -{ - for (auto strategy : m_cont->m_frozen) { - m_profile->SetStrategy(strategy); - } - for (auto player : m_cont->m_unfrozen) { - m_currentStrat[player] = m_cont->m_support.GetStrategies(player).begin(); - m_profile->SetStrategy(*m_currentStrat[player]); - } -} - -StrategyContingencies::iterator &StrategyContingencies::iterator::operator++() -{ - for (auto player : m_cont->m_unfrozen) { - ++m_currentStrat[player]; - if (m_currentStrat[player] != m_cont->m_support.GetStrategies(player).end()) { - m_profile->SetStrategy(*m_currentStrat[player]); - return *this; - } - m_currentStrat[player] = m_cont->m_support.GetStrategies(player).begin(); - m_profile->SetStrategy(*m_currentStrat[player]); - } - m_atEnd = true; - return *this; -} - -} // namespace Gambit diff --git a/src/games/stratpure.h b/src/games/stratpure.h index ac1d99c4b..24596fe7f 100644 --- a/src/games/stratpure.h +++ b/src/games/stratpure.h @@ -36,11 +36,17 @@ class PureStrategyProfileRep { friend class PureStrategyProfile; protected: - Game m_nfg; - std::map m_profile; + Game m_game; + long m_index{0}; /// Construct a new strategy profile - explicit PureStrategyProfileRep(const Game &p_game); + explicit PureStrategyProfileRep(const Game &p_game) : m_game(p_game) + { + for (const auto &stride : p_game->m_pureStrategies.m_strides) { + constexpr long digit = 0; // The digit of the first strategy + m_index += digit * stride; + } + } public: virtual ~PureStrategyProfileRep() = default; @@ -50,18 +56,28 @@ class PureStrategyProfileRep { /// @name Data access and manipulation //@{ - const Game &GetGame() const { return m_nfg; } + const Game &GetGame() const { return m_game; } /// Get the strategy played by the player - const GameStrategy &GetStrategy(const GamePlayer &p_player) const + GameStrategy GetStrategy(const GamePlayer &p_player) const { - return m_profile.at(p_player); + const auto &[m_radices, m_strides] = m_game->m_pureStrategies; + const size_t index = p_player->m_number - 1; + const long stride = m_strides[index]; + const long radix = m_radices[index]; + const long digit = (m_index / stride) % radix; + return p_player->m_strategies[digit]; } /// Set the strategy for a player - virtual void SetStrategy(const GameStrategy &p_strategy) + void SetStrategy(const GameStrategy &p_strategy) { - m_profile[p_strategy->GetPlayer()] = p_strategy; + const auto &[m_radices, m_strides] = m_game->m_pureStrategies; + const size_t index = p_strategy->m_player->m_number - 1; + const long stride = m_strides[index]; + const long digit_old = (m_index / stride) % m_radices[index]; + const long digit_new = p_strategy->m_number - 1; + m_index += (digit_new - digit_old) * stride; } /// Get the outcome that results from the profile @@ -82,13 +98,14 @@ class PureStrategyProfileRep { }; class PureStrategyProfile { -private: std::shared_ptr rep; public: PureStrategyProfile(const PureStrategyProfile &r) : rep(r.rep->Copy()) {} - explicit PureStrategyProfile(std::shared_ptr p_rep) : rep(p_rep) {} + explicit PureStrategyProfile(const std::shared_ptr &p_rep) : rep(p_rep) + { + } ~PureStrategyProfile() = default; @@ -104,42 +121,77 @@ class PureStrategyProfile { }; class StrategyContingencies { -private: StrategySupportProfile m_support; - std::vector m_unfrozen; - std::vector m_frozen; public: class iterator { - private: StrategyContingencies *m_cont; - bool m_atEnd; - std::map m_currentStrat; + bool m_atEnd{false}; + std::vector m_pos; PureStrategyProfile m_profile; public: - iterator(StrategyContingencies *, bool p_end); + using iterator_category = std::forward_iterator_tag; + using value_type = PureStrategyProfile; + using difference_type = std::ptrdiff_t; + using pointer = PureStrategyProfile *; + using reference = PureStrategyProfile &; + + iterator(StrategyContingencies *cont, bool p_end) + : m_cont(cont), m_atEnd(p_end), + m_profile(cont->m_support.GetGame()->NewPureStrategyProfile()) + { + if (m_atEnd) { + return; + } + const size_t n = m_cont->m_support.GetPlayers().size(); + m_pos.assign(n, 0); + for (size_t i = 0; i < n; ++i) { + const GamePlayer player = m_cont->m_support.GetGame()->GetPlayer(i + 1); + auto support = m_cont->m_support.GetStrategies(player); + m_profile->SetStrategy(support[0]); + } + } - iterator &operator++(); + iterator &operator++() + { + const size_t n = m_pos.size(); + for (size_t i = 0; i < n; ++i) { + const GamePlayer player = m_cont->m_support.GetGame()->GetPlayer(i + 1); + auto support = m_cont->m_support.GetStrategies(player); + ++m_pos[i]; + if (m_pos[i] < support.size()) { + m_profile->SetStrategy(support[m_pos[i]]); + return *this; + } + m_pos[i] = 0; + m_profile->SetStrategy(support[0]); + } - bool operator==(const iterator &p_other) const + m_atEnd = true; + return *this; + } + + bool operator==(const iterator &other) const { - if (m_atEnd && p_other.m_atEnd && m_cont == p_other.m_cont) { - return true; + if (m_atEnd && other.m_atEnd) { + return m_cont == other.m_cont; } - if (m_atEnd != p_other.m_atEnd || m_cont != p_other.m_cont) { + if (m_atEnd != other.m_atEnd) { return false; } - return (m_profile.operator->() == p_other.m_profile.operator->()); + return m_profile.operator->() == other.m_profile.operator->(); } - bool operator!=(const iterator &p_other) const { return !(*this == p_other); } + + bool operator!=(const iterator &other) const { return !(*this == other); } PureStrategyProfile &operator*() { return m_profile; } const PureStrategyProfile &operator*() const { return m_profile; } }; - explicit StrategyContingencies(const StrategySupportProfile &, - const std::vector &p_frozen = {}); + explicit StrategyContingencies(const Game &p_game) : m_support(StrategySupportProfile(p_game)) {} + explicit StrategyContingencies(const StrategySupportProfile &support) : m_support(support) {} + iterator begin() { return {this, false}; } iterator end() { return {this, true}; } }; diff --git a/src/games/stratspt.cc b/src/games/stratspt.cc index fe818cafe..296d65af4 100644 --- a/src/games/stratspt.cc +++ b/src/games/stratspt.cc @@ -32,51 +32,48 @@ namespace Gambit { // class StrategySupportProfile //=========================================================================== -StrategySupportProfile::StrategySupportProfile(const Game &p_nfg) : m_nfg(p_nfg) +StrategySupportProfile::StrategySupportProfile(const Game &p_game) : m_game(p_game) { - for (auto player : m_nfg->GetPlayers()) { - for (auto strategy : player->GetStrategies()) { - m_support[player].push_back(strategy); - } + m_game->BuildComputedValues(); + m_strategies.m_space = &p_game->m_pureStrategies; + m_strategies.m_allowedDigits.resize(p_game->NumPlayers()); + + for (size_t i = 0; i < p_game->NumPlayers(); ++i) { + const int radix = p_game->m_pureStrategies.m_radices[i]; + auto &digits = m_strategies.m_allowedDigits[i]; + digits.resize(radix); + std::iota(digits.begin(), digits.end(), 0); } } -Array StrategySupportProfile::NumStrategies() const -{ - Array dim(m_support.size()); - std::transform( - m_support.cbegin(), m_support.cend(), dim.begin(), - [](std::pair> a) { return a.second.size(); }); - return dim; -} - int StrategySupportProfile::MixedProfileLength() const { - return std::accumulate(m_support.cbegin(), m_support.cend(), 0, - [](size_t tot, std::pair> a) { - return tot + a.second.size(); - }); + return sum_function(m_strategies.m_allowedDigits, [](const std::vector &digits) { + return static_cast(digits.size()); + }); } template <> MixedStrategyProfile StrategySupportProfile::NewMixedStrategyProfile() const { - return m_nfg->NewMixedStrategyProfile(0.0, *this); + return m_game->NewMixedStrategyProfile(0.0, *this); } template <> MixedStrategyProfile StrategySupportProfile::NewMixedStrategyProfile() const { - return m_nfg->NewMixedStrategyProfile(Rational(0), *this); + return m_game->NewMixedStrategyProfile(Rational(0), *this); } -bool StrategySupportProfile::IsSubsetOf(const StrategySupportProfile &p_support) const +bool StrategySupportProfile::IsSubsetOf(const StrategySupportProfile &p_other) const { - for (auto player : m_nfg->GetPlayers()) { - if (!std::includes(p_support.m_support.at(player).begin(), - p_support.m_support.at(player).end(), m_support.at(player).begin(), - m_support.at(player).end(), - [](const GameStrategy &s, const GameStrategy &t) { - return s->GetNumber() < t->GetNumber(); - })) { + if (m_game != p_other.m_game) { + return false; + } + const auto &A = m_strategies.m_allowedDigits; + const auto &B = p_other.m_strategies.m_allowedDigits; + const size_t n = A.size(); + + for (size_t i = 0; i < n; ++i) { + if (!std::includes(B[i].begin(), B[i].end(), A[i].begin(), A[i].end())) { return false; } } @@ -85,8 +82,8 @@ bool StrategySupportProfile::IsSubsetOf(const StrategySupportProfile &p_support) void StrategySupportProfile::WriteNfgFile(std::ostream &p_file) const { - auto players = m_nfg->GetPlayers(); - p_file << "NFG 1 R " << std::quoted(m_nfg->GetTitle()) << ' ' + auto players = m_game->GetPlayers(); + p_file << "NFG 1 R " << std::quoted(m_game->GetTitle()) << ' ' << FormatList(players, [](const GamePlayer &p) { return QuoteString(p->GetLabel()); }) << std::endl << std::endl; @@ -97,9 +94,9 @@ void StrategySupportProfile::WriteNfgFile(std::ostream &p_file) const }) << std::endl; } p_file << "}" << std::endl; - p_file << std::quoted(m_nfg->GetComment()) << std::endl << std::endl; + p_file << std::quoted(m_game->GetComment()) << std::endl << std::endl; - for (auto iter : StrategyContingencies(*this)) { + for (const auto &iter : StrategyContingencies(*this)) { p_file << FormatList( players, [&iter](const GamePlayer &p) { @@ -112,25 +109,32 @@ void StrategySupportProfile::WriteNfgFile(std::ostream &p_file) const void StrategySupportProfile::AddStrategy(const GameStrategy &p_strategy) { - auto &support = m_support[p_strategy->GetPlayer()]; - auto pos = std::find_if(support.begin(), support.end(), [p_strategy](const GameStrategy &s) { - return s->GetNumber() >= p_strategy->GetNumber(); - }); - if (pos == support.end() || *pos != p_strategy) { - // Strategy is not in the support for the player; add at this location to keep sorted by number - support.insert(pos, p_strategy); + if (p_strategy->GetGame() != m_game) { + throw MismatchException(); + } + const size_t index = p_strategy->GetPlayer()->GetNumber() - 1; + const int digit = p_strategy->GetNumber() - 1; + auto &digits = m_strategies.m_allowedDigits[index]; + auto pos = std::lower_bound(digits.begin(), digits.end(), digit); + if (pos == digits.end() || *pos != digit) { + digits.insert(pos, digit); } } bool StrategySupportProfile::RemoveStrategy(const GameStrategy &p_strategy) { - auto &support = m_support[p_strategy->GetPlayer()]; - if (support.size() == 1) { + if (p_strategy->GetGame() != m_game) { + throw MismatchException(); + } + const size_t index = p_strategy->GetPlayer()->GetNumber() - 1; + const int digit = p_strategy->GetNumber() - 1; + auto &digits = m_strategies.m_allowedDigits[index]; + if (digits.size() == 1) { return false; } - auto pos = std::find(support.begin(), support.end(), p_strategy); - if (pos != support.end()) { - support.erase(pos); + auto pos = std::lower_bound(digits.begin(), digits.end(), digit); + if (pos != digits.end() && *pos == digit) { + digits.erase(pos); return true; } return false; @@ -145,7 +149,7 @@ bool StrategySupportProfile::Dominates(const GameStrategy &s, const GameStrategy { bool equal = true; - for (auto iter : StrategyContingencies(*this)) { + for (const auto &iter : StrategyContingencies(*this)) { const Rational ap = iter->GetStrategyValue(s); const Rational bp = iter->GetStrategyValue(t); if (p_strict && ap <= bp) { @@ -263,7 +267,7 @@ bool UndominatedForPlayer(const StrategySupportProfile &p_support, StrategySupportProfile StrategySupportProfile::Undominated(bool p_strict, bool p_external) const { StrategySupportProfile newSupport(*this); - for (auto player : m_nfg->GetPlayers()) { + for (auto player : m_game->GetPlayers()) { UndominatedForPlayer(*this, newSupport, player, p_strict, p_external); } return newSupport; diff --git a/src/games/stratspt.h b/src/games/stratspt.h index bbae3eb3d..502bbbbd1 100644 --- a/src/games/stratspt.h +++ b/src/games/stratspt.h @@ -39,31 +39,89 @@ namespace Gambit { /// Within the support, strategies are maintained in the same order /// in which they appear in the underlying game. class StrategySupportProfile { -protected: - Game m_nfg; - std::map> m_support; + Game m_game; + CartesianSubset m_strategies; public: class Support { - private: const StrategySupportProfile *m_profile; - GamePlayer m_player; + size_t m_playerIndex; public: - using const_iterator = std::vector::const_iterator; + class const_iterator { + const StrategySupportProfile *m_profile{nullptr}; + size_t m_playerIndex{0}; + std::vector::const_iterator m_it; + + public: + using value_type = GameStrategy; + using reference = GameStrategy; + using pointer = void; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + const_iterator() = default; + const_iterator(const StrategySupportProfile *profile, const size_t playerIndex, + const std::vector::const_iterator it) + : m_profile(profile), m_playerIndex(playerIndex), m_it(it) + { + } + + GameStrategy operator*() const + { + const auto &player = m_profile->m_game->GetPlayer(m_playerIndex + 1); + return player->GetStrategy(*m_it + 1); + } + + const_iterator &operator++() + { + ++m_it; + return *this; + } + + bool operator==(const const_iterator &other) const { return m_it == other.m_it; } + + bool operator!=(const const_iterator &other) const { return !(*this == other); } + }; + + Support() : m_profile(nullptr), m_playerIndex(0) {} + + Support(const StrategySupportProfile *profile, GamePlayer player) + : m_profile(profile), m_playerIndex(player->GetNumber() - 1) + { + } + + size_t size() const { return m_profile->m_strategies.m_allowedDigits[m_playerIndex].size(); } + + GameStrategy operator[](const size_t index) const + { + const int digit = m_profile->m_strategies.m_allowedDigits[m_playerIndex][index]; + return m_profile->m_game->GetPlayer(m_playerIndex + 1)->GetStrategy(digit + 1); + } - Support() : m_profile(nullptr), m_player(nullptr) {} - Support(const StrategySupportProfile *p_profile, GamePlayer p_player) - : m_profile(p_profile), m_player(p_player) + GameStrategy front() const { + const int digit = m_profile->m_strategies.m_allowedDigits[m_playerIndex].front(); + return m_profile->m_game->GetPlayer(m_playerIndex + 1)->GetStrategy(digit + 1); } - size_t size() const { return m_profile->m_support.at(m_player).size(); } - GameStrategy front() const { return m_profile->m_support.at(m_player).front(); } - GameStrategy back() const { return m_profile->m_support.at(m_player).back(); } + GameStrategy back() const + { + const int digit = m_profile->m_strategies.m_allowedDigits[m_playerIndex].back(); + return m_profile->m_game->GetPlayer(m_playerIndex + 1)->GetStrategy(digit + 1); + } + + const_iterator begin() const + { + const auto &digits = m_profile->m_strategies.m_allowedDigits[m_playerIndex]; + return {m_profile, m_playerIndex, digits.begin()}; + } - const_iterator begin() const { return m_profile->m_support.at(m_player).begin(); } - const_iterator end() const { return m_profile->m_support.at(m_player).end(); } + const_iterator end() const + { + const auto &digits = m_profile->m_strategies.m_allowedDigits[m_playerIndex]; + return {m_profile, m_playerIndex, digits.end()}; + } }; /// @name Lifecycle @@ -77,22 +135,21 @@ class StrategySupportProfile { /// Test for the equality of two supports (same strategies for all players) bool operator==(const StrategySupportProfile &p_support) const { - return (m_support == p_support.m_support); + return m_game == p_support.m_game && + m_strategies.m_allowedDigits == p_support.m_strategies.m_allowedDigits; } /// Test for the inequality of two supports bool operator!=(const StrategySupportProfile &p_support) const { - return (m_support != p_support.m_support); + return m_game != p_support.m_game || + m_strategies.m_allowedDigits != p_support.m_strategies.m_allowedDigits; } //@} /// @name General information //@{ /// Returns the game on which the support is defined. - Game GetGame() const { return m_nfg; } - - /// Returns the number of strategies in the support for all players. - Array NumStrategies() const; + Game GetGame() const { return m_game; } /// Returns the total number of strategies in the support. int MixedProfileLength() const; @@ -100,14 +157,19 @@ class StrategySupportProfile { template MixedStrategyProfile NewMixedStrategyProfile() const; /// Returns the number of players in the game - int NumPlayers() const { return m_nfg->NumPlayers(); } + int NumPlayers() const { return m_game->NumPlayers(); } /// Returns the set of players in the game - GameRep::Players GetPlayers() const { return m_nfg->GetPlayers(); } + GameRep::Players GetPlayers() const { return m_game->GetPlayers(); } /// Returns the set of strategies in the support for a player Support GetStrategies(const GamePlayer &p_player) const { return {this, p_player}; } /// Returns true exactly when the strategy is in the support. - bool Contains(const GameStrategy &s) const { return contains(m_support.at(s->GetPlayer()), s); } + bool Contains(const GameStrategy &s) const + { + const auto &digits = m_strategies.m_allowedDigits[s->GetPlayer()->GetNumber() - 1]; + const int digit = s->GetNumber() - 1; + return std::binary_search(digits.begin(), digits.end(), digit); + } /// Returns true iff this support is a (weak) subset of the specified support bool IsSubsetOf(const StrategySupportProfile &) const; @@ -131,6 +193,19 @@ class StrategySupportProfile { /// player, it is not removed. Returns true if the removal was /// executed, and false if not. bool RemoveStrategy(const GameStrategy &); + + StrategySupportProfile RestrictTo(const GameStrategy &p_strategy) const + { + if (p_strategy->GetGame() != m_game) { + throw MismatchException(); + } + const GamePlayer player = p_strategy->GetPlayer(); + const size_t player_index = player->GetNumber() - 1; + const int digit = p_strategy->GetNumber() - 1; + StrategySupportProfile restricted(*this); + restricted.m_strategies.m_allowedDigits[player_index].assign(1, digit); + return restricted; + } //@} /// @name Identification of dominated strategies diff --git a/src/games/writer.cc b/src/games/writer.cc index abffe1ad4..744f3efad 100644 --- a/src/games/writer.cc +++ b/src/games/writer.cc @@ -31,8 +31,10 @@ std::string WriteHTMLFile(const Game &p_game, const GamePlayer &p_rowPlayer, std::string theHtml; theHtml += "

" + p_game->GetTitle() + "

\n"; - for (auto iter : StrategyContingencies( - p_game, {p_rowPlayer->GetStrategies().front(), p_colPlayer->GetStrategies().front()})) { + auto support = StrategySupportProfile(p_game) + .RestrictTo(p_rowPlayer->GetStrategies().front()) + .RestrictTo(p_colPlayer->GetStrategies().front()); + for (const auto &iter : StrategyContingencies(support)) { if (p_game->NumPlayers() > 2) { theHtml += "
Subtable with strategies:
"; for (auto player : p_game->GetPlayers()) { @@ -99,8 +101,10 @@ std::string WriteLaTeXFile(const Game &p_game, const GamePlayer &p_rowPlayer, { std::string theHtml; - for (auto iter : StrategyContingencies( - p_game, {p_rowPlayer->GetStrategies().front(), p_colPlayer->GetStrategies().front()})) { + auto support = StrategySupportProfile(p_game) + .RestrictTo(p_rowPlayer->GetStrategies().front()) + .RestrictTo(p_colPlayer->GetStrategies().front()); + for (const auto &iter : StrategyContingencies(support)) { theHtml += "\\begin{game}{"; theHtml += std::to_string(p_rowPlayer->GetStrategies().size()); theHtml += "}{"; diff --git a/src/solvers/enumpoly/nfgpoly.cc b/src/solvers/enumpoly/nfgpoly.cc index aad63d3ea..3334b7de7 100644 --- a/src/solvers/enumpoly/nfgpoly.cc +++ b/src/solvers/enumpoly/nfgpoly.cc @@ -63,7 +63,7 @@ IndifferenceEquation(std::shared_ptr space, const StrategySupport { Polynomial equation(space); - for (auto iter : StrategyContingencies(support, {s1})) { + for (const auto &iter : StrategyContingencies(support.RestrictTo(s1))) { Polynomial term(space, 1); for (auto player : support.GetGame()->GetPlayers()) { if (player != s1->GetPlayer()) { diff --git a/src/solvers/enumpure/enumpure.h b/src/solvers/enumpure/enumpure.h index 6dcf4bb18..7b2563a02 100644 --- a/src/solvers/enumpure/enumpure.h +++ b/src/solvers/enumpure/enumpure.h @@ -53,9 +53,9 @@ inline std::list> EnumPureStrategySolve( "Computing equilibria of games with imperfect recall is not supported."); } std::list> solutions; - for (auto citer : StrategyContingencies(p_game)) { - if (IsNash(citer)) { - solutions.push_back(citer->ToMixedStrategyProfile()); + for (const auto &profile : StrategyContingencies(p_game)) { + if (IsNash(profile)) { + solutions.push_back(profile->ToMixedStrategyProfile()); p_onEquilibrium(solutions.back(), "NE"); } } diff --git a/src/solvers/gtracer/gtracer.cc b/src/solvers/gtracer/gtracer.cc index 3b505651e..164e98c97 100644 --- a/src/solvers/gtracer/gtracer.cc +++ b/src/solvers/gtracer/gtracer.cc @@ -44,7 +44,7 @@ std::shared_ptr BuildGame(const Game &p_game, bool p_scaled) std::shared_ptr A(new nfgame(actions)); std::vector profile(players.size()); - for (auto iter : StrategyContingencies(p_game)) { + for (const auto &iter : StrategyContingencies(p_game)) { std::transform(players.cbegin(), players.cend(), profile.begin(), [iter](const GamePlayer &p) { return iter->GetStrategy(p)->GetNumber() - 1; }); for (auto player : players) {