@@ -105,6 +105,7 @@ template <class P, class T> class ElementCollection {
105105 return *this ;
106106 }
107107 value_type operator *() const { return m_container->at (m_index); }
108+ const P &GetOwner () const { return m_owner; }
108109 };
109110
110111 ElementCollection () = default ;
@@ -458,6 +459,23 @@ class GameNodeRep : public GameObject {
458459public:
459460 using Children = ElementCollection<GameNode, GameNodeRep>;
460461
462+ // / @brief A range class for iterating over a node's (action, child) pairs.
463+ class Actions {
464+ private:
465+ GameNode m_owner{nullptr };
466+
467+ public:
468+ class iterator ;
469+
470+ explicit Actions (GameNode p_owner);
471+
472+ iterator begin () const ;
473+ iterator end () const ;
474+ };
475+
476+ // / @brief Returns a collection for iterating over this node's (action, child) pairs.
477+ Actions GetActions () const ;
478+
461479 Game GetGame () const ;
462480
463481 const std::string &GetLabel () const { return m_label; }
@@ -488,6 +506,97 @@ class GameNodeRep : public GameObject {
488506 bool IsSubgameRoot () const ;
489507};
490508
509+ class GameNodeRep ::Actions::iterator {
510+ public:
511+ // / @name Iterator
512+ // @{
513+ using iterator_category = std::forward_iterator_tag;
514+ using value_type = std::pair<GameAction, GameNode>;
515+ using difference_type = std::ptrdiff_t ;
516+ using pointer = value_type *;
517+ using reference = value_type;
518+ // @}
519+
520+ private:
521+ // / @brief An iterator to the action at the parent's information set.
522+ GameInfosetRep::Actions::iterator m_action_it;
523+ // / @brief An iterator to the child node.
524+ GameNodeRep::Children::iterator m_child_it;
525+
526+ public:
527+ // / @name Lifecycle
528+ // @{
529+ // / Default constructor. Creates an iterator in a past-the-end state.
530+ iterator () = default ;
531+
532+ // / Creates a new iterator that zips an action iterator and a child iterator.
533+ iterator (GameInfosetRep::Actions::iterator p_action_it,
534+ GameNodeRep::Children::iterator p_child_it);
535+ // @}
536+
537+ // / @name Iterator Operations
538+ // @{
539+ // / Returns the current action-child pair.
540+ reference operator *() const { return {*m_action_it, *m_child_it}; }
541+
542+ // / Advances the iterator to the next pair (pre-increment).
543+ iterator &operator ++()
544+ {
545+ ++m_action_it;
546+ ++m_child_it;
547+ return *this ;
548+ }
549+
550+ // / Advances the iterator to the next pair (post-increment).
551+ iterator operator ++(int )
552+ {
553+ iterator tmp = *this ;
554+ ++(*this );
555+ return tmp;
556+ }
557+
558+ // / Compares two iterators for equality.
559+ bool operator ==(const iterator &p_other) const
560+ {
561+ // Comparing one of the wrapped iterators is sufficient as they move in lockstep.
562+ return m_child_it == p_other.m_child_it ;
563+ }
564+
565+ // / Compares two iterators for inequality.
566+ bool operator !=(const iterator &p_other) const { return !(*this == p_other); }
567+ // @}
568+
569+ GameNode GetOwner () const ;
570+ };
571+
572+ inline GameNodeRep::Actions::Actions (GameNode p_owner) : m_owner(p_owner) {}
573+
574+ inline GameNodeRep::Actions GameNodeRep::GetActions () const { return Actions (this ); }
575+
576+ inline GameNodeRep::Actions::iterator GameNodeRep::Actions::begin () const
577+ {
578+ if (m_owner->IsTerminal ()) {
579+ return end ();
580+ }
581+ return {m_owner->GetInfoset ()->GetActions ().begin (), m_owner->GetChildren ().begin ()};
582+ }
583+
584+ inline GameNodeRep::Actions::iterator GameNodeRep::Actions::end () const
585+ {
586+ if (m_owner->IsTerminal ()) {
587+ return {};
588+ }
589+ return {m_owner->GetInfoset ()->GetActions ().end (), m_owner->GetChildren ().end ()};
590+ }
591+
592+ inline GameNodeRep::Actions::iterator::iterator (GameInfosetRep::Actions::iterator p_action_it,
593+ GameNodeRep::Children::iterator p_child_it)
594+ : m_action_it(p_action_it), m_child_it(p_child_it)
595+ {
596+ }
597+
598+ inline GameNode GameNodeRep::Actions::iterator::GetOwner () const { return m_child_it.GetOwner (); }
599+
491600// / This is the class for representing an arbitrary finite game.
492601class GameRep : public std ::enable_shared_from_this<GameRep> {
493602 friend class GameOutcomeRep ;
@@ -648,15 +757,8 @@ class GameRep : public std::enable_shared_from_this<GameRep> {
648757 // / Returns the set of terminal nodes which are descendants of members of an action
649758 virtual std::vector<GameNode> GetPlays (GameAction action) const { throw UndefinedException (); }
650759
651- // / Returns true if the game is perfect recall. If not,
652- // / a pair of violating information sets is returned in the parameters.
653- virtual bool IsPerfectRecall (GameInfoset &, GameInfoset &) const = 0;
654760 // / Returns true if the game is perfect recall
655- bool IsPerfectRecall () const
656- {
657- GameInfoset s, t;
658- return IsPerfectRecall (s, t);
659- }
761+ virtual bool IsPerfectRecall () const = 0;
660762 // @}
661763
662764 // / @name Writing data files
0 commit comments