From 29fc744658f41d3290390604b0c901e6b07084d2 Mon Sep 17 00:00:00 2001 From: Theodore Turocy Date: Wed, 10 Dec 2025 21:05:52 +0000 Subject: [PATCH 1/2] 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). --- src/games/game.h | 144 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 104 insertions(+), 40 deletions(-) diff --git a/src/games/game.h b/src/games/game.h index f98827b57..bb926f697 100644 --- a/src/games/game.h +++ b/src/games/game.h @@ -597,6 +597,8 @@ inline GameNodeRep::Actions::iterator::iterator(GameInfosetRep::Actions::iterato inline GameNode GameNodeRep::Actions::iterator::GetOwner() const { return m_child_it.GetOwner(); } +enum class TraversalOrder { Preorder, Postorder }; + /// This is the class for representing an arbitrary finite game. class GameRep : public std::enable_shared_from_this { friend class GameOutcomeRep; @@ -627,85 +629,147 @@ class GameRep : public std::enable_shared_from_this { class Nodes { Game m_owner{nullptr}; + TraversalOrder m_order{TraversalOrder::Preorder}; public: class iterator { friend class Nodes; - using ChildIterator = ElementCollection::iterator; - Game m_owner{nullptr}; - GameNode m_current_node{nullptr}; - std::stack> m_stack{}; + using ChildIterator = ElementCollection::iterator; - iterator(const Game &game) : m_owner(game) {} + struct Frame { + GameNode m_node; + ChildIterator m_current, m_end; + bool m_expanded = false; + }; - public: - using iterator_category = std::forward_iterator_tag; - using value_type = GameNode; - using pointer = value_type *; - - iterator() = default; + Game m_owner{nullptr}; + TraversalOrder m_order{TraversalOrder::Preorder}; + std::stack m_stack{}; + GameNode m_current{nullptr}; - iterator(const Game &game, const GameNode &start_node) - : m_owner(game), m_current_node(start_node) + iterator(const Game &p_game, const GameNode &p_start, const TraversalOrder p_order) + : m_owner(p_game), m_order(p_order) { - if (!start_node) { + if (!p_start) { return; } - if (start_node->GetGame() != m_owner) { + if (p_start->GetGame() != p_game) { throw MismatchException(); } + m_stack.push(make_frame(p_start)); + if (m_order == TraversalOrder::Preorder) { + m_current = p_start; + } + else { + descend_postorder(); + m_current = m_stack.empty() ? nullptr : m_stack.top().m_node; + } + if (!m_current) { + m_owner = nullptr; + } } - value_type operator*() const + static Frame make_frame(const GameNode &p_node) { - if (!m_current_node) { - throw std::runtime_error("Cannot dereference an end iterator"); + if (p_node->IsTerminal()) { + return Frame{p_node, {}, {}, true}; } - return m_current_node; + const auto children = p_node->GetChildren(); + return Frame{p_node, children.begin(), children.end(), false}; } - iterator &operator++() + void descend_postorder() { - if (!m_current_node) { - throw std::out_of_range("Cannot increment an end iterator"); + while (!m_stack.empty()) { + auto &f = m_stack.top(); + if (f.m_expanded || f.m_current == f.m_end) { + return; + } + f.m_expanded = true; + const GameNode child = *f.m_current; + ++f.m_current; + m_stack.push(make_frame(child)); } + } - if (!m_current_node->IsTerminal()) { - auto children = m_current_node->GetChildren(); - m_stack.emplace(children.begin(), children.end()); + GameNode advance_preorder() + { + if (auto &top = m_stack.top(); !top.m_node->IsTerminal()) { + const auto children = top.m_node->GetChildren(); + top.m_current = children.begin(); + top.m_end = children.end(); } - while (!m_stack.empty()) { - auto &[current_it, end_it] = m_stack.top(); - - if (current_it != end_it) { - m_current_node = *current_it; - ++current_it; - return *this; + if (auto &f = m_stack.top(); f.m_current != f.m_end) { + GameNode const next = *f.m_current; + ++f.m_current; + m_stack.push(make_frame(next)); + return next; } m_stack.pop(); } + return nullptr; + } - m_current_node = nullptr; + GameNode advance_postorder() + { + m_stack.pop(); + if (m_stack.empty()) { + return nullptr; + } + descend_postorder(); + return m_stack.empty() ? nullptr : m_stack.top().m_node; + } + + public: + using iterator_category = std::input_iterator_tag; + using value_type = GameNode; + + iterator() = default; + iterator(const iterator &) = default; + iterator &operator=(const iterator &) = default; + + value_type operator*() const + { + if (!m_current) { + throw std::runtime_error("Dereferencing end iterator"); + } + return m_current; + } + + iterator &operator++() + { + if (!m_current) { + return *this; + } + const GameNode next = + (m_order == TraversalOrder::Preorder) ? advance_preorder() : advance_postorder(); + m_current = next; + if (!m_current) { + m_owner = nullptr; + } return *this; } - bool operator==(const iterator &other) const + bool operator==(const iterator &p_other) const { - return m_owner == other.m_owner && m_current_node == other.m_current_node; + return m_owner == p_other.m_owner && m_current == p_other.m_current; } - bool operator!=(const iterator &other) const { return !(*this == other); } + bool operator!=(const iterator &p_other) const { return !(*this == p_other); } }; Nodes() = default; - explicit Nodes(const Game &p_owner) : m_owner(p_owner) {} + Nodes(const Game &p_owner, const TraversalOrder p_order = TraversalOrder::Preorder) + : m_owner(p_owner), m_order(p_order) + { + } iterator begin() const { - return (m_owner) ? iterator{m_owner, m_owner->GetRoot()} : iterator{}; + return (m_owner) ? iterator{m_owner, m_owner->GetRoot(), m_order} : iterator{}; } - iterator end() const { return (m_owner) ? iterator{m_owner} : iterator{}; } + static iterator end() { return iterator{}; } }; /// @name Lifecycle @@ -937,7 +1001,7 @@ class GameRep : public std::enable_shared_from_this { /// Returns the root node of the game virtual GameNode GetRoot() const = 0; /// Returns a range that can be used to iterate over the nodes of the game - Nodes GetNodes() const { return Nodes(std::const_pointer_cast(shared_from_this())); } + Nodes GetNodes() const { return {std::const_pointer_cast(shared_from_this())}; } /// Returns the number of nodes in the game virtual size_t NumNodes() const = 0; /// Returns the number of non-terminal nodes in the game From 29a12ecdec42957ebca8c86119a7825b11645926 Mon Sep 17 00:00:00 2001 From: Theodore Turocy Date: Fri, 12 Dec 2025 12:05:38 +0000 Subject: [PATCH 2/2] Correction to postfix traversal --- src/games/game.h | 51 ++++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/src/games/game.h b/src/games/game.h index bb926f697..6b5ee4056 100644 --- a/src/games/game.h +++ b/src/games/game.h @@ -640,7 +640,6 @@ class GameRep : public std::enable_shared_from_this { struct Frame { GameNode m_node; ChildIterator m_current, m_end; - bool m_expanded = false; }; Game m_owner{nullptr}; @@ -673,32 +672,18 @@ class GameRep : public std::enable_shared_from_this { static Frame make_frame(const GameNode &p_node) { if (p_node->IsTerminal()) { - return Frame{p_node, {}, {}, true}; + return Frame{p_node, {}, {}}; } const auto children = p_node->GetChildren(); - return Frame{p_node, children.begin(), children.end(), false}; - } - - void descend_postorder() - { - while (!m_stack.empty()) { - auto &f = m_stack.top(); - if (f.m_expanded || f.m_current == f.m_end) { - return; - } - f.m_expanded = true; - const GameNode child = *f.m_current; - ++f.m_current; - m_stack.push(make_frame(child)); - } + return Frame{p_node, children.begin(), children.end()}; } GameNode advance_preorder() { - if (auto &top = m_stack.top(); !top.m_node->IsTerminal()) { - const auto children = top.m_node->GetChildren(); - top.m_current = children.begin(); - top.m_end = children.end(); + if (auto &[node, current, m_end] = m_stack.top(); !node->IsTerminal()) { + const auto children = node->GetChildren(); + current = children.begin(); + m_end = children.end(); } while (!m_stack.empty()) { if (auto &f = m_stack.top(); f.m_current != f.m_end) { @@ -712,6 +697,23 @@ class GameRep : public std::enable_shared_from_this { return nullptr; } + void descend_postorder() + { + while (!m_stack.empty()) { + auto &f = m_stack.top(); + if (f.m_current == f.m_end) { + return; + } + const auto child = *f.m_current; + ++f.m_current; + m_stack.push(make_frame(child)); + if (!child->IsTerminal()) { + continue; + } + return; + } + } + GameNode advance_postorder() { m_stack.pop(); @@ -743,7 +745,7 @@ class GameRep : public std::enable_shared_from_this { if (!m_current) { return *this; } - const GameNode next = + const auto next = (m_order == TraversalOrder::Preorder) ? advance_preorder() : advance_postorder(); m_current = next; if (!m_current) { @@ -1001,7 +1003,10 @@ class GameRep : public std::enable_shared_from_this { /// Returns the root node of the game virtual GameNode GetRoot() const = 0; /// Returns a range that can be used to iterate over the nodes of the game - Nodes GetNodes() const { return {std::const_pointer_cast(shared_from_this())}; } + Nodes GetNodes(TraversalOrder p_traversal = TraversalOrder::Preorder) const + { + return {std::const_pointer_cast(shared_from_this()), p_traversal}; + } /// Returns the number of nodes in the game virtual size_t NumNodes() const = 0; /// Returns the number of non-terminal nodes in the game