diff --git a/src/games/game.h b/src/games/game.h index f98827b57..6b5ee4056 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,149 @@ 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{}; - iterator(const Game &game) : m_owner(game) {} + using ChildIterator = ElementCollection::iterator; - public: - using iterator_category = std::forward_iterator_tag; - using value_type = GameNode; - using pointer = value_type *; + struct Frame { + GameNode m_node; + ChildIterator m_current, m_end; + }; - 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, {}, {}}; } - return m_current_node; + const auto children = p_node->GetChildren(); + return Frame{p_node, children.begin(), children.end()}; } - iterator &operator++() + GameNode advance_preorder() { - if (!m_current_node) { - throw std::out_of_range("Cannot increment an end iterator"); + if (auto &[node, current, m_end] = m_stack.top(); !node->IsTerminal()) { + const auto children = node->GetChildren(); + current = children.begin(); + m_end = children.end(); } - - if (!m_current_node->IsTerminal()) { - auto children = m_current_node->GetChildren(); - m_stack.emplace(children.begin(), children.end()); + while (!m_stack.empty()) { + 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; + } + void descend_postorder() + { 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; + auto &f = m_stack.top(); + if (f.m_current == f.m_end) { + return; } - m_stack.pop(); + 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(); + 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; - m_current_node = nullptr; + 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 auto 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 +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 Nodes(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