Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 110 additions & 41 deletions src/games/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<GameRep> {
friend class GameOutcomeRep;
Expand Down Expand Up @@ -627,85 +629,149 @@ class GameRep : public std::enable_shared_from_this<GameRep> {

class Nodes {
Game m_owner{nullptr};
TraversalOrder m_order{TraversalOrder::Preorder};

public:
class iterator {
friend class Nodes;
using ChildIterator = ElementCollection<GameNode, GameNodeRep>::iterator;

Game m_owner{nullptr};
GameNode m_current_node{nullptr};
std::stack<std::pair<ChildIterator, ChildIterator>> m_stack{};

iterator(const Game &game) : m_owner(game) {}
using ChildIterator = ElementCollection<GameNode, GameNodeRep>::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<Frame> 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
Expand Down Expand Up @@ -937,7 +1003,10 @@ class GameRep : public std::enable_shared_from_this<GameRep> {
/// 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<GameRep>(shared_from_this())); }
Nodes GetNodes(TraversalOrder p_traversal = TraversalOrder::Preorder) const
{
return {std::const_pointer_cast<GameRep>(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
Expand Down