From cfb137d7b15ae0f240c4c6bde177b9164a62b412 Mon Sep 17 00:00:00 2001 From: Theodore Turocy Date: Mon, 26 Jan 2026 15:31:14 +0000 Subject: [PATCH 1/6] Preparatory internal renaming --- src/games/stratspt.cc | 16 ++++++++-------- src/games/stratspt.h | 25 ++++++++++++++----------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/games/stratspt.cc b/src/games/stratspt.cc index 296d65af4..58defa9ee 100644 --- a/src/games/stratspt.cc +++ b/src/games/stratspt.cc @@ -35,12 +35,12 @@ namespace Gambit { StrategySupportProfile::StrategySupportProfile(const Game &p_game) : m_game(p_game) { m_game->BuildComputedValues(); - m_strategies.m_space = &p_game->m_pureStrategies; - m_strategies.m_allowedDigits.resize(p_game->NumPlayers()); + m_strategyDigits.m_space = &p_game->m_pureStrategies; + m_strategyDigits.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]; + auto &digits = m_strategyDigits.m_allowedDigits[i]; digits.resize(radix); std::iota(digits.begin(), digits.end(), 0); } @@ -48,7 +48,7 @@ StrategySupportProfile::StrategySupportProfile(const Game &p_game) : m_game(p_ga int StrategySupportProfile::MixedProfileLength() const { - return sum_function(m_strategies.m_allowedDigits, [](const std::vector &digits) { + return sum_function(m_strategyDigits.m_allowedDigits, [](const std::vector &digits) { return static_cast(digits.size()); }); } @@ -68,8 +68,8 @@ bool StrategySupportProfile::IsSubsetOf(const StrategySupportProfile &p_other) c 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 auto &A = m_strategyDigits.m_allowedDigits; + const auto &B = p_other.m_strategyDigits.m_allowedDigits; const size_t n = A.size(); for (size_t i = 0; i < n; ++i) { @@ -114,7 +114,7 @@ void StrategySupportProfile::AddStrategy(const GameStrategy &p_strategy) } const size_t index = p_strategy->GetPlayer()->GetNumber() - 1; const int digit = p_strategy->GetNumber() - 1; - auto &digits = m_strategies.m_allowedDigits[index]; + auto &digits = m_strategyDigits.m_allowedDigits[index]; auto pos = std::lower_bound(digits.begin(), digits.end(), digit); if (pos == digits.end() || *pos != digit) { digits.insert(pos, digit); @@ -128,7 +128,7 @@ bool StrategySupportProfile::RemoveStrategy(const GameStrategy &p_strategy) } const size_t index = p_strategy->GetPlayer()->GetNumber() - 1; const int digit = p_strategy->GetNumber() - 1; - auto &digits = m_strategies.m_allowedDigits[index]; + auto &digits = m_strategyDigits.m_allowedDigits[index]; if (digits.size() == 1) { return false; } diff --git a/src/games/stratspt.h b/src/games/stratspt.h index 502bbbbd1..fe267cde1 100644 --- a/src/games/stratspt.h +++ b/src/games/stratspt.h @@ -40,7 +40,7 @@ namespace Gambit { /// in which they appear in the underlying game. class StrategySupportProfile { Game m_game; - CartesianSubset m_strategies; + CartesianSubset m_strategyDigits; public: class Support { @@ -91,35 +91,38 @@ class StrategySupportProfile { { } - size_t size() const { return m_profile->m_strategies.m_allowedDigits[m_playerIndex].size(); } + size_t size() const + { + return m_profile->m_strategyDigits.m_allowedDigits[m_playerIndex].size(); + } GameStrategy operator[](const size_t index) const { - const int digit = m_profile->m_strategies.m_allowedDigits[m_playerIndex][index]; + const int digit = m_profile->m_strategyDigits.m_allowedDigits[m_playerIndex][index]; return m_profile->m_game->GetPlayer(m_playerIndex + 1)->GetStrategy(digit + 1); } GameStrategy front() const { - const int digit = m_profile->m_strategies.m_allowedDigits[m_playerIndex].front(); + const int digit = m_profile->m_strategyDigits.m_allowedDigits[m_playerIndex].front(); return m_profile->m_game->GetPlayer(m_playerIndex + 1)->GetStrategy(digit + 1); } GameStrategy back() const { - const int digit = m_profile->m_strategies.m_allowedDigits[m_playerIndex].back(); + const int digit = m_profile->m_strategyDigits.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]; + const auto &digits = m_profile->m_strategyDigits.m_allowedDigits[m_playerIndex]; return {m_profile, m_playerIndex, digits.begin()}; } const_iterator end() const { - const auto &digits = m_profile->m_strategies.m_allowedDigits[m_playerIndex]; + const auto &digits = m_profile->m_strategyDigits.m_allowedDigits[m_playerIndex]; return {m_profile, m_playerIndex, digits.end()}; } }; @@ -136,13 +139,13 @@ class StrategySupportProfile { bool operator==(const StrategySupportProfile &p_support) const { return m_game == p_support.m_game && - m_strategies.m_allowedDigits == p_support.m_strategies.m_allowedDigits; + m_strategyDigits.m_allowedDigits == p_support.m_strategyDigits.m_allowedDigits; } /// Test for the inequality of two supports bool operator!=(const StrategySupportProfile &p_support) const { return m_game != p_support.m_game || - m_strategies.m_allowedDigits != p_support.m_strategies.m_allowedDigits; + m_strategyDigits.m_allowedDigits != p_support.m_strategyDigits.m_allowedDigits; } //@} @@ -166,7 +169,7 @@ class StrategySupportProfile { /// Returns true exactly when the strategy is in the support. bool Contains(const GameStrategy &s) const { - const auto &digits = m_strategies.m_allowedDigits[s->GetPlayer()->GetNumber() - 1]; + const auto &digits = m_strategyDigits.m_allowedDigits[s->GetPlayer()->GetNumber() - 1]; const int digit = s->GetNumber() - 1; return std::binary_search(digits.begin(), digits.end(), digit); } @@ -203,7 +206,7 @@ class StrategySupportProfile { 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); + restricted.m_strategyDigits.m_allowedDigits[player_index].assign(1, digit); return restricted; } //@} From d3bd17d98da047389f868f74ada25d142df91fb6 Mon Sep 17 00:00:00 2001 From: Theodore Turocy Date: Mon, 26 Jan 2026 15:41:39 +0000 Subject: [PATCH 2/6] Re-fill m_support and modify on add/remove --- src/games/stratspt.cc | 40 +++++++++++++++++++++++++++++++--------- src/games/stratspt.h | 1 + 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/games/stratspt.cc b/src/games/stratspt.cc index 58defa9ee..fb86aed01 100644 --- a/src/games/stratspt.cc +++ b/src/games/stratspt.cc @@ -35,6 +35,12 @@ namespace Gambit { StrategySupportProfile::StrategySupportProfile(const Game &p_game) : m_game(p_game) { m_game->BuildComputedValues(); + for (const auto &player : m_game->GetPlayers()) { + for (const auto &strategy : player->GetStrategies()) { + m_support[player].push_back(strategy); + } + } + m_strategyDigits.m_space = &p_game->m_pureStrategies; m_strategyDigits.m_allowedDigits.resize(p_game->NumPlayers()); @@ -112,12 +118,22 @@ void StrategySupportProfile::AddStrategy(const GameStrategy &p_strategy) if (p_strategy->GetGame() != m_game) { throw MismatchException(); } + + 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); + } + const size_t index = p_strategy->GetPlayer()->GetNumber() - 1; const int digit = p_strategy->GetNumber() - 1; auto &digits = m_strategyDigits.m_allowedDigits[index]; - auto pos = std::lower_bound(digits.begin(), digits.end(), digit); - if (pos == digits.end() || *pos != digit) { - digits.insert(pos, digit); + auto ipos = std::lower_bound(digits.begin(), digits.end(), digit); + if (ipos == digits.end() || *ipos != digit) { + digits.insert(ipos, digit); } } @@ -126,15 +142,21 @@ bool StrategySupportProfile::RemoveStrategy(const GameStrategy &p_strategy) if (p_strategy->GetGame() != m_game) { throw MismatchException(); } + auto &support = m_support[p_strategy->GetPlayer()]; + if (support.size() == 1) { + return false; + } + auto pos = std::find(support.begin(), support.end(), p_strategy); + if (pos != support.end()) { + support.erase(pos); + } + const size_t index = p_strategy->GetPlayer()->GetNumber() - 1; const int digit = p_strategy->GetNumber() - 1; auto &digits = m_strategyDigits.m_allowedDigits[index]; - if (digits.size() == 1) { - return false; - } - auto pos = std::lower_bound(digits.begin(), digits.end(), digit); - if (pos != digits.end() && *pos == digit) { - digits.erase(pos); + auto ipos = std::lower_bound(digits.begin(), digits.end(), digit); + if (ipos != digits.end() && *ipos == digit) { + digits.erase(ipos); return true; } return false; diff --git a/src/games/stratspt.h b/src/games/stratspt.h index fe267cde1..8afb6a109 100644 --- a/src/games/stratspt.h +++ b/src/games/stratspt.h @@ -40,6 +40,7 @@ namespace Gambit { /// in which they appear in the underlying game. class StrategySupportProfile { Game m_game; + std::map> m_support; CartesianSubset m_strategyDigits; public: From 6b19772a1cc2e9494fec6cd3b360453a47d2a882 Mon Sep 17 00:00:00 2001 From: Theodore Turocy Date: Mon, 26 Jan 2026 15:56:46 +0000 Subject: [PATCH 3/6] Re-add iterators based on strategy vector directly. --- src/games/stratspt.h | 50 ++++++++++++++------------------------------ 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/src/games/stratspt.h b/src/games/stratspt.h index 8afb6a109..560740366 100644 --- a/src/games/stratspt.h +++ b/src/games/stratspt.h @@ -46,13 +46,13 @@ class StrategySupportProfile { public: class Support { const StrategySupportProfile *m_profile; - size_t m_playerIndex; + const GamePlayer m_player; public: class const_iterator { const StrategySupportProfile *m_profile{nullptr}; - size_t m_playerIndex{0}; - std::vector::const_iterator m_it; + const GamePlayer m_player; + std::vector::const_iterator m_it; public: using value_type = GameStrategy; @@ -62,17 +62,13 @@ class StrategySupportProfile { 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) + const_iterator(const StrategySupportProfile *profile, const GamePlayer &player, + const std::vector::const_iterator it) + : m_profile(profile), m_player(player), m_it(it) { } - GameStrategy operator*() const - { - const auto &player = m_profile->m_game->GetPlayer(m_playerIndex + 1); - return player->GetStrategy(*m_it + 1); - } + GameStrategy operator*() const { return *m_it; } const_iterator &operator++() { @@ -85,46 +81,32 @@ class StrategySupportProfile { bool operator!=(const const_iterator &other) const { return !(*this == other); } }; - Support() : m_profile(nullptr), m_playerIndex(0) {} + Support() : m_profile(nullptr) {} - Support(const StrategySupportProfile *profile, GamePlayer player) - : m_profile(profile), m_playerIndex(player->GetNumber() - 1) + Support(const StrategySupportProfile *profile, const GamePlayer &player) + : m_profile(profile), m_player(player) { } - size_t size() const - { - return m_profile->m_strategyDigits.m_allowedDigits[m_playerIndex].size(); - } + size_t size() const { return m_profile->m_support.at(m_player).size(); } GameStrategy operator[](const size_t index) const { - const int digit = m_profile->m_strategyDigits.m_allowedDigits[m_playerIndex][index]; - return m_profile->m_game->GetPlayer(m_playerIndex + 1)->GetStrategy(digit + 1); + return m_profile->m_support.at(m_player)[index]; } - GameStrategy front() const - { - const int digit = m_profile->m_strategyDigits.m_allowedDigits[m_playerIndex].front(); - return m_profile->m_game->GetPlayer(m_playerIndex + 1)->GetStrategy(digit + 1); - } + GameStrategy front() const { return m_profile->m_support.at(m_player).front(); } - GameStrategy back() const - { - const int digit = m_profile->m_strategyDigits.m_allowedDigits[m_playerIndex].back(); - return m_profile->m_game->GetPlayer(m_playerIndex + 1)->GetStrategy(digit + 1); - } + GameStrategy back() const { return m_profile->m_support.at(m_player).back(); } const_iterator begin() const { - const auto &digits = m_profile->m_strategyDigits.m_allowedDigits[m_playerIndex]; - return {m_profile, m_playerIndex, digits.begin()}; + return {m_profile, m_player, m_profile->m_support.at(m_player).begin()}; } const_iterator end() const { - const auto &digits = m_profile->m_strategyDigits.m_allowedDigits[m_playerIndex]; - return {m_profile, m_playerIndex, digits.end()}; + return {m_profile, m_player, m_profile->m_support.at(m_player).end()}; } }; From 38e5675cd6332073fe80dd9d549f452d72b57454 Mon Sep 17 00:00:00 2001 From: Theodore Turocy Date: Mon, 26 Jan 2026 16:11:24 +0000 Subject: [PATCH 4/6] Use const_iterator directly --- src/games/stratspt.h | 50 ++++---------------------------------------- 1 file changed, 4 insertions(+), 46 deletions(-) diff --git a/src/games/stratspt.h b/src/games/stratspt.h index 560740366..ce314aa6c 100644 --- a/src/games/stratspt.h +++ b/src/games/stratspt.h @@ -49,65 +49,23 @@ class StrategySupportProfile { const GamePlayer m_player; public: - class const_iterator { - const StrategySupportProfile *m_profile{nullptr}; - const GamePlayer m_player; - 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 GamePlayer &player, - const std::vector::const_iterator it) - : m_profile(profile), m_player(player), m_it(it) - { - } - - GameStrategy operator*() const { return *m_it; } - - 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) {} + using const_iterator = std::vector::const_iterator; + Support() : m_profile(nullptr), m_player(nullptr) {} Support(const StrategySupportProfile *profile, const GamePlayer &player) : m_profile(profile), m_player(player) { } size_t size() const { return m_profile->m_support.at(m_player).size(); } - GameStrategy operator[](const size_t index) const { return m_profile->m_support.at(m_player)[index]; } - GameStrategy front() const { return m_profile->m_support.at(m_player).front(); } - GameStrategy back() const { return m_profile->m_support.at(m_player).back(); } - - const_iterator begin() const - { - return {m_profile, m_player, m_profile->m_support.at(m_player).begin()}; - } - - const_iterator end() const - { - return {m_profile, m_player, m_profile->m_support.at(m_player).end()}; - } + 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(); } }; /// @name Lifecycle From 4fa27ed8d5251ec77d6d4f9580ceaefadc9fc96d Mon Sep 17 00:00:00 2001 From: Theodore Turocy Date: Mon, 26 Jan 2026 16:49:33 +0000 Subject: [PATCH 5/6] A few optimisations --- src/games/gametable.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/games/gametable.cc b/src/games/gametable.cc index 09356d501..2a2d11952 100644 --- a/src/games/gametable.cc +++ b/src/games/gametable.cc @@ -115,17 +115,17 @@ template class TableMixedStrategyProfileRep : public MixedStrategyProf T &value) const; //@} + const CartesianProductSpace &m_pureStrategies; + 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]; + return (s->GetNumber() - 1) * m_pureStrategies.m_strides[s->m_player->GetNumber() - 1]; } public: explicit TableMixedStrategyProfileRep(const StrategySupportProfile &p_support) - : MixedStrategyProfileRep(p_support) + : MixedStrategyProfileRep(p_support), + m_pureStrategies(p_support.GetGame()->m_pureStrategies) { } ~TableMixedStrategyProfileRep() override = default; From 29f54402aadd151e4ee4b3f2467371c66fbe6f41 Mon Sep 17 00:00:00 2001 From: Theodore Turocy Date: Mon, 26 Jan 2026 17:09:46 +0000 Subject: [PATCH 6/6] Fix declaration --- src/games/stratspt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/games/stratspt.h b/src/games/stratspt.h index ce314aa6c..0ceef7ac3 100644 --- a/src/games/stratspt.h +++ b/src/games/stratspt.h @@ -46,7 +46,7 @@ class StrategySupportProfile { public: class Support { const StrategySupportProfile *m_profile; - const GamePlayer m_player; + GamePlayer m_player; public: using const_iterator = std::vector::const_iterator;