Skip to content

Commit 76cb7c1

Browse files
committed
Implement collection of members at infoset
1 parent fe39263 commit 76cb7c1

8 files changed

Lines changed: 105 additions & 54 deletions

File tree

src/games/game.h

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,68 @@ class GameInfosetRep : public GameObject {
261261
iterator cend() const { return {m_infoset, (m_infoset) ? m_infoset->m_actions.size() : 0}; }
262262
};
263263

264+
class Members {
265+
GameInfoset m_infoset{nullptr};
266+
267+
public:
268+
class iterator {
269+
GameInfoset m_infoset{nullptr};
270+
size_t m_index{0};
271+
272+
public:
273+
using iterator_category = std::bidirectional_iterator_tag;
274+
using difference_type = std::ptrdiff_t;
275+
using value_type = GameNode;
276+
using pointer = value_type *;
277+
using reference = value_type &;
278+
279+
iterator() = default;
280+
iterator(const GameInfoset &p_infoset, size_t p_index = 0)
281+
: m_infoset(p_infoset), m_index(p_index)
282+
{
283+
}
284+
iterator(const iterator &) = default;
285+
~iterator() = default;
286+
iterator &operator=(const iterator &) = default;
287+
288+
bool operator==(const iterator &p_iter) const
289+
{
290+
return m_infoset == p_iter.m_infoset && m_index == p_iter.m_index;
291+
}
292+
bool operator!=(const iterator &p_iter) const
293+
{
294+
return m_infoset != p_iter.m_infoset || m_index != p_iter.m_index;
295+
}
296+
297+
iterator &operator++()
298+
{
299+
m_index++;
300+
return *this;
301+
}
302+
iterator &operator--()
303+
{
304+
m_index--;
305+
return *this;
306+
}
307+
GameNode operator*() const { return m_infoset->m_members.at(m_index); }
308+
};
309+
310+
Members() = default;
311+
explicit Members(const GameInfoset &p_infoset) : m_infoset(p_infoset) {}
312+
Members(const Members &) = default;
313+
~Members() = default;
314+
Members &operator=(const Members &) = default;
315+
316+
size_t size() const { return m_infoset->m_members.size(); }
317+
GameNode front() const { return m_infoset->m_members.front(); }
318+
GameNode back() const { return m_infoset->m_members.back(); }
319+
320+
iterator begin() const { return {m_infoset, 0}; }
321+
iterator end() const { return {m_infoset, (m_infoset) ? m_infoset->m_members.size() : 0}; }
322+
iterator cbegin() const { return {m_infoset, 0}; }
323+
iterator cend() const { return {m_infoset, (m_infoset) ? m_infoset->m_members.size() : 0}; }
324+
};
325+
264326
Game GetGame() const;
265327
int GetNumber() const { return m_number; }
266328

@@ -280,8 +342,8 @@ class GameInfosetRep : public GameObject {
280342
//@}
281343

282344
size_t NumMembers() const { return m_members.size(); }
283-
GameNode GetMember(int p_index) const;
284-
Array<GameNode> GetMembers() const;
345+
GameNode GetMember(int p_index) const { return m_members.at(p_index - 1); }
346+
Members GetMembers() const { return Members(this); }
285347

286348
bool Precedes(GameNode) const;
287349

@@ -922,6 +984,8 @@ inline void GameOutcomeRep::SetPayoff(const GamePlayer &p_player, const Number &
922984
inline GamePlayer GameStrategyRep::GetPlayer() const { return m_player; }
923985

924986
inline Game GameInfosetRep::GetGame() const { return m_game; }
987+
inline GamePlayer GameInfosetRep::GetPlayer() const { return m_player; }
988+
inline bool GameInfosetRep::IsChanceInfoset() const { return m_player->IsChance(); }
925989

926990
inline Game GamePlayerRep::GetGame() const { return m_game; }
927991
inline size_t GamePlayerRep::NumStrategies() const

src/games/gametree.cc

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -281,20 +281,6 @@ void GameTreeRep::Reveal(GameInfoset p_atInfoset, GamePlayer p_player)
281281
Canonicalize();
282282
}
283283

284-
GameNode GameInfosetRep::GetMember(int p_index) const { return m_members[p_index - 1]; }
285-
286-
Array<GameNode> GameInfosetRep::GetMembers() const
287-
{
288-
Array<GameNode> ret(m_members.size());
289-
std::transform(m_members.cbegin(), m_members.cend(), ret.begin(),
290-
[](const GameNodeRep *n) -> GameNode { return n; });
291-
return ret;
292-
}
293-
294-
GamePlayer GameInfosetRep::GetPlayer() const { return m_player; }
295-
296-
bool GameInfosetRep::IsChanceInfoset() const { return m_player->IsChance(); }
297-
298284
//========================================================================
299285
// class GameNodeRep
300286
//========================================================================
@@ -373,7 +359,7 @@ bool GameNodeRep::IsSuccessorOf(GameNode p_node) const
373359
bool GameNodeRep::IsSubgameRoot() const
374360
{
375361
// First take care of a couple easy cases
376-
if (m_children.empty() || m_infoset->NumMembers() > 1) {
362+
if (m_children.empty() || m_infoset->m_members.size() > 1) {
377363
return false;
378364
}
379365
if (!m_parent) {
@@ -385,9 +371,10 @@ bool GameNodeRep::IsSubgameRoot() const
385371
// or all members do not succeed the node in the tree.
386372
for (auto player : m_game->GetPlayers()) {
387373
for (auto infoset : player->GetInfosets()) {
388-
const bool precedes = infoset->GetMember(1)->IsSuccessorOf(const_cast<GameNodeRep *>(this));
389-
for (size_t mem = 2; mem <= infoset->NumMembers(); mem++) {
390-
if (infoset->GetMember(mem)->IsSuccessorOf(const_cast<GameNodeRep *>(this)) != precedes) {
374+
const bool precedes =
375+
infoset->m_members.front()->IsSuccessorOf(const_cast<GameNodeRep *>(this));
376+
for (size_t mem = 0; mem < infoset->m_members.size(); mem++) {
377+
if (infoset->m_members[mem]->IsSuccessorOf(const_cast<GameNodeRep *>(this)) != precedes) {
391378
return false;
392379
}
393380
}
@@ -755,9 +742,9 @@ bool GameTreeRep::IsPerfectRecall(GameInfoset &s1, GameInfoset &s2) const
755742
bool precedes = false;
756743
GameAction action = nullptr;
757744

758-
for (size_t m = 1; m <= iset2->NumMembers(); m++) {
745+
for (size_t m = 1; m <= iset2->m_members.size(); m++) {
759746
size_t n;
760-
for (n = 1; n <= iset1->NumMembers(); n++) {
747+
for (n = 1; n <= iset1->m_members.size(); n++) {
761748
if (iset2->GetMember(m)->IsSuccessorOf(iset1->GetMember(n)) &&
762749
iset1->GetMember(n) != iset2->GetMember(m)) {
763750
precedes = true;
@@ -781,7 +768,7 @@ bool GameTreeRep::IsPerfectRecall(GameInfoset &s1, GameInfoset &s2) const
781768
return false;
782769
}
783770

784-
if (n > iset1->NumMembers() && precedes) {
771+
if (n > iset1->m_members.size() && precedes) {
785772
s1 = iset1;
786773
s2 = iset2;
787774
return false;

src/gui/dleditmove.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,10 @@ wxBEGIN_EVENT_TABLE(gbtEditMoveDialog,
160160
labelSizer->Add(m_infosetName, 1, wxALL | wxEXPAND, 5);
161161
topSizer->Add(labelSizer, 0, wxALL | wxEXPAND, 0);
162162

163-
topSizer->Add(
164-
new wxStaticText(this, wxID_STATIC,
165-
wxString::Format(_("Number of members: %d"), p_infoset->NumMembers())),
166-
0, wxALL | wxALIGN_CENTER, 5);
163+
topSizer->Add(new wxStaticText(
164+
this, wxID_STATIC,
165+
wxString::Format(_("Number of members: %d"), p_infoset->GetMembers().size())),
166+
0, wxALL | wxALIGN_CENTER, 5);
167167

168168
auto *playerSizer = new wxBoxSizer(wxHORIZONTAL);
169169
playerSizer->Add(new wxStaticText(this, wxID_STATIC, _("Belongs to player")), 0,

src/gui/dlinsertmove.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ gbtInsertMoveDialog::gbtInsertMoveDialog(wxWindow *p_parent, gbtGameDocument *p_
6868
s += wxT("s");
6969
}
7070

71-
s += wxString::Format(wxT(", %d member node"), infoset->NumMembers());
72-
if (infoset->NumMembers() > 1) {
71+
s += wxString::Format(wxT(", %d member node"), infoset->GetMembers().size());
72+
if (infoset->GetMembers().size() > 1) {
7373
s += wxT("s)");
7474
}
7575
else {
@@ -144,8 +144,8 @@ void gbtInsertMoveDialog::OnPlayer(wxCommandEvent &)
144144
s += wxT("s");
145145
}
146146

147-
s += wxString::Format(wxT(", %d member node"), infoset->NumMembers());
148-
if (infoset->NumMembers() > 1) {
147+
s += wxString::Format(wxT(", %d member node"), infoset->GetMembers().size());
148+
if (infoset->GetMembers().size() > 1) {
149149
s += wxT("s)");
150150
}
151151
else {

src/gui/efgdisplay.cc

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -375,15 +375,10 @@ static GameNode PriorSameIset(const GameNode &n)
375375
if (!iset) {
376376
return nullptr;
377377
}
378-
for (size_t i = 1; i <= iset->NumMembers(); i++) {
379-
if (iset->GetMember(i) == n) {
380-
if (i > 1) {
381-
return iset->GetMember(i - 1);
382-
}
383-
else {
384-
return nullptr;
385-
}
386-
}
378+
auto members = iset->GetMembers();
379+
auto node = std::find(members.begin(), members.end(), n);
380+
if (node != members.begin()) {
381+
return *std::prev(node);
387382
}
388383
return nullptr;
389384
}
@@ -394,15 +389,10 @@ static GameNode NextSameIset(const GameNode &n)
394389
if (!iset) {
395390
return nullptr;
396391
}
397-
for (size_t i = 1; i <= iset->NumMembers(); i++) {
398-
if (iset->GetMember(i) == n) {
399-
if (i < iset->NumMembers()) {
400-
return iset->GetMember(i + 1);
401-
}
402-
else {
403-
return nullptr;
404-
}
405-
}
392+
auto members = iset->GetMembers();
393+
auto node = std::find(members.begin(), members.end(), n);
394+
if (node != members.end()) {
395+
return *std::next(node);
406396
}
407397
return nullptr;
408398
}

src/pygambit/gambit.pxd

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,16 @@ cdef extern from "games/game.h":
117117
iterator begin() except +
118118
iterator end() except +
119119

120+
cppclass Members:
121+
cppclass iterator:
122+
c_GameNode operator *()
123+
iterator operator++()
124+
bint operator ==(iterator)
125+
bint operator !=(iterator)
126+
int size() except +
127+
iterator begin() except +
128+
iterator end() except +
129+
120130
int GetNumber() except +
121131
c_Game GetGame() except +
122132
c_GamePlayer GetPlayer() except +
@@ -128,8 +138,8 @@ cdef extern from "games/game.h":
128138
Actions GetActions() except +
129139
c_Number GetActionProb(c_GameAction) except +IndexError
130140

131-
int NumMembers() except +
132141
c_GameNode GetMember(int) except +IndexError
142+
Members GetMembers() except +
133143

134144
bint IsChanceInfoset() except +
135145
bint Precedes(c_GameNode) except +

src/pygambit/infoset.pxi

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ class InfosetMembers:
3939
return f"InfosetMembers(infoset={Infoset.wrap(self.infoset)})"
4040

4141
def __len__(self) -> int:
42-
return self.infoset.deref().NumMembers()
42+
return self.infoset.deref().GetMembers().size()
4343

4444
def __iter__(self) -> typing.Iterator[Node]:
45-
for i in range(self.infoset.deref().NumMembers()):
46-
yield Node.wrap(self.infoset.deref().GetMember(i + 1))
45+
for member in self.infoset.deref().GetMembers():
46+
yield Node.wrap(member)
4747

4848
def __getitem__(self, index: typing.Union[int, str]) -> Node:
4949
if isinstance(index, str):

src/solvers/logit/logbehav.imp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ template <class T> void LogBehavProfile<T>::ComputeSolutionData() const
234234
}
235235
if (infosetProb == T(0)) {
236236
for (auto member : infoset->GetMembers()) {
237-
m_beliefs[member] = 1.0 / T(infoset->NumMembers());
237+
m_beliefs[member] = 1.0 / T(infoset->GetMembers().size());
238238
}
239239
continue;
240240
}

0 commit comments

Comments
 (0)