Skip to content

Commit 29fc744

Browse files
committed
Implement postorder iterator for nodes range.
This extends the Nodes range iterator to have two modes, preorder (the existing one), and postorder (added). These are runtime-selectable via the TraversalOrder parameter. The default is preorder (so existing code still works).
1 parent 46121fc commit 29fc744

1 file changed

Lines changed: 104 additions & 40 deletions

File tree

src/games/game.h

Lines changed: 104 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,8 @@ inline GameNodeRep::Actions::iterator::iterator(GameInfosetRep::Actions::iterato
597597

598598
inline GameNode GameNodeRep::Actions::iterator::GetOwner() const { return m_child_it.GetOwner(); }
599599

600+
enum class TraversalOrder { Preorder, Postorder };
601+
600602
/// This is the class for representing an arbitrary finite game.
601603
class GameRep : public std::enable_shared_from_this<GameRep> {
602604
friend class GameOutcomeRep;
@@ -627,85 +629,147 @@ class GameRep : public std::enable_shared_from_this<GameRep> {
627629

628630
class Nodes {
629631
Game m_owner{nullptr};
632+
TraversalOrder m_order{TraversalOrder::Preorder};
630633

631634
public:
632635
class iterator {
633636
friend class Nodes;
634-
using ChildIterator = ElementCollection<GameNode, GameNodeRep>::iterator;
635637

636-
Game m_owner{nullptr};
637-
GameNode m_current_node{nullptr};
638-
std::stack<std::pair<ChildIterator, ChildIterator>> m_stack{};
638+
using ChildIterator = ElementCollection<GameNode, GameNodeRep>::iterator;
639639

640-
iterator(const Game &game) : m_owner(game) {}
640+
struct Frame {
641+
GameNode m_node;
642+
ChildIterator m_current, m_end;
643+
bool m_expanded = false;
644+
};
641645

642-
public:
643-
using iterator_category = std::forward_iterator_tag;
644-
using value_type = GameNode;
645-
using pointer = value_type *;
646-
647-
iterator() = default;
646+
Game m_owner{nullptr};
647+
TraversalOrder m_order{TraversalOrder::Preorder};
648+
std::stack<Frame> m_stack{};
649+
GameNode m_current{nullptr};
648650

649-
iterator(const Game &game, const GameNode &start_node)
650-
: m_owner(game), m_current_node(start_node)
651+
iterator(const Game &p_game, const GameNode &p_start, const TraversalOrder p_order)
652+
: m_owner(p_game), m_order(p_order)
651653
{
652-
if (!start_node) {
654+
if (!p_start) {
653655
return;
654656
}
655-
if (start_node->GetGame() != m_owner) {
657+
if (p_start->GetGame() != p_game) {
656658
throw MismatchException();
657659
}
660+
m_stack.push(make_frame(p_start));
661+
if (m_order == TraversalOrder::Preorder) {
662+
m_current = p_start;
663+
}
664+
else {
665+
descend_postorder();
666+
m_current = m_stack.empty() ? nullptr : m_stack.top().m_node;
667+
}
668+
if (!m_current) {
669+
m_owner = nullptr;
670+
}
658671
}
659672

660-
value_type operator*() const
673+
static Frame make_frame(const GameNode &p_node)
661674
{
662-
if (!m_current_node) {
663-
throw std::runtime_error("Cannot dereference an end iterator");
675+
if (p_node->IsTerminal()) {
676+
return Frame{p_node, {}, {}, true};
664677
}
665-
return m_current_node;
678+
const auto children = p_node->GetChildren();
679+
return Frame{p_node, children.begin(), children.end(), false};
666680
}
667681

668-
iterator &operator++()
682+
void descend_postorder()
669683
{
670-
if (!m_current_node) {
671-
throw std::out_of_range("Cannot increment an end iterator");
684+
while (!m_stack.empty()) {
685+
auto &f = m_stack.top();
686+
if (f.m_expanded || f.m_current == f.m_end) {
687+
return;
688+
}
689+
f.m_expanded = true;
690+
const GameNode child = *f.m_current;
691+
++f.m_current;
692+
m_stack.push(make_frame(child));
672693
}
694+
}
673695

674-
if (!m_current_node->IsTerminal()) {
675-
auto children = m_current_node->GetChildren();
676-
m_stack.emplace(children.begin(), children.end());
696+
GameNode advance_preorder()
697+
{
698+
if (auto &top = m_stack.top(); !top.m_node->IsTerminal()) {
699+
const auto children = top.m_node->GetChildren();
700+
top.m_current = children.begin();
701+
top.m_end = children.end();
677702
}
678-
679703
while (!m_stack.empty()) {
680-
auto &[current_it, end_it] = m_stack.top();
681-
682-
if (current_it != end_it) {
683-
m_current_node = *current_it;
684-
++current_it;
685-
return *this;
704+
if (auto &f = m_stack.top(); f.m_current != f.m_end) {
705+
GameNode const next = *f.m_current;
706+
++f.m_current;
707+
m_stack.push(make_frame(next));
708+
return next;
686709
}
687710
m_stack.pop();
688711
}
712+
return nullptr;
713+
}
689714

690-
m_current_node = nullptr;
715+
GameNode advance_postorder()
716+
{
717+
m_stack.pop();
718+
if (m_stack.empty()) {
719+
return nullptr;
720+
}
721+
descend_postorder();
722+
return m_stack.empty() ? nullptr : m_stack.top().m_node;
723+
}
724+
725+
public:
726+
using iterator_category = std::input_iterator_tag;
727+
using value_type = GameNode;
728+
729+
iterator() = default;
730+
iterator(const iterator &) = default;
731+
iterator &operator=(const iterator &) = default;
732+
733+
value_type operator*() const
734+
{
735+
if (!m_current) {
736+
throw std::runtime_error("Dereferencing end iterator");
737+
}
738+
return m_current;
739+
}
740+
741+
iterator &operator++()
742+
{
743+
if (!m_current) {
744+
return *this;
745+
}
746+
const GameNode next =
747+
(m_order == TraversalOrder::Preorder) ? advance_preorder() : advance_postorder();
748+
m_current = next;
749+
if (!m_current) {
750+
m_owner = nullptr;
751+
}
691752
return *this;
692753
}
693754

694-
bool operator==(const iterator &other) const
755+
bool operator==(const iterator &p_other) const
695756
{
696-
return m_owner == other.m_owner && m_current_node == other.m_current_node;
757+
return m_owner == p_other.m_owner && m_current == p_other.m_current;
697758
}
698-
bool operator!=(const iterator &other) const { return !(*this == other); }
759+
bool operator!=(const iterator &p_other) const { return !(*this == p_other); }
699760
};
700761

701762
Nodes() = default;
702-
explicit Nodes(const Game &p_owner) : m_owner(p_owner) {}
763+
Nodes(const Game &p_owner, const TraversalOrder p_order = TraversalOrder::Preorder)
764+
: m_owner(p_owner), m_order(p_order)
765+
{
766+
}
703767

704768
iterator begin() const
705769
{
706-
return (m_owner) ? iterator{m_owner, m_owner->GetRoot()} : iterator{};
770+
return (m_owner) ? iterator{m_owner, m_owner->GetRoot(), m_order} : iterator{};
707771
}
708-
iterator end() const { return (m_owner) ? iterator{m_owner} : iterator{}; }
772+
static iterator end() { return iterator{}; }
709773
};
710774

711775
/// @name Lifecycle
@@ -937,7 +1001,7 @@ class GameRep : public std::enable_shared_from_this<GameRep> {
9371001
/// Returns the root node of the game
9381002
virtual GameNode GetRoot() const = 0;
9391003
/// Returns a range that can be used to iterate over the nodes of the game
940-
Nodes GetNodes() const { return Nodes(std::const_pointer_cast<GameRep>(shared_from_this())); }
1004+
Nodes GetNodes() const { return {std::const_pointer_cast<GameRep>(shared_from_this())}; }
9411005
/// Returns the number of nodes in the game
9421006
virtual size_t NumNodes() const = 0;
9431007
/// Returns the number of non-terminal nodes in the game

0 commit comments

Comments
 (0)