Skip to content

Commit 694052b

Browse files
committed
Convert bespoke game element reference counting to use std::shared_ptr.
For historical reasons Gambit has had its own `GameObject` base class, which combined tracking of object validity (i.e. not having been deleted from its game) with reference-counting (for references to the object in calling code). This modernises this mechanism and more clearly separates the two concerns: * Reference-counting is now done by using `std::shared_ptr`. Game objects are 'owned' by the collections to which they belong, in terms of whether the game itself contains a reference to an allocated object. * Rather than having a separate base class which provides a validity flag (as that would be all that remains in such a class), `GameObject` has been removed and instead the `GameObjectPtr` template simply assumes that game elements provide an `IsValid` member. Convert player ownership model to shared_ptr Convert outcome ownership model to shared_ptr Make chance player shared at allocation. Convert strategy ownership model to shared_ptr Move appending of information set out of ctor. Convert action ownership model to shared_ptr Convert infoset ownership model to shared_ptr Convert node ownership model to shared_ptr Remove old GameObject Remove reference to smart pointer in class names
1 parent 842504c commit 694052b

File tree

13 files changed

+432
-434
lines changed

13 files changed

+432
-434
lines changed

src/games/behavmixed.cc

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ void MixedBehaviorProfile<T>::RealizationProbs(const MixedStrategyProfile<T> &mp
9898
if (node->GetPlayer() && !node->GetPlayer()->IsChance()) {
9999
if (node->GetPlayer() == player) {
100100
if (contains(actions, node->m_infoset) &&
101-
actions.at(node->GetInfoset()) == static_cast<int>(i)) {
101+
actions.at(node->GetInfoset().get()) == static_cast<int>(i)) {
102102
prob = T(1);
103103
}
104104
else {
@@ -117,12 +117,12 @@ void MixedBehaviorProfile<T>::RealizationProbs(const MixedStrategyProfile<T> &mp
117117
prob = T(node->m_infoset->GetActionProb(node->m_infoset->GetAction(i)));
118118
}
119119

120-
GameNodeRep *child = node->m_children[i - 1];
120+
auto child = node->m_children[i - 1];
121121

122-
map_bvals[child] = prob * map_bvals[node];
122+
map_bvals[child] = prob * map_bvals[node->shared_from_this()];
123123
map_nvals[child] += map_bvals[child];
124124

125-
RealizationProbs(mp, player, actions, child, map_nvals, map_bvals);
125+
RealizationProbs(mp, player, actions, child.get(), map_nvals, map_bvals);
126126
}
127127
}
128128

@@ -141,7 +141,7 @@ MixedBehaviorProfile<T>::MixedBehaviorProfile(const MixedStrategyProfile<T> &p_p
141141
}
142142
}
143143

144-
GameNodeRep *root = m_support.GetGame()->GetRoot();
144+
GameNodeRep *root = m_support.GetGame()->GetRoot().get();
145145

146146
const StrategySupportProfile &support = p_profile.GetSupport();
147147
GameRep *game = m_support.GetGame().get();
@@ -151,11 +151,11 @@ MixedBehaviorProfile<T>::MixedBehaviorProfile(const MixedStrategyProfile<T> &p_p
151151
for (auto strategy : support.GetStrategies(player)) {
152152
if (p_profile[strategy] > T(0)) {
153153
const auto &actions = strategy->m_behav;
154-
map_bvals[root] = p_profile[strategy];
154+
map_bvals[root->shared_from_this()] = p_profile[strategy];
155155
RealizationProbs(p_profile, player, actions, root, map_nvals, map_bvals);
156156
}
157157
}
158-
map_nvals[root] = T(1); // set the root nval
158+
map_nvals[root->shared_from_this()] = T(1); // set the root nval
159159
auto root = m_support.GetGame()->GetRoot();
160160
BehaviorStrat(player, root, map_nvals, map_bvals);
161161
}

src/games/game.cc

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ namespace Gambit {
4242
GameOutcomeRep::GameOutcomeRep(GameRep *p_game, int p_number) : m_game(p_game), m_number(p_number)
4343
{
4444
for (const auto &player : m_game->m_players) {
45-
m_payoffs[player] = Number();
45+
m_payoffs[player.get()] = Number();
4646
}
4747
}
4848

@@ -52,11 +52,11 @@ GameOutcomeRep::GameOutcomeRep(GameRep *p_game, int p_number) : m_game(p_game),
5252

5353
GameAction GameStrategyRep::GetAction(const GameInfoset &p_infoset) const
5454
{
55-
if (p_infoset->GetPlayer() != m_player) {
55+
if (p_infoset->GetPlayer().get() != m_player) {
5656
throw MismatchException();
5757
}
5858
try {
59-
return *std::next(p_infoset->GetActions().cbegin(), m_behav.at(p_infoset) - 1);
59+
return *std::next(p_infoset->GetActions().cbegin(), m_behav.at(p_infoset.get()) - 1);
6060
}
6161
catch (std::out_of_range &) {
6262
return nullptr;
@@ -71,7 +71,7 @@ GamePlayerRep::GamePlayerRep(GameRep *p_game, int p_id, int p_strats)
7171
: m_game(p_game), m_number(p_id)
7272
{
7373
for (int j = 1; j <= p_strats; j++) {
74-
m_strategies.push_back(new GameStrategyRep(this, j, ""));
74+
m_strategies.push_back(std::make_shared<GameStrategyRep>(this, j, ""));
7575
}
7676
}
7777

@@ -87,11 +87,12 @@ GamePlayerRep::~GamePlayerRep()
8787

8888
void GamePlayerRep::MakeStrategy(const std::map<GameInfosetRep *, int> &behav)
8989
{
90-
auto *strategy = new GameStrategyRep(this, m_strategies.size() + 1, "");
90+
auto strategy = std::make_shared<GameStrategyRep>(this, m_strategies.size() + 1, "");
9191
strategy->m_behav = behav;
9292
for (const auto &infoset : m_infosets) {
93-
strategy->m_label +=
94-
(contains(strategy->m_behav, infoset)) ? std::to_string(strategy->m_behav[infoset]) : "*";
93+
strategy->m_label += (contains(strategy->m_behav, infoset.get()))
94+
? std::to_string(strategy->m_behav[infoset.get()])
95+
: "*";
9596
}
9697
if (strategy->m_label.empty()) {
9798
strategy->m_label = "*";
@@ -109,7 +110,7 @@ void GamePlayerRep::MakeReducedStrats(GameNodeRep *n, GameNodeRep *nn,
109110
if (!contains(behav, n->m_infoset)) {
110111
// we haven't visited this infoset before
111112
for (size_t i = 1; i <= n->m_children.size(); i++) {
112-
GameNodeRep *m = n->m_children[i - 1];
113+
GameNodeRep *m = n->m_children[i - 1].get();
113114
whichbranch[n] = m;
114115
behav[n->m_infoset] = i;
115116
MakeReducedStrats(m, nn, behav, ptr, whichbranch);
@@ -118,7 +119,8 @@ void GamePlayerRep::MakeReducedStrats(GameNodeRep *n, GameNodeRep *nn,
118119
}
119120
else {
120121
// we have visited this infoset, take same action
121-
MakeReducedStrats(n->m_children[behav[n->m_infoset] - 1], nn, behav, ptr, whichbranch);
122+
MakeReducedStrats(n->m_children[behav[n->m_infoset] - 1].get(), nn, behav, ptr,
123+
whichbranch);
122124
}
123125
}
124126
else {
@@ -128,12 +130,13 @@ void GamePlayerRep::MakeReducedStrats(GameNodeRep *n, GameNodeRep *nn,
128130
else {
129131
ptr.erase(n);
130132
}
131-
whichbranch[n] = n->m_children.front();
132-
MakeReducedStrats(n->m_children.front(), n->m_children.front(), behav, ptr, whichbranch);
133+
whichbranch[n] = n->m_children.front().get();
134+
MakeReducedStrats(n->m_children.front().get(), n->m_children.front().get(), behav, ptr,
135+
whichbranch);
133136
}
134137
}
135138
else if (nn) {
136-
GameNodeRep *m;
139+
GameNode m;
137140
for (;; nn = whichbranch.at(ptr.at(nn->m_parent))) {
138141
m = nn->GetNextSibling();
139142
if (m || !contains(ptr, nn->m_parent)) {
@@ -142,8 +145,8 @@ void GamePlayerRep::MakeReducedStrats(GameNodeRep *n, GameNodeRep *nn,
142145
}
143146
if (m) {
144147
GameNodeRep *mm = whichbranch.at(m->m_parent);
145-
whichbranch[m->m_parent] = m;
146-
MakeReducedStrats(m, m, behav, ptr, whichbranch);
148+
whichbranch[m->m_parent] = m.get();
149+
MakeReducedStrats(m.get(), m.get(), behav, ptr, whichbranch);
147150
whichbranch[m->m_parent] = mm;
148151
}
149152
else {
@@ -160,8 +163,9 @@ size_t GamePlayerRep::NumSequences() const
160163
if (!m_game->IsTree()) {
161164
throw UndefinedException();
162165
}
163-
return std::transform_reduce(m_infosets.cbegin(), m_infosets.cend(), 1, std::plus<>(),
164-
[](const GameInfosetRep *s) { return s->m_actions.size(); });
166+
return std::transform_reduce(
167+
m_infosets.cbegin(), m_infosets.cend(), 1, std::plus<>(),
168+
[](const std::shared_ptr<GameInfosetRep> &s) { return s->m_actions.size(); });
165169
}
166170

167171
//========================================================================
@@ -318,8 +322,8 @@ MixedStrategyProfile<T>::MixedStrategyProfile(const MixedBehaviorProfile<T> &p_p
318322
for (const auto &strategy : player->m_strategies) {
319323
auto prob = static_cast<T>(1);
320324
for (const auto &infoset : player->m_infosets) {
321-
if (strategy->m_behav[infoset] > 0) {
322-
prob *= p_profile[infoset->GetAction(strategy->m_behav[infoset])];
325+
if (strategy->m_behav[infoset.get()] > 0) {
326+
prob *= p_profile[infoset->GetAction(strategy->m_behav[infoset.get()])];
323327
}
324328
}
325329
(*m_rep)[strategy] = prob;
@@ -342,7 +346,7 @@ MixedStrategyProfile<T>::operator=(const MixedStrategyProfile<T> &p_profile)
342346
// MixedStrategyProfile<T>: General data access
343347
//========================================================================
344348

345-
template <class T> Vector<T> MixedStrategyProfile<T>::operator[](const GamePlayer &p_player) const
349+
template <class T> Vector<T> MixedStrategyProfile<T>::GetStrategy(const GamePlayer &p_player) const
346350
{
347351
CheckVersion();
348352
auto strategies = m_rep->m_support.GetStrategies(p_player);

0 commit comments

Comments
 (0)