Skip to content

Commit a4b4936

Browse files
d-kadtturocy
authored andcommitted
add m_numNonterminalNodes field to GameRep
1 parent feec4ec commit a4b4936

9 files changed

Lines changed: 261 additions & 14 deletions

File tree

src/games/game.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,8 @@ class GameRep : public BaseGameRep {
634634
virtual GameNode GetRoot() const = 0;
635635
/// Returns the number of nodes in the game
636636
virtual size_t NumNodes() const = 0;
637+
/// Returns the number of non-terminal nodes in the game
638+
virtual size_t NumNonterminalNodes() const = 0;
637639
//@}
638640

639641
/// @name Modification

src/games/gameagg.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ class GameAGGRep : public GameRep {
101101
GameNode GetRoot() const override { throw UndefinedException(); }
102102
/// Returns the number of nodes in the game
103103
size_t NumNodes() const override { throw UndefinedException(); }
104+
/// Returns the number of non-terminal nodes in the game
105+
size_t NumNonterminalNodes() const override { throw UndefinedException(); }
104106
//@}
105107

106108
/// @name General data access

src/games/gamebagg.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ class GameBAGGRep : public GameRep {
109109
GameNode GetRoot() const override { throw UndefinedException(); }
110110
/// Returns the number of nodes in the game
111111
size_t NumNodes() const override { throw UndefinedException(); }
112+
/// Returns the number of non-terminal nodes in the game
113+
size_t NumNonterminalNodes() const override { throw UndefinedException(); }
112114
//@}
113115

114116
/// @name General data access

src/games/gametable.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ class GameTableRep : public GameExplicitRep {
9090
GameNode GetRoot() const override { throw UndefinedException(); }
9191
/// Returns the number of nodes in the game
9292
size_t NumNodes() const override { throw UndefinedException(); }
93+
/// Returns the number of non-terminal nodes in the game
94+
size_t NumNonterminalNodes() const override { throw UndefinedException(); }
9395
//@}
9496

9597
/// @name Outcomes

src/games/gametree.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ GameAction GameTreeRep::InsertAction(GameInfoset p_infoset, GameAction p_action
242242
}
243243

244244
m_numNodes += p_infoset->m_members.size();
245+
// m_numNonterminalNodes stays unchanged when an action is appended to an information set
245246
ClearComputedValues();
246247
Canonicalize();
247248
return action;
@@ -455,6 +456,9 @@ void GameTreeRep::DeleteTree(GameNode p_node)
455456
throw MismatchException();
456457
}
457458
GameNodeRep *node = p_node;
459+
if (!node->IsTerminal()) {
460+
m_numNonterminalNodes--;
461+
}
458462
IncrementVersion();
459463
while (!node->m_children.empty()) {
460464
DeleteTree(node->m_children.front());
@@ -633,6 +637,7 @@ GameInfoset GameTreeRep::AppendMove(GameNode p_node, GameInfoset p_infoset)
633637
node->m_children.push_back(new GameNodeRep(this, node));
634638
m_numNodes++;
635639
});
640+
m_numNonterminalNodes++;
636641
ClearComputedValues();
637642
Canonicalize();
638643
return node->m_infoset;
@@ -681,6 +686,7 @@ GameInfoset GameTreeRep::InsertMove(GameNode p_node, GameInfoset p_infoset)
681686

682687
// Total nodes added = 1 (newNode) + (NumActions - 1) (new children of newNode) = NumActions
683688
m_numNodes += newNode->m_infoset->m_actions.size();
689+
m_numNonterminalNodes++;
684690
ClearComputedValues();
685691
Canonicalize();
686692
return p_infoset;

src/games/gametree.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class GameTreeRep : public GameExplicitRep {
3737
GameNodeRep *m_root;
3838
GamePlayerRep *m_chance;
3939
std::size_t m_numNodes = 1;
40+
std::size_t m_numNonterminalNodes = 0;
4041

4142
/// @name Private auxiliary functions
4243
//@{
@@ -95,6 +96,8 @@ class GameTreeRep : public GameExplicitRep {
9596
GameNode GetRoot() const override { return m_root; }
9697
/// Returns the number of nodes in the game
9798
size_t NumNodes() const override { return m_numNodes; }
99+
/// Returns the number of non-terminal nodes in the game
100+
size_t NumNonterminalNodes() const override { return m_numNonterminalNodes; }
98101
//@}
99102

100103
void DeleteOutcome(const GameOutcome &) override;

src/pygambit/gambit.pxd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ cdef extern from "games/game.h":
187187
void DeleteOutcome(c_GameOutcome) except +
188188

189189
int NumNodes() except +
190+
int NumNonterminalNodes() except +
190191
c_GameNode GetRoot() except +
191192

192193
c_GameStrategy GetStrategy(int) except +IndexError

src/pygambit/game.pxi

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,41 @@ class GameNodes:
194194
yield from dfs(Node.wrap(self.game.deref().GetRoot()))
195195

196196

197+
@cython.cclass
198+
class GameNonterminalNodes:
199+
"""Represents the set of nodes in a game."""
200+
game = cython.declare(c_Game)
201+
202+
def __init__(self, *args, **kwargs) -> None:
203+
raise ValueError("Cannot create GameNonterminalNodes outside a Game.")
204+
205+
@staticmethod
206+
@cython.cfunc
207+
def wrap(game: c_Game) -> GameNonterminalNodes:
208+
obj: GameNonterminalNodes = GameNonterminalNodes.__new__(GameNonterminalNodes)
209+
obj.game = game
210+
return obj
211+
212+
def __repr__(self) -> str:
213+
return f"GameNonterminalNodes(game={Game.wrap(self.game)})"
214+
215+
def __len__(self) -> int:
216+
"""The number of non-terminal nodes in the game."""
217+
if not self.game.deref().IsTree():
218+
return 0
219+
return self.game.deref().NumNonterminalNodes()
220+
221+
def __iter__(self) -> typing.Iterator[Node]:
222+
def dfs(node):
223+
if not node.is_terminal:
224+
yield node
225+
for child in node.children:
226+
yield from dfs(child)
227+
if not self.game.deref().IsTree():
228+
return
229+
yield from dfs(Node.wrap(self.game.deref().GetRoot()))
230+
231+
197232
@cython.cclass
198233
class GameOutcomes:
199234
"""Represents the set of outcomes in a game."""
@@ -713,6 +748,15 @@ class Game:
713748
"""
714749
return GameNodes.wrap(self.game)
715750

751+
@property
752+
def _nonterminal_nodes(self) -> GameNonterminalNodes:
753+
"""The set of non-terminal nodes in the game.
754+
755+
Iteration over this property yields the non-terminal nodes in the order of depth-first
756+
search.
757+
"""
758+
return GameNonterminalNodes.wrap(self.game)
759+
716760
@property
717761
def contingencies(self) -> pygambit.gameiter.Contingencies:
718762
"""An iterator over the contingencies in the game."""

0 commit comments

Comments
 (0)