Skip to content

Commit 1cb87a4

Browse files
authored
Merge maint16_4 changes into master
2 parents f9c99cf + 39e4286 commit 1cb87a4

8 files changed

Lines changed: 57 additions & 30 deletions

File tree

ChangeLog

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@
3333
when changing the number of strategies in the game (#571)
3434
- Fixed improper shared pointer handling when writing a .nfg file based on a game in
3535
extensive form.
36+
- Fixed a regression in the GUI in which unique action labels were not being generated when
37+
adding a move via drag-and-drop of a player icon (#618)
38+
- Fixed a regression generating null pointer dereference errors when setting the outcome of
39+
a node to the null outcome (#625, #647)
40+
- Fixed a regression in calculating payoff quantities for mixed strategy profiles derived from
41+
mixed behavior profiles (#616)
3642

3743

3844
## [16.3.2] - unreleased

src/games/game.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -781,15 +781,17 @@ class GameRep : public std::enable_shared_from_this<GameRep> {
781781
{
782782
throw UndefinedException();
783783
}
784-
virtual GameInfoset AppendMove(GameNode p_node, GamePlayer p_player, int p_actions)
784+
virtual GameInfoset AppendMove(GameNode p_node, GamePlayer p_player, int p_actions,
785+
bool p_generateLabels = false)
785786
{
786787
throw UndefinedException();
787788
}
788789
virtual GameInfoset AppendMove(GameNode p_node, GameInfoset p_infoset)
789790
{
790791
throw UndefinedException();
791792
}
792-
virtual GameInfoset InsertMove(GameNode p_node, GamePlayer p_player, int p_actions)
793+
virtual GameInfoset InsertMove(GameNode p_node, GamePlayer p_player, int p_actions,
794+
bool p_generateLabels = false)
793795
{
794796
throw UndefinedException();
795797
}
@@ -811,7 +813,10 @@ class GameRep : public std::enable_shared_from_this<GameRep> {
811813
throw UndefinedException();
812814
}
813815
virtual void DeleteAction(GameAction) { throw UndefinedException(); }
814-
virtual void SetOutcome(GameNode, const GameOutcome &p_outcome) { throw UndefinedException(); }
816+
virtual void SetOutcome(const GameNode &p_node, const GameOutcome &p_outcome)
817+
{
818+
throw UndefinedException();
819+
}
815820

816821
/// @name Dimensions of the game
817822
//@{

src/games/gameobject.h

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,10 @@ template <class T> class GameObjectPtr {
9696
/// game element held by this object and throws an exception if the object is the
9797
/// null object, or is no longer valid (has been removed from the game)
9898
///
99-
/// @exception NullException if the object holds a reference to a null element
10099
/// @exception InvalidObjectException if the element referred to has been deleted from its game
101100
std::shared_ptr<T> get_shared() const
102101
{
103-
if (!m_rep) {
104-
throw NullException();
105-
}
106-
if (!m_rep->IsValid()) {
102+
if (m_rep && !m_rep->IsValid()) {
107103
throw InvalidObjectException();
108104
}
109105
return m_rep;
@@ -129,16 +125,16 @@ template <class T> class GameObjectPtr {
129125
}
130126

131127
bool operator==(const GameObjectPtr<T> &r) const { return (m_rep == r.m_rep); }
132-
bool operator==(const std::shared_ptr<T> r) const { return (m_rep == r); }
133-
bool operator==(const std::shared_ptr<const T> r) const { return (m_rep == r); }
134-
bool operator==(const std::nullptr_t) const { return !bool(m_rep); }
128+
bool operator==(const std::shared_ptr<T> &r) const { return (m_rep == r); }
129+
bool operator==(const std::shared_ptr<const T> &r) const { return (m_rep == r); }
130+
bool operator==(const std::nullptr_t &) const { return m_rep == nullptr; }
135131
bool operator!=(const GameObjectPtr<T> &r) const { return (m_rep != r.m_rep); }
136-
bool operator!=(const std::shared_ptr<T> r) const { return (m_rep != r); }
137-
bool operator!=(const std::shared_ptr<const T> r) const { return (m_rep != r); }
138-
bool operator!=(const std::nullptr_t) const { return bool(m_rep); }
132+
bool operator!=(const std::shared_ptr<T> &r) const { return (m_rep != r); }
133+
bool operator!=(const std::shared_ptr<const T> &r) const { return (m_rep != r); }
134+
bool operator!=(const std::nullptr_t &) const { return m_rep != nullptr; }
139135
bool operator<(const GameObjectPtr<T> &r) const { return (m_rep < r.m_rep); }
140136

141-
operator bool() const noexcept { return bool(m_rep); }
137+
operator bool() const noexcept { return m_rep != nullptr; }
142138
operator std::shared_ptr<T>() const { return m_rep; }
143139
};
144140

src/games/gametree.cc

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -345,17 +345,17 @@ void GameNodeRep::DeleteOutcome(GameOutcomeRep *outc)
345345
}
346346
}
347347

348-
void GameTreeRep::SetOutcome(GameNode p_node, const GameOutcome &p_outcome)
348+
void GameTreeRep::SetOutcome(const GameNode &p_node, const GameOutcome &p_outcome)
349349
{
350-
IncrementVersion();
351350
if (p_node->m_game != this) {
352351
throw MismatchException();
353352
}
354353
if (p_outcome && p_outcome->m_game != this) {
355354
throw MismatchException();
356355
}
357-
if (p_outcome.get() != p_node->m_outcome) {
358-
p_node->m_outcome = p_outcome.get();
356+
if (const auto newOutcome = p_outcome.get_shared().get(); newOutcome != p_node->m_outcome) {
357+
p_node->m_outcome = newOutcome;
358+
IncrementVersion();
359359
ClearComputedValues();
360360
}
361361
}
@@ -592,7 +592,8 @@ GameInfoset GameTreeRep::LeaveInfoset(GameNode p_node)
592592
return node->m_infoset->shared_from_this();
593593
}
594594

595-
GameInfoset GameTreeRep::AppendMove(GameNode p_node, GamePlayer p_player, int p_actions)
595+
GameInfoset GameTreeRep::AppendMove(GameNode p_node, GamePlayer p_player, int p_actions,
596+
bool p_generateLabels)
596597
{
597598
GameNodeRep *node = p_node.get();
598599
if (p_actions <= 0 || !node->m_children.empty()) {
@@ -606,6 +607,10 @@ GameInfoset GameTreeRep::AppendMove(GameNode p_node, GamePlayer p_player, int p_
606607
auto newInfoset = std::make_shared<GameInfosetRep>(this, p_player->m_infosets.size() + 1,
607608
p_player.get(), p_actions);
608609
p_player->m_infosets.push_back(newInfoset);
610+
if (p_generateLabels) {
611+
std::for_each(newInfoset->m_actions.begin(), newInfoset->m_actions.end(),
612+
[act = 1](const GameAction &a) mutable { a->SetLabel(std::to_string(act++)); });
613+
}
609614
return AppendMove(p_node, newInfoset);
610615
}
611616

@@ -632,7 +637,8 @@ GameInfoset GameTreeRep::AppendMove(GameNode p_node, GameInfoset p_infoset)
632637
return node->m_infoset->shared_from_this();
633638
}
634639

635-
GameInfoset GameTreeRep::InsertMove(GameNode p_node, GamePlayer p_player, int p_actions)
640+
GameInfoset GameTreeRep::InsertMove(GameNode p_node, GamePlayer p_player, int p_actions,
641+
bool p_generateLabels)
636642
{
637643
if (p_actions <= 0) {
638644
throw UndefinedException();
@@ -645,6 +651,10 @@ GameInfoset GameTreeRep::InsertMove(GameNode p_node, GamePlayer p_player, int p_
645651
auto newInfoset = std::make_shared<GameInfosetRep>(this, p_player->m_infosets.size() + 1,
646652
p_player.get(), p_actions);
647653
p_player->m_infosets.push_back(newInfoset);
654+
if (p_generateLabels) {
655+
std::for_each(newInfoset->m_actions.begin(), newInfoset->m_actions.end(),
656+
[act = 1](const GameAction &a) mutable { a->SetLabel(std::to_string(act++)); });
657+
}
648658
return InsertMove(p_node, newInfoset);
649659
}
650660

src/games/gametree.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,11 @@ class GameTreeRep : public GameExplicitRep {
136136

137137
/// @name Modification
138138
//@{
139-
GameInfoset AppendMove(GameNode p_node, GamePlayer p_player, int p_actions) override;
139+
GameInfoset AppendMove(GameNode p_node, GamePlayer p_player, int p_actions,
140+
bool p_generateLabels = false) override;
140141
GameInfoset AppendMove(GameNode p_node, GameInfoset p_infoset) override;
141-
GameInfoset InsertMove(GameNode p_node, GamePlayer p_player, int p_actions) override;
142+
GameInfoset InsertMove(GameNode p_node, GamePlayer p_player, int p_actions,
143+
bool p_generateLabels = false) override;
142144
GameInfoset InsertMove(GameNode p_node, GameInfoset p_infoset) override;
143145
void CopyTree(GameNode dest, GameNode src) override;
144146
void MoveTree(GameNode dest, GameNode src) override;
@@ -151,7 +153,7 @@ class GameTreeRep : public GameExplicitRep {
151153
Game SetChanceProbs(const GameInfoset &, const Array<Number> &) override;
152154
GameAction InsertAction(GameInfoset, GameAction p_where = nullptr) override;
153155
void DeleteAction(GameAction) override;
154-
void SetOutcome(GameNode, const GameOutcome &p_outcome) override;
156+
void SetOutcome(const GameNode &p_node, const GameOutcome &p_outcome) override;
155157

156158
std::vector<GameNode> GetPlays(GameNode node) const override;
157159
std::vector<GameNode> GetPlays(GameInfoset infoset) const override;

src/games/stratmixed.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ template <class T> class MixedStrategyProfileRep {
4949
return m_probs[m_profileIndex.at(p_strategy)];
5050
}
5151
/// Returns the probability the strategy is played
52-
T &operator[](const GameStrategy &p_strategy) { return m_probs[m_profileIndex.at(p_strategy)]; }
52+
T &operator[](const GameStrategy &p_strategy)
53+
{
54+
InvalidateCache();
55+
return m_probs[m_profileIndex.at(p_strategy)];
56+
}
5357
/// Set the strategy of the corresponding player to a pure strategy
5458
void SetStrategy(const GameStrategy &p_strategy)
5559
{

src/gui/gamedoc.cc

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -547,10 +547,7 @@ void GameDocument::DoAppendMove(GameNode p_node, GameInfoset p_infoset)
547547

548548
void GameDocument::DoInsertMove(GameNode p_node, GamePlayer p_player, unsigned int p_actions)
549549
{
550-
const GameInfoset infoset = m_game->InsertMove(p_node, p_player, p_actions);
551-
auto actions = infoset->GetActions();
552-
std::for_each(actions.begin(), actions.end(),
553-
[act = 1](const GameAction &a) mutable { a->SetLabel(std::to_string(act)); });
550+
m_game->InsertMove(p_node, p_player, p_actions, true);
554551
m_game->SortInfosets();
555552
UpdateViews(GBT_DOC_MODIFIED_GAME);
556553
}

tests/test_node.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,17 @@ def test_get_infoset():
1919
def test_get_outcome():
2020
"""Test to ensure that we can retrieve an outcome for a given node"""
2121
game = games.read_from_file("basic_extensive_game.efg")
22-
assert game.root.children[0].children[1].children[0].outcome == game.outcomes[1]
22+
assert game.root.children[0].children[1].children[0].outcome == game.outcomes["Outcome 1"]
2323
assert game.root.outcome is None
2424

2525

26+
def test_set_outcome_null():
27+
"""Test to set an outcome to the null outcome."""
28+
game = games.read_from_file("basic_extensive_game.efg")
29+
game.set_outcome(game.root.children[0].children[0].children[0], None)
30+
assert game.root.children[0].children[0].children[0].outcome is None
31+
32+
2633
def test_get_player():
2734
"""Test to ensure that we can retrieve a player for a given node"""
2835
game = games.read_from_file("basic_extensive_game.efg")

0 commit comments

Comments
 (0)