Skip to content

Commit

Permalink
Modernise and simplify StrategySupportProfile implementation.
Browse files Browse the repository at this point in the history
This cleans up various aspects of technical debt in StrategySupportProfile
and related classes:
* Using STL containers instead of Gambit ones
* Defining appropriate abstractions
* Preferring STL-style algorithms to manually-crafted ones
* Avoiding using explicit 1-based indexes in favour of contaniner-iterator idiom.
  • Loading branch information
tturocy committed Oct 31, 2024
1 parent 1d5c3c4 commit 008b7e8
Show file tree
Hide file tree
Showing 22 changed files with 380 additions and 392 deletions.
2 changes: 1 addition & 1 deletion src/core/array.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ template <class T> class Array {
using pointer = value_type *;
using reference = value_type &;

iterator(Array *p_array, int p_index) : m_array(p_array), m_index(p_index) {}
iterator(Array *p_array = 0, int p_index = 0) : m_array(p_array), m_index(p_index) {}
reference operator*() { return (*m_array)[m_index]; }
pointer operator->() { return &(*m_array)[m_index]; }
iterator &operator++()
Expand Down
2 changes: 2 additions & 0 deletions src/core/rational.cc
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,8 @@ Rational::Rational(long n) : num(n), den(&OneRep) {}

Rational::Rational(int n) : num(n), den(&OneRep) {}

Rational::Rational(size_t n) : num(int(n)), den(&OneRep) {}

Rational::Rational(long n, long d) : num(n), den(d)
{
if (d == 0) {
Expand Down
1 change: 1 addition & 0 deletions src/core/rational.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class Rational {
Rational();
explicit Rational(double);
explicit Rational(int);
explicit Rational(size_t);
explicit Rational(long n);
Rational(int n, int d);
Rational(long n, long d);
Expand Down
6 changes: 6 additions & 0 deletions src/gambit.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <sstream>
#include <iomanip>
#include <cmath>
#include <algorithm>
#include <map>

namespace Gambit {
Expand All @@ -53,6 +54,11 @@ inline double abs(double x) { return std::fabs(x); }

inline double sqr(double x) { return x * x; }

template <class C, class T> bool contains(const C &p_container, const T &p_value)
{
return std::find(p_container.cbegin(), p_container.cend(), p_value) != p_container.cend();
}

template <class Key, class T> bool contains(const std::map<Key, T> &map, const Key &key)
// TODO: remove when we move to C++20 which already includes a "contains" method
{
Expand Down
14 changes: 13 additions & 1 deletion src/games/game.cc
Original file line number Diff line number Diff line change
Expand Up @@ -312,15 +312,27 @@ void GameRep::WriteNfgFile(std::ostream &p_file) const
template <class T>
MixedStrategyProfileRep<T>::MixedStrategyProfileRep(const StrategySupportProfile &p_support)
: m_probs(p_support.MixedProfileLength()), m_support(p_support),
m_profileIndex(p_support.GetGame()->MixedProfileLength()),
m_gameversion(p_support.GetGame()->GetVersion())
{
int index = 1, stnum = 1;
for (auto player : p_support.GetGame()->GetPlayers()) {
for (auto strategy : player->GetStrategies()) {
if (p_support.Contains(strategy)) {
m_profileIndex[stnum++] = index++;
}
else {
m_profileIndex[stnum++] = -1;
}
}
}
SetCentroid();
}

template <class T> void MixedStrategyProfileRep<T>::SetCentroid()
{
for (auto player : m_support.GetGame()->GetPlayers()) {
T center = ((T)1) / ((T)m_support.NumStrategies(player->GetNumber()));
T center = T(1) / T(m_support.GetStrategies(player).size());
for (auto strategy : m_support.GetStrategies(player)) {
(*this)[strategy] = center;
}
Expand Down
6 changes: 3 additions & 3 deletions src/games/gameagg.cc
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ template <class T> T AGGMixedStrategyProfileRep<T>::GetPayoff(int pl) const
for (int i = 0; i < g.aggPtr->getNumPlayers(); ++i) {
for (int j = 0; j < g.aggPtr->getNumActions(i); ++j) {
GameStrategy strategy = this->m_support.GetGame()->GetPlayer(i + 1)->GetStrategy(j + 1);
int ind = this->m_support.m_profileIndex[strategy->GetId()];
int ind = this->m_profileIndex[strategy->GetId()];
s[g.aggPtr->firstAction(i) + j] = (ind == -1) ? (T)0 : this->m_probs[ind];
}
}
Expand All @@ -125,7 +125,7 @@ T AGGMixedStrategyProfileRep<T>::GetPayoffDeriv(int pl, const GameStrategy &ps)
else {
for (int j = 0; j < g.aggPtr->getNumActions(i); ++j) {
GameStrategy strategy = this->m_support.GetGame()->GetPlayer(i + 1)->GetStrategy(j + 1);
const int &ind = this->m_support.m_profileIndex[strategy->GetId()];
const int &ind = this->m_profileIndex[strategy->GetId()];
s[g.aggPtr->firstAction(i) + j] = (ind == -1) ? (T)0 : this->m_probs[ind];
}
}
Expand Down Expand Up @@ -161,7 +161,7 @@ T AGGMixedStrategyProfileRep<T>::GetPayoffDeriv(int pl, const GameStrategy &ps1,
else {
for (int j = 0; j < g.aggPtr->getNumActions(i); ++j) {
GameStrategy strategy = this->m_support.GetGame()->GetPlayer(i + 1)->GetStrategy(j + 1);
const int &ind = this->m_support.m_profileIndex[strategy->GetId()];
const int &ind = this->m_profileIndex[strategy->GetId()];
s[g.aggPtr->firstAction(i) + j] = (ind == -1) ? (T)0 : this->m_probs[ind];
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/games/gamebagg.cc
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ template <class T> T BAGGMixedStrategyProfileRep<T>::GetPayoff(int pl) const
GameStrategy strategy = this->m_support.GetGame()
->GetPlayer(g.baggPtr->typeOffset[i] + tp + 1)
->GetStrategy(j + 1);
const int &ind = this->m_support.m_profileIndex[strategy->GetId()];
int ind = this->m_profileIndex[strategy->GetId()];
s.at(offs) = (ind == -1) ? (T)0 : this->m_probs[ind];
}
}
Expand Down Expand Up @@ -146,7 +146,7 @@ T BAGGMixedStrategyProfileRep<T>::GetPayoffDeriv(int pl, const GameStrategy &ps)
GameStrategy strategy = this->m_support.GetGame()
->GetPlayer(g.baggPtr->typeOffset[i] + tp + 1)
->GetStrategy(j + 1);
const int &ind = this->m_support.m_profileIndex[strategy->GetId()];
int ind = this->m_profileIndex[strategy->GetId()];
s.at(g.baggPtr->firstAction(i, tp) + j) = (ind == -1) ? Rational(0) : this->m_probs[ind];
}
}
Expand Down Expand Up @@ -192,7 +192,7 @@ T BAGGMixedStrategyProfileRep<T>::GetPayoffDeriv(int pl, const GameStrategy &ps1
GameStrategy strategy = this->m_support.GetGame()
->GetPlayer(g.baggPtr->typeOffset[i] + tp + 1)
->GetStrategy(j + 1);
const int &ind = this->m_support.m_profileIndex[strategy->GetId()];
int ind = this->m_profileIndex[strategy->GetId()];
s.at(g.baggPtr->firstAction(i, tp) + j) =
static_cast<T>((ind == -1) ? T(0) : this->m_probs[ind]);
}
Expand Down
6 changes: 4 additions & 2 deletions src/games/stratmixed.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ template <class T> class MixedStrategyProfileRep {
public:
Vector<T> m_probs;
StrategySupportProfile m_support;
/// The index into the strategy profile for a strategy (-1 if not in support)
Array<int> m_profileIndex;
unsigned int m_gameversion;

explicit MixedStrategyProfileRep(const StrategySupportProfile &);
Expand All @@ -44,12 +46,12 @@ template <class T> class MixedStrategyProfileRep {
/// Returns the probability the strategy is played
const T &operator[](const GameStrategy &p_strategy) const
{
return m_probs[m_support.m_profileIndex[p_strategy->GetId()]];
return m_probs[m_profileIndex[p_strategy->GetId()]];
}
/// Returns the probability the strategy is played
T &operator[](const GameStrategy &p_strategy)
{
return m_probs[m_support.m_profileIndex[p_strategy->GetId()]];
return m_probs[m_profileIndex[p_strategy->GetId()]];
}

virtual T GetPayoff(int pl) const = 0;
Expand Down
91 changes: 21 additions & 70 deletions src/games/stratpure.cc
Original file line number Diff line number Diff line change
Expand Up @@ -111,87 +111,38 @@ MixedStrategyProfile<Rational> PureStrategyProfileRep::ToMixedStrategyProfile()
// Lifecycle
//---------------------------------------------------------------------------

StrategyProfileIterator::StrategyProfileIterator(const StrategySupportProfile &p_support)
: m_atEnd(false), m_support(p_support), m_currentStrat(m_support.GetGame()->NumPlayers()),
m_profile(m_support.GetGame()->NewPureStrategyProfile()), m_frozen1(0), m_frozen2(0)
{
First();
}

StrategyProfileIterator::StrategyProfileIterator(const StrategySupportProfile &p_support, int pl,
int st)
: m_atEnd(false), m_support(p_support), m_currentStrat(m_support.GetGame()->NumPlayers()),
m_profile(m_support.GetGame()->NewPureStrategyProfile()), m_frozen1(pl), m_frozen2(0)
{
m_currentStrat[pl] = st;
m_profile->SetStrategy(m_support.GetStrategy(pl, st));
First();
}

StrategyProfileIterator::StrategyProfileIterator(const StrategySupportProfile &p_support,
const GameStrategy &p_strategy)
: m_atEnd(false), m_support(p_support), m_currentStrat(p_support.GetGame()->NumPlayers()),
m_profile(p_support.GetGame()->NewPureStrategyProfile()),
m_frozen1(p_strategy->GetPlayer()->GetNumber()), m_frozen2(0)
{
m_currentStrat[m_frozen1] = p_strategy->GetNumber();
m_profile->SetStrategy(p_strategy);
First();
}

StrategyProfileIterator::StrategyProfileIterator(const StrategySupportProfile &p_support, int pl1,
int st1, int pl2, int st2)
: m_atEnd(false), m_support(p_support), m_currentStrat(m_support.GetGame()->NumPlayers()),
m_profile(m_support.GetGame()->NewPureStrategyProfile()), m_frozen1(pl1), m_frozen2(pl2)
{
m_currentStrat[pl1] = st1;
m_profile->SetStrategy(m_support.GetStrategy(pl1, st1));
m_currentStrat[pl2] = st2;
m_profile->SetStrategy(m_support.GetStrategy(pl2, st2));
First();
}

//---------------------------------------------------------------------------
// Iteration
//---------------------------------------------------------------------------

void StrategyProfileIterator::First()
const std::vector<GameStrategy> &p_frozen)
: m_support(p_support), m_profile(p_support.GetGame()->NewPureStrategyProfile()),
m_frozen(p_frozen)
{
for (int pl = 1; pl <= m_support.GetGame()->NumPlayers(); pl++) {
if (pl == m_frozen1 || pl == m_frozen2) {
continue;
for (auto strategy : m_frozen) {
m_profile->SetStrategy(strategy);
}
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);
m_currentStrat[player] = m_support.GetStrategies(player).begin();
m_profile->SetStrategy(*m_currentStrat[player]);
}
m_profile->SetStrategy(m_support.GetStrategy(pl, 1));
m_currentStrat[pl] = 1;
}
}

void StrategyProfileIterator::operator++()
{
int pl = 1;

while (true) {
if (pl == m_frozen1 || pl == m_frozen2) {
pl++;
if (pl > m_support.GetGame()->NumPlayers()) {
m_atEnd = true;
return;
}
continue;
}

if (m_currentStrat[pl] < m_support.NumStrategies(pl)) {
m_profile->SetStrategy(m_support.GetStrategy(pl, ++(m_currentStrat[pl])));
return;
}
m_profile->SetStrategy(m_support.GetStrategy(pl, 1));
m_currentStrat[pl] = 1;
pl++;
if (pl > m_support.GetGame()->NumPlayers()) {
m_atEnd = true;
for (auto player : m_unfrozen) {
++m_currentStrat[player];
if (m_currentStrat[player] != m_support.GetStrategies(player).end()) {
m_profile->SetStrategy(*m_currentStrat[player]);
return;
}
m_currentStrat[player] = m_support.GetStrategies(player).begin();
m_profile->SetStrategy(*m_currentStrat[player]);
}
m_atEnd = true;
}

} // namespace Gambit
21 changes: 7 additions & 14 deletions src/games/stratpure.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,26 +137,19 @@ class StrategyProfileIterator {
friend class GameTableRep;

private:
bool m_atEnd;
bool m_atEnd{false};
StrategySupportProfile m_support;
Array<int> m_currentStrat;
std::map<GamePlayer, StrategySupportProfile::Support::const_iterator> m_currentStrat;
PureStrategyProfile m_profile;
int m_frozen1, m_frozen2;

/// Reset the iterator to the first contingency (this is called by ctors)
void First();
std::vector<GamePlayer> m_unfrozen;
std::vector<GameStrategy> m_frozen;

public:
/// @name Lifecycle
//@{
/// Construct a new iterator on the support, with no strategies held fixed
explicit StrategyProfileIterator(const StrategySupportProfile &);
/// Construct a new iterator on the support, fixing player pl's strategy
StrategyProfileIterator(const StrategySupportProfile &s, int pl, int st);
/// Construct a new iterator on the support, fixing the given strategy
StrategyProfileIterator(const StrategySupportProfile &, const GameStrategy &);
/// Construct a new iterator on the support, fixing two players' strategies
StrategyProfileIterator(const StrategySupportProfile &s, int pl1, int st1, int pl2, int st2);
/// Construct a new iterator on the support
explicit StrategyProfileIterator(const StrategySupportProfile &,
const std::vector<GameStrategy> &p_frozen = {});
//@}

/// @name Iteration and data access
Expand Down
Loading

0 comments on commit 008b7e8

Please sign in to comment.