@@ -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+ 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 range 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,100 @@ 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+ // : m_action_it(p_action_it), m_child_it(p_child_it)
536+ // {
537+ // }
538+ // @}
539+
540+ // / @name Iterator Operations
541+ // @{
542+ // / Returns the current action-child pair.
543+ reference operator *() const { return {*m_action_it, *m_child_it}; }
544+
545+ // / Advances the iterator to the next pair (pre-increment).
546+ iterator &operator ++()
547+ {
548+ ++m_action_it;
549+ ++m_child_it;
550+ return *this ;
551+ }
552+
553+ // / Advances the iterator to the next pair (post-increment).
554+ iterator operator ++(int )
555+ {
556+ iterator tmp = *this ;
557+ ++(*this );
558+ return tmp;
559+ }
560+
561+ // / Compares two iterators for equality.
562+ bool operator ==(const iterator &p_other) const
563+ {
564+ // Comparing one of the wrapped iterators is sufficient as they move in lockstep.
565+ return m_child_it == p_other.m_child_it ;
566+ }
567+
568+ // / Compares two iterators for inequality.
569+ bool operator !=(const iterator &p_other) const { return !(*this == p_other); }
570+ // @}
571+
572+ GameNode GetOwner () const ;
573+ };
574+
575+ inline GameNodeRep::Actions::Actions (GameNode p_owner) : m_owner(p_owner) {}
576+
577+ inline GameNodeRep::Actions GameNodeRep::GetActions () const { return Actions (this ); }
578+
579+ inline GameNodeRep::Actions::iterator GameNodeRep::Actions::begin () const
580+ {
581+ if (m_owner->IsTerminal ()) {
582+ return end ();
583+ }
584+ return {m_owner->GetInfoset ()->GetActions ().begin (), m_owner->GetChildren ().begin ()};
585+ }
586+
587+ inline GameNodeRep::Actions::iterator GameNodeRep::Actions::end () const
588+ {
589+ if (m_owner->IsTerminal ()) {
590+ return {};
591+ }
592+ return {m_owner->GetInfoset ()->GetActions ().end (), m_owner->GetChildren ().end ()};
593+ }
594+
595+ inline GameNodeRep::Actions::iterator::iterator (GameInfosetRep::Actions::iterator p_action_it,
596+ GameNodeRep::Children::iterator p_child_it)
597+ : m_action_it(p_action_it), m_child_it(p_child_it)
598+ {
599+ }
600+
601+ inline GameNode GameNodeRep::Actions::iterator::GetOwner () const { return m_child_it.GetOwner (); }
602+
491603// / This is the class for representing an arbitrary finite game.
492604class GameRep : public std ::enable_shared_from_this<GameRep> {
493605 friend class GameOutcomeRep ;
@@ -648,15 +760,8 @@ class GameRep : public std::enable_shared_from_this<GameRep> {
648760 // / Returns the set of terminal nodes which are descendants of members of an action
649761 virtual std::vector<GameNode> GetPlays (GameAction action) const { throw UndefinedException (); }
650762
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;
654763 // / Returns true if the game is perfect recall
655- bool IsPerfectRecall () const
656- {
657- GameInfoset s, t;
658- return IsPerfectRecall (s, t);
659- }
764+ virtual bool IsPerfectRecall () const = 0;
660765 // @}
661766
662767 // / @name Writing data files
0 commit comments