Skip to content

Commit 2641fde

Browse files
committed
Implement non-copying version of Infosets collection
This develops a template that allows us to treat "nested" containers (such as information sets within players) as a single flattened container. As an implication, `GameRep::GetInfosets` now does not create an explicit array of information sets but rather iterates and generates `GameInfoset` handle instances only on demand. Closes #520.
1 parent 788ed67 commit 2641fde

File tree

4 files changed

+123
-11
lines changed

4 files changed

+123
-11
lines changed

src/games/game.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -901,10 +901,13 @@ class GameRep : public std::enable_shared_from_this<GameRep> {
901901

902902
/// @name Information sets
903903
//@{
904+
using Infosets =
905+
NestedElementCollection<&GameRep::m_players, &GamePlayerRep::m_infosets, GameInfoset>;
906+
904907
/// Returns the iset'th information set in the game (numbered globally)
905908
virtual GameInfoset GetInfoset(int iset) const { throw UndefinedException(); }
906909
/// Returns the set of information sets in the game
907-
virtual std::vector<GameInfoset> GetInfosets() const { throw UndefinedException(); }
910+
virtual Infosets GetInfosets() const { throw UndefinedException(); }
908911
/// Sort the information sets for each player in a canonical order
909912
virtual void SortInfosets() {}
910913
/// Returns the set of actions taken by the infoset's owner before reaching this infoset

src/games/gameobject.h

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,124 @@ template <class P, class T> class ElementCollection {
214214
iterator cend() const { return {m_owner, m_container, (m_owner) ? m_container->size() : 0}; }
215215
};
216216

217+
template <auto OuterMember, auto InnerMember, typename ValueType> class NestedElementCollection {
218+
template <typename MemberPtr> struct outer_element_of;
219+
220+
template <typename OuterClass, typename Elem>
221+
struct outer_element_of<std::vector<std::shared_ptr<Elem>> OuterClass::*> {
222+
using outer_class = OuterClass;
223+
using outer_elem = Elem;
224+
using container = std::vector<std::shared_ptr<Elem>>;
225+
};
226+
227+
template <typename MemberPtr> struct inner_element_of;
228+
229+
template <typename OuterElem, typename InnerElem>
230+
struct inner_element_of<std::vector<std::shared_ptr<InnerElem>> OuterElem::*> {
231+
using outer_elem = OuterElem;
232+
using inner_elem = InnerElem;
233+
using container = std::vector<std::shared_ptr<InnerElem>>;
234+
};
235+
236+
using OM = outer_element_of<decltype(OuterMember)>;
237+
using IM = inner_element_of<decltype(InnerMember)>;
238+
239+
using OuterClass = typename OM::outer_class;
240+
using OuterElem = typename OM::outer_elem;
241+
using InnerElem = typename IM::inner_elem;
242+
243+
public:
244+
class iterator {
245+
using outer_iter = typename OM::container::const_iterator;
246+
using inner_iter = typename IM::container::const_iterator;
247+
248+
outer_iter m_outer, m_outerEnd;
249+
inner_iter m_inner, m_innerEnd;
250+
mutable ValueType m_cache;
251+
252+
void advance()
253+
{
254+
while (m_outer != m_outerEnd && m_inner == m_innerEnd) {
255+
++m_outer;
256+
if (m_outer != m_outerEnd) {
257+
const auto &outerPtr = *m_outer;
258+
const auto &innerVec = outerPtr.get()->*InnerMember;
259+
m_inner = innerVec.begin();
260+
m_innerEnd = innerVec.end();
261+
}
262+
}
263+
}
264+
265+
public:
266+
using iterator_category = std::input_iterator_tag;
267+
using value_type = ValueType;
268+
using difference_type = std::ptrdiff_t;
269+
using reference = ValueType;
270+
using pointer = const ValueType *;
271+
272+
iterator() = default;
273+
274+
iterator(const OuterClass *p_obj, bool p_isEnd)
275+
{
276+
const auto &outerVec = p_obj->*OuterMember;
277+
278+
m_outer = outerVec.begin();
279+
m_outerEnd = outerVec.end();
280+
281+
if (p_isEnd || m_outer == m_outerEnd) {
282+
m_outer = m_outerEnd;
283+
return;
284+
}
285+
286+
const auto &outerPtr = *m_outer;
287+
const auto &innerVec = outerPtr.get()->*InnerMember;
288+
289+
m_inner = innerVec.begin();
290+
m_innerEnd = innerVec.end();
291+
292+
advance();
293+
}
294+
295+
reference operator*() const { return ValueType(*m_inner); }
296+
297+
pointer operator->() const
298+
{
299+
m_cache = ValueType(*m_inner);
300+
return &m_cache;
301+
}
302+
303+
iterator &operator++()
304+
{
305+
++m_inner;
306+
advance();
307+
return *this;
308+
}
309+
310+
iterator operator++(int)
311+
{
312+
iterator tmp = *this;
313+
++(*this);
314+
return tmp;
315+
}
316+
317+
bool operator==(const iterator &p_other) const
318+
{
319+
return m_outer == p_other.m_outer && (m_outer == m_outerEnd || m_inner == p_other.m_inner);
320+
}
321+
322+
bool operator!=(const iterator &p_other) const { return !(*this == p_other); }
323+
};
324+
325+
private:
326+
const OuterClass *m_obj;
327+
328+
public:
329+
explicit NestedElementCollection(const OuterClass *p_obj) : m_obj(p_obj) {}
330+
331+
iterator begin() const { return {m_obj, false}; }
332+
iterator end() const { return {m_obj, true}; }
333+
};
334+
217335
} // end namespace Gambit
218336

219337
#endif // GAMBIT_GAMES_GAMEOBJECT_H

src/games/gametree.cc

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,15 +1193,6 @@ GameInfoset GameTreeRep::GetInfoset(int p_index) const
11931193
throw std::out_of_range("Infoset index out of range");
11941194
}
11951195

1196-
std::vector<GameInfoset> GameTreeRep::GetInfosets() const
1197-
{
1198-
std::vector<GameInfoset> infosets;
1199-
for (const auto &player : m_players) {
1200-
std::copy(player->m_infosets.begin(), player->m_infosets.end(), std::back_inserter(infosets));
1201-
}
1202-
return infosets;
1203-
}
1204-
12051196
//------------------------------------------------------------------------
12061197
// GameTreeRep: Outcomes
12071198
//------------------------------------------------------------------------

src/games/gametree.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ class GameTreeRep : public GameExplicitRep {
127127
/// Returns the iset'th information set in the game (numbered globally)
128128
GameInfoset GetInfoset(int iset) const override;
129129
/// Returns the set of information sets in the game
130-
std::vector<GameInfoset> GetInfosets() const override;
130+
Infosets GetInfosets() const override { return Infosets(this); }
131131
/// Sort the information sets for each player in a canonical order
132132
void SortInfosets() override;
133133
/// Returns the set of actions taken by the infoset's owner before reaching this infoset

0 commit comments

Comments
 (0)