From d2f88747a44211ee1f3eb145f257fa279c68173d Mon Sep 17 00:00:00 2001 From: drdkad Date: Sat, 29 Mar 2025 09:11:55 +0000 Subject: [PATCH 1/7] add GameTreeRep::m_numNodes field and expose it in NumNodes; reiplemented Game.nodes as a property of class Game --- src/games/gametree.cc | 33 +++++++++----------- src/games/gametree.h | 3 +- src/pygambit/game.pxi | 72 ++++++++++++++++++++++++------------------- 3 files changed, 56 insertions(+), 52 deletions(-) diff --git a/src/games/gametree.cc b/src/games/gametree.cc index dae51d55c..9b07645db 100644 --- a/src/games/gametree.cc +++ b/src/games/gametree.cc @@ -230,6 +230,8 @@ GameAction GameTreeInfosetRep::InsertAction(GameAction p_action /* =0 */) new GameTreeNodeRep(m_efg, member)); } + m_efg->m_numNodes += m_members.size(); + m_efg->ClearComputedValues(); m_efg->Canonicalize(); return action; @@ -444,6 +446,9 @@ void GameTreeNodeRep::DeleteTree() m_children.front()->Invalidate(); erase_atindex(m_children, 1); } + + m_efg->m_numNodes--; + if (m_infoset) { m_infoset->RemoveMember(this); m_infoset = nullptr; @@ -602,7 +607,10 @@ GameInfoset GameTreeNodeRep::AppendMove(GameInfoset p_infoset) m_infoset->AddMember(this); std::for_each( m_infoset->m_actions.begin(), m_infoset->m_actions.end(), - [this](const GameActionRep *) { m_children.push_back(new GameTreeNodeRep(m_efg, this)); }); + [this](const GameActionRep *) { + m_children.push_back(new GameTreeNodeRep(m_efg, this)); + m_efg->m_numNodes++; + }); m_efg->ClearComputedValues(); m_efg->Canonicalize(); return m_infoset; @@ -647,6 +655,9 @@ GameInfoset GameTreeNodeRep::InsertMove(GameInfoset p_infoset) newNode->m_children.push_back(new GameTreeNodeRep(m_efg, newNode)); }); + // Total nodes added = 1 (newNode) + (NumActions - 1) (new children of newNode) = NumActions + m_efg->m_numNodes += newNode->m_infoset->m_actions.size(); + m_efg->ClearComputedValues(); m_efg->Canonicalize(); return p_infoset; @@ -661,7 +672,8 @@ GameInfoset GameTreeNodeRep::InsertMove(GameInfoset p_infoset) //------------------------------------------------------------------------ GameTreeRep::GameTreeRep() - : m_root(new GameTreeNodeRep(this, nullptr)), m_chance(new GamePlayerRep(this, 0)) + : m_numNodes(1), m_computedValues(false), m_doCanon(true), + m_root(new GameTreeNodeRep(this, nullptr)), m_chance(new GamePlayerRep(this, 0)) { } @@ -1015,23 +1027,6 @@ void GameTreeRep::DeleteOutcome(const GameOutcome &p_outcome) ClearComputedValues(); } -//------------------------------------------------------------------------ -// GameTreeRep: Nodes -//------------------------------------------------------------------------ - -namespace { -size_t CountNodes(GameNode p_node) -{ - size_t num = 1; - for (size_t i = 1; i <= p_node->NumChildren(); num += CountNodes(p_node->GetChild(i++))) - ; - return num; -} - -} // end anonymous namespace - -size_t GameTreeRep::NumNodes() const { return CountNodes(m_root); } - //------------------------------------------------------------------------ // GameTreeRep: Modification //------------------------------------------------------------------------ diff --git a/src/games/gametree.h b/src/games/gametree.h index 9441d5c7d..70862c7de 100644 --- a/src/games/gametree.h +++ b/src/games/gametree.h @@ -213,6 +213,7 @@ class GameTreeRep : public GameExplicitRep { mutable bool m_computedValues{false}, m_doCanon{true}; GameTreeNodeRep *m_root; GamePlayerRep *m_chance; + std::size_t m_numNodes; /// @name Private auxiliary functions //@{ @@ -265,7 +266,7 @@ class GameTreeRep : public GameExplicitRep { /// Returns the root node of the game GameNode GetRoot() const override { return m_root; } /// Returns the number of nodes in the game - size_t NumNodes() const override; + size_t NumNodes() const override { return m_numNodes; } //@} void DeleteOutcome(const GameOutcome &) override; diff --git a/src/pygambit/game.pxi b/src/pygambit/game.pxi index 9fe2e1943..916466dcb 100644 --- a/src/pygambit/game.pxi +++ b/src/pygambit/game.pxi @@ -160,6 +160,40 @@ def read_agg(filepath_or_buffer: typing.Union[str, pathlib.Path, io.IOBase]) -> return read_game(filepath_or_buffer, parser=ParseAggGame) +@cython.cclass +class GameNodes: + """Represents the set of nodes in a game.""" + game = cython.declare(c_Game) + + def __init__(self, *args, **kwargs) -> None: + raise ValueError("Cannot create GameNodes outside a Game.") + + @staticmethod + @cython.cfunc + def wrap(game: c_Game) -> GameNodes: + obj: GameNodes = GameNodes.__new__(GameNodes) + obj.game = game + return obj + + def __repr__(self) -> str: + return f"GameNodes(game={Game.wrap(self.game)})" + + def __len__(self) -> int: + """The number of nodes in the game.""" + if not self.game.deref().IsTree(): + return 0 + return self.game.deref().NumNodes() + + def __iter__(self) -> typing.Iterator[Node]: + def dfs(node): + for child in node.children: + yield from dfs(child) + yield node + if not self.game.deref().IsTree(): + return + yield from dfs(Node.wrap(self.game.deref().GetRoot())) + + @cython.cclass class GameOutcomes: """Represents the set of outcomes in a game.""" @@ -667,6 +701,11 @@ class Game: """The set of outcomes in the game.""" return GameOutcomes.wrap(self.game) + @property + def nodes(self) -> GameNodes: + """The set of nodes in the game.""" + return GameNodes.wrap(self.game) + @property def contingencies(self) -> pygambit.gameiter.Contingencies: """An iterator over the contingencies in the game.""" @@ -1062,37 +1101,6 @@ class Game: raise ValueError("attempted to remove the last strategy for player") return profile - def nodes( - self, - subtree: typing.Optional[typing.Union[Node, str]] = None - ) -> typing.List[Node]: - """Return a list of nodes in the game tree. If `subtree` is not None, returns - the nodes in the subtree rooted at that node. - - Nodes are returned in prefix-traversal order: a node appears prior to the list of - nodes in the subtrees rooted at the node's children. - - Parameters - ---------- - subtree : Node or str, optional - If specified, return only the nodes in the subtree rooted at `subtree`. - - Raises - ------ - MismatchError - If `node` is a `Node` from a different game. - """ - if not self.is_tree: - return [] - if subtree: - resolved_node = cython.cast(Node, self._resolve_node(subtree, "nodes", "subtree")) - else: - resolved_node = self.root - return ( - [resolved_node] + - [n for child in resolved_node.children for n in self.nodes(child)] - ) - @cython.cfunc def _to_format( self, @@ -1369,7 +1377,7 @@ class Game: raise ValueError( f"{funcname}(): {argname} cannot be an empty string or all spaces" ) - for n in self.nodes(): + for n in self.nodes: if n.label == node: return n raise KeyError(f"{funcname}(): no node with label '{node}'") From bedaff3d70a84c9c2224188f100ade680eab12f1 Mon Sep 17 00:00:00 2001 From: drdkad Date: Fri, 11 Apr 2025 10:41:49 +0100 Subject: [PATCH 2/7] add tests for new functionality of GameNodes.__len__ --- tests/test_extensive.py | 2 +- tests/test_game_resolve.py | 2 +- tests/test_node.py | 191 ++++++++++++++++++++++++++++++++++++- tests/test_strategic.py | 2 +- 4 files changed, 190 insertions(+), 7 deletions(-) diff --git a/tests/test_extensive.py b/tests/test_extensive.py index 9fe517f8f..3e3976521 100644 --- a/tests/test_extensive.py +++ b/tests/test_extensive.py @@ -57,7 +57,7 @@ def test_game_add_players_nolabel(): def test_game_num_nodes(): game = games.read_from_file("basic_extensive_game.efg") - assert len(game.nodes()) == 15 + assert len(game.nodes) == 15 def test_game_is_perfect_recall(): diff --git a/tests/test_game_resolve.py b/tests/test_game_resolve.py index e18c76794..8b7822151 100644 --- a/tests/test_game_resolve.py +++ b/tests/test_game_resolve.py @@ -106,7 +106,7 @@ def test_resolve_strategy_invalid( ] ) def test_resolve_node(game: gbt.Game) -> None: - _test_valid_resolutions(game.nodes(), + _test_valid_resolutions(game.nodes, lambda label, fn: game._resolve_node(label, fn)) diff --git a/tests/test_node.py b/tests/test_node.py index 6bd1029c7..bca4e6732 100644 --- a/tests/test_node.py +++ b/tests/test_node.py @@ -200,8 +200,9 @@ def _subtrees_equal( def test_copy_tree_onto_nondescendent_terminal_node(): """Test copying a subtree to a non-descendent node.""" g = games.read_from_file("e01.efg") - src_node = g.nodes()[3] # path=[1, 0] - dest_node = g.nodes()[2] # path=[0, 0] + list_nodes = list(g.nodes) + src_node = list_nodes[3] # path=[1, 0] + dest_node = list_nodes[0] # path=[0, 0] g.copy_tree(src_node, dest_node) @@ -211,8 +212,9 @@ def test_copy_tree_onto_nondescendent_terminal_node(): def test_copy_tree_onto_descendent_terminal_node(): """Test copying a subtree to a node that's a descendent of the original.""" g = games.read_from_file("e01.efg") - src_node = g.nodes()[1] # path=[0] - dest_node = g.nodes()[4] # path=[0, 1, 0] + list_nodes = list(g.nodes) + src_node = list_nodes[4] # path=[0] + dest_node = list_nodes[1] # path=[0, 1, 0] g.copy_tree(src_node, dest_node) @@ -404,3 +406,184 @@ def test_append_infoset_node_list_is_empty(): game.append_move(game.root.children[0].children[0], "Player 3", ["B", "F"]) with pytest.raises(ValueError): game.append_infoset([], game.root.children[0].children[0].infoset) + + +def _get_members(action: gbt.Action) -> set[gbt.Node]: + """Calculates the set of nodes resulting from taking a specific action + at all nodes within its information set. + """ + infoset = action.infoset + action_index = action.number + + return [member_node.children[action_index] for member_node in infoset.members] + + +def _count_subtree_nodes(start_node: gbt.Node) -> int: + """Counts nodes in the subtree rooted at start_node (including start_node).""" + count = 1 + for child in start_node.children: + count += _count_subtree_nodes(child) + return count + + +def test_len_matches_sum_children_plus_one(): + """Verify `len(game.nodes)` matches (sum of children counts + 1) + """ + game = games.read_from_file("e01.efg") + expected_node_count = 9 + + direct_len = len(game.nodes) + assert direct_len == expected_node_count + + assert direct_len == _count_subtree_nodes(game.root) + + +def test_len_after_delete_tree(): + """Verify `len(game.nodes)` is correct after `delete_tree`. + """ + game = games.read_from_file("e01.efg") + initial_number_of_nodes = len(game.nodes) + list_nodes = list(game.nodes) + + root_of_the_deleted_subtree = list_nodes[3] + number_of_deleted_nodes = _count_subtree_nodes(root_of_the_deleted_subtree) + + game.delete_tree(root_of_the_deleted_subtree) + + assert len(game.nodes) == initial_number_of_nodes - number_of_deleted_nodes + + +def test_len_after_delete_parent(): + """Verify `len(game.nodes)` is correct after `delete_parent`. + """ + game = games.read_from_file("e02.efg") + initial_number_of_nodes = len(game.nodes) + list_nodes = list(game.nodes) + + node_parent_to_delete = list_nodes[4] + + number_of_node_ancestors = _count_subtree_nodes(node_parent_to_delete) + number_of_parent_ancestors = _count_subtree_nodes(node_parent_to_delete.parent) + diff = number_of_parent_ancestors - number_of_node_ancestors + + game.delete_parent(node_parent_to_delete) + + assert len(game.nodes) == initial_number_of_nodes - diff + + +def test_len_after_append_move(): + """Verify `len(game.nodes)` is correct after `append_move`. + """ + game = games.read_from_file("e01.efg") + initial_number_of_nodes = len(game.nodes) + list_nodes = list(game.nodes) + + terminal_node = list_nodes[1] + player = game.players[0] + actions_to_add = ["T", "M", "B"] + + game.append_move(terminal_node, player, actions_to_add) + + assert len(game.nodes) == initial_number_of_nodes + len(actions_to_add) + + +def test_len_after_append_infoset(): + """Verify `len(game.nodes)` is correct after `append_infoset`. + """ + game = games.read_from_file("e02.efg") + initial_number_of_nodes = len(game.nodes) + list_nodes = list(game.nodes) + + member_node = list_nodes[5] # path=[1] + infoset_to_modify = member_node.infoset + number_of_infoset_actions = len(infoset_to_modify.actions) + terminal_node_to_add = list_nodes[3] # path=[1, 1, 1] + + game.append_infoset(terminal_node_to_add, infoset_to_modify) + + assert len(game.nodes) == initial_number_of_nodes + number_of_infoset_actions + + +def test_len_after_add_action(): + """Verify `len(game.nodes)` is correct after `add_action`. + """ + game = games.read_from_file("e01.efg") + initial_number_of_nodes = len(game.nodes) + + infoset_to_modify = game.infosets[1] + + num_nodes_in_infoset = len(infoset_to_modify.members) + + game.add_action(infoset_to_modify) + + assert len(game.nodes) == initial_number_of_nodes + num_nodes_in_infoset + + +def test_len_after_delete_action(): + """Verify `len(game.nodes)` is correct after `delete_action`. + """ + game = games.read_from_file("e02.efg") + initial_number_of_nodes = len(game.nodes) + + action_to_delete = game.infosets[0].actions[1] + + # Calculate the total number of nodes within all subtrees + # that begin immediately after taking the specified action. + nodes_to_delete = 0 + action_nodes = _get_members(action_to_delete) + + for subtree_root in action_nodes: + nodes_to_delete += _count_subtree_nodes(subtree_root) + + game.delete_action(action_to_delete) + + assert len(game.nodes) == initial_number_of_nodes - nodes_to_delete + + +def test_len_after_insert_move(): + """Verify `len(game.nodes)` is correct after `insert_move`. + """ + game = games.read_from_file("e01.efg") + initial_number_of_nodes = len(game.nodes) + list_nodes = list(game.nodes) + + node_to_insert_above = list_nodes[3] + + player = game.players[1] + num_actions_to_add = 3 + + game.insert_move(node_to_insert_above, player, num_actions_to_add) + + assert len(game.nodes) == initial_number_of_nodes + num_actions_to_add + + +def test_len_after_insert_infoset(): + """Verify `len(game.nodes)` is correct after `insert_infoset`. + """ + game = games.read_from_file("e01.efg") + initial_number_of_nodes = len(game.nodes) + list_nodes = list(game.nodes) + + member_node = list_nodes[4] # path=[1, 1] + infoset_to_modify = member_node.infoset + node_to_insert_above = list_nodes[1] # path=[0, 1] + number_of_infoset_actions = len(infoset_to_modify.actions) + + game.insert_infoset(node_to_insert_above, infoset_to_modify) + + assert len(game.nodes) == initial_number_of_nodes + number_of_infoset_actions + + +def test_len_after_copy_tree(): + """Verify `len(game.nodes)` is correct after `copy_tree`. + """ + game = games.read_from_file("e01.efg") + initial_number_of_nodes = len(game.nodes) + list_nodes = list(game.nodes) + src_node = list_nodes[3] # path=[1, 0] + dest_node = list_nodes[0] # path=[0, 0] + number_of_src_ancestors = _count_subtree_nodes(src_node) + + game.copy_tree(src_node, dest_node) + + assert len(game.nodes) == initial_number_of_nodes + number_of_src_ancestors - 1 diff --git a/tests/test_strategic.py b/tests/test_strategic.py index c8ab47d09..5b453ce78 100644 --- a/tests/test_strategic.py +++ b/tests/test_strategic.py @@ -25,7 +25,7 @@ def test_strategic_game_root(): def test_strategic_game_nodes(): game = gbt.Game.new_table([2, 2]) - assert game.nodes() == [] + assert list(game.nodes) == [] def test_game_behav_profile_error(): From 726493a22fdd30ee7a9a27071b3e4e2144831697 Mon Sep 17 00:00:00 2001 From: drdkad Date: Fri, 11 Apr 2025 10:43:11 +0100 Subject: [PATCH 3/7] change order of nodes while traversing GameNodes --- tests/test_behav.py | 88 ++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/tests/test_behav.py b/tests/test_behav.py index 6636cf04c..fd6c669e8 100644 --- a/tests/test_behav.py +++ b/tests/test_behav.py @@ -584,64 +584,64 @@ def test_set_probabilities_player_by_label(game: gbt.Game, player_label: str, be @pytest.mark.parametrize( "game,node_idx,realiz_prob,rational_flag", - [(games.create_mixed_behav_game_efg(), 0, "1", True), - (games.create_mixed_behav_game_efg(), 1, "1/2", True), + [(games.create_mixed_behav_game_efg(), 0, "1/8", True), + (games.create_mixed_behav_game_efg(), 1, "1/8", True), (games.create_mixed_behav_game_efg(), 2, "1/4", True), (games.create_mixed_behav_game_efg(), 3, "1/8", True), (games.create_mixed_behav_game_efg(), 4, "1/8", True), (games.create_mixed_behav_game_efg(), 5, "1/4", True), - (games.create_mixed_behav_game_efg(), 6, "1/8", True), + (games.create_mixed_behav_game_efg(), 6, "1/2", True), (games.create_mixed_behav_game_efg(), 7, "1/8", True), - (games.create_mixed_behav_game_efg(), 8, "1/2", True), + (games.create_mixed_behav_game_efg(), 8, "1/8", True), (games.create_mixed_behav_game_efg(), 9, "1/4", True), (games.create_mixed_behav_game_efg(), 10, "1/8", True), (games.create_mixed_behav_game_efg(), 11, "1/8", True), (games.create_mixed_behav_game_efg(), 12, "1/4", True), - (games.create_mixed_behav_game_efg(), 13, "1/8", True), - (games.create_mixed_behav_game_efg(), 14, "1/8", True), - (games.create_mixed_behav_game_efg(), 0, 1.0, False), - (games.create_mixed_behav_game_efg(), 1, 0.5, False), + (games.create_mixed_behav_game_efg(), 13, "1/2", True), + (games.create_mixed_behav_game_efg(), 14, "1", True), + (games.create_mixed_behav_game_efg(), 0, 0.125, False), + (games.create_mixed_behav_game_efg(), 1, 0.125, False), (games.create_mixed_behav_game_efg(), 2, 0.25, False), (games.create_mixed_behav_game_efg(), 3, 0.125, False), (games.create_mixed_behav_game_efg(), 4, 0.125, False), (games.create_mixed_behav_game_efg(), 5, 0.25, False), - (games.create_mixed_behav_game_efg(), 6, 0.125, False), + (games.create_mixed_behav_game_efg(), 6, 0.5, False), (games.create_mixed_behav_game_efg(), 7, 0.125, False), - (games.create_mixed_behav_game_efg(), 8, 0.5, False), + (games.create_mixed_behav_game_efg(), 8, 0.125, False), (games.create_mixed_behav_game_efg(), 9, 0.25, False), (games.create_mixed_behav_game_efg(), 10, 0.125, False), (games.create_mixed_behav_game_efg(), 11, 0.125, False), (games.create_mixed_behav_game_efg(), 12, 0.25, False), - (games.create_mixed_behav_game_efg(), 13, 0.125, False), - (games.create_mixed_behav_game_efg(), 14, 0.125, False), - (games.create_myerson_2_card_poker_efg(), 0, "1", True), - (games.create_myerson_2_card_poker_efg(), 1, "1/2", True), + (games.create_mixed_behav_game_efg(), 13, 0.5, False), + (games.create_mixed_behav_game_efg(), 14, 1, False), + (games.create_myerson_2_card_poker_efg(), 0, "1/8", True), + (games.create_myerson_2_card_poker_efg(), 1, "1/8", True), (games.create_myerson_2_card_poker_efg(), 2, "1/4", True), - (games.create_myerson_2_card_poker_efg(), 3, "1/8", True), - (games.create_myerson_2_card_poker_efg(), 4, "1/8", True), - (games.create_myerson_2_card_poker_efg(), 5, "1/4", True), - (games.create_myerson_2_card_poker_efg(), 6, "1/2", True), + (games.create_myerson_2_card_poker_efg(), 3, "1/4", True), + (games.create_myerson_2_card_poker_efg(), 4, "1/2", True), + (games.create_myerson_2_card_poker_efg(), 5, "1/8", True), + (games.create_myerson_2_card_poker_efg(), 6, "1/8", True), (games.create_myerson_2_card_poker_efg(), 7, "1/4", True), - (games.create_myerson_2_card_poker_efg(), 8, "1/8", True), - (games.create_myerson_2_card_poker_efg(), 9, "1/8", True), - (games.create_myerson_2_card_poker_efg(), 10, "1/4", True), - (games.create_myerson_2_card_poker_efg(), 0, 1.0, False), - (games.create_myerson_2_card_poker_efg(), 1, 0.5, False), + (games.create_myerson_2_card_poker_efg(), 8, "1/4", True), + (games.create_myerson_2_card_poker_efg(), 9, "1/2", True), + (games.create_myerson_2_card_poker_efg(), 10, "1", True), + (games.create_myerson_2_card_poker_efg(), 0, 0.125, False), + (games.create_myerson_2_card_poker_efg(), 1, 0.125, False), (games.create_myerson_2_card_poker_efg(), 2, 0.25, False), - (games.create_myerson_2_card_poker_efg(), 3, 0.125, False), - (games.create_myerson_2_card_poker_efg(), 4, 0.125, False), - (games.create_myerson_2_card_poker_efg(), 5, 0.25, False), - (games.create_myerson_2_card_poker_efg(), 6, 0.5, False), + (games.create_myerson_2_card_poker_efg(), 3, 0.25, False), + (games.create_myerson_2_card_poker_efg(), 4, 0.5, False), + (games.create_myerson_2_card_poker_efg(), 5, 0.125, False), + (games.create_myerson_2_card_poker_efg(), 6, 0.125, False), (games.create_myerson_2_card_poker_efg(), 7, 0.25, False), - (games.create_myerson_2_card_poker_efg(), 8, 0.125, False), - (games.create_myerson_2_card_poker_efg(), 9, 0.125, False), - (games.create_myerson_2_card_poker_efg(), 10, 0.25, False)] + (games.create_myerson_2_card_poker_efg(), 8, 0.25, False), + (games.create_myerson_2_card_poker_efg(), 9, 0.5, False), + (games.create_myerson_2_card_poker_efg(), 10, 1, False)] ) def test_realiz_prob_nodes_reference(game: gbt.Game, node_idx: int, realiz_prob: typing.Union[str, float], rational_flag: bool): profile = game.mixed_behavior_profile(rational=rational_flag) realiz_prob = (gbt.Rational(realiz_prob) if rational_flag else realiz_prob) - node = game.nodes()[node_idx] + node = list(game.nodes)[node_idx] assert profile.realiz_prob(node) == realiz_prob @@ -890,7 +890,7 @@ def test_martingale_property_of_node_value(game: gbt.Game, rational_flag: bool): realization probabilities of those children """ profile = game.mixed_behavior_profile(rational=rational_flag) - for node in game.nodes(): + for node in game.nodes: if node.is_terminal or node.player.is_chance: continue expected_val = 0 @@ -1080,23 +1080,23 @@ def _get_and_check_answers(game: gbt.Game, action_probs1: tuple, action_probs2: ###################################################################################### # belief (at nodes) (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda x, y: x.belief(y), lambda x: x.nodes()), + lambda x, y: x.belief(y), lambda x: x.nodes), (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.belief(y), lambda x: x.nodes()), + lambda x, y: x.belief(y), lambda x: x.nodes), (games.create_myerson_2_card_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda x, y: x.belief(y), lambda x: x.nodes()), + lambda x, y: x.belief(y), lambda x: x.nodes), (games.create_myerson_2_card_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.belief(y), lambda x: x.nodes()), + lambda x, y: x.belief(y), lambda x: x.nodes), ###################################################################################### # realiz_prob (at nodes) (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda x, y: x.realiz_prob(y), lambda x: x.nodes()), + lambda x, y: x.realiz_prob(y), lambda x: x.nodes), (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.realiz_prob(y), lambda x: x.nodes()), + lambda x, y: x.realiz_prob(y), lambda x: x.nodes), (games.create_myerson_2_card_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda x, y: x.realiz_prob(y), lambda x: x.nodes()), + lambda x, y: x.realiz_prob(y), lambda x: x.nodes), (games.create_myerson_2_card_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.realiz_prob(y), lambda x: x.nodes()), + lambda x, y: x.realiz_prob(y), lambda x: x.nodes), ###################################################################################### # infoset_prob (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, @@ -1141,16 +1141,16 @@ def _get_and_check_answers(game: gbt.Game, action_probs1: tuple, action_probs2: # node_value (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, lambda x, y: x.node_value(player=y[0], node=y[1]), - lambda x: list(product(x.players, x.nodes()))), + lambda x: list(product(x.players, x.nodes))), (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, lambda x, y: x.node_value(player=y[0], node=y[1]), - lambda x: list(product(x.players, x.nodes()))), + lambda x: list(product(x.players, x.nodes))), (games.create_myerson_2_card_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, lambda x, y: x.node_value(player=y[0], node=y[1]), - lambda x: list(product(x.players, x.nodes()))), + lambda x: list(product(x.players, x.nodes))), (games.create_myerson_2_card_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, lambda x, y: x.node_value(player=y[0], node=y[1]), - lambda x: list(product(x.players, x.nodes()))), + lambda x: list(product(x.players, x.nodes))), ###################################################################################### # liap_value (of profile, hence [1] for objects_to_test, any singleton collection would do) (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, From 54f32606f4aee080c9f71bd83ea9b2b3b4e7091d Mon Sep 17 00:00:00 2001 From: drdkad Date: Fri, 11 Apr 2025 11:29:01 +0100 Subject: [PATCH 4/7] clang-format --- src/games/gametree.cc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/games/gametree.cc b/src/games/gametree.cc index 9b07645db..88e75eb90 100644 --- a/src/games/gametree.cc +++ b/src/games/gametree.cc @@ -605,12 +605,11 @@ GameInfoset GameTreeNodeRep::AppendMove(GameInfoset p_infoset) m_efg->IncrementVersion(); m_infoset = dynamic_cast(p_infoset.operator->()); m_infoset->AddMember(this); - std::for_each( - m_infoset->m_actions.begin(), m_infoset->m_actions.end(), - [this](const GameActionRep *) { - m_children.push_back(new GameTreeNodeRep(m_efg, this)); - m_efg->m_numNodes++; - }); + std::for_each(m_infoset->m_actions.begin(), m_infoset->m_actions.end(), + [this](const GameActionRep *) { + m_children.push_back(new GameTreeNodeRep(m_efg, this)); + m_efg->m_numNodes++; + }); m_efg->ClearComputedValues(); m_efg->Canonicalize(); return m_infoset; From 3b9a2fdb727e7b418ecf97fd352f6259f63db463 Mon Sep 17 00:00:00 2001 From: drdkad Date: Fri, 11 Apr 2025 11:59:36 +0100 Subject: [PATCH 5/7] clang-tidy --- src/games/gametree.cc | 3 +-- src/games/gametree.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/games/gametree.cc b/src/games/gametree.cc index 88e75eb90..ca8ecb3e4 100644 --- a/src/games/gametree.cc +++ b/src/games/gametree.cc @@ -671,8 +671,7 @@ GameInfoset GameTreeNodeRep::InsertMove(GameInfoset p_infoset) //------------------------------------------------------------------------ GameTreeRep::GameTreeRep() - : m_numNodes(1), m_computedValues(false), m_doCanon(true), - m_root(new GameTreeNodeRep(this, nullptr)), m_chance(new GamePlayerRep(this, 0)) + : m_root(new GameTreeNodeRep(this, nullptr)), m_chance(new GamePlayerRep(this, 0)) { } diff --git a/src/games/gametree.h b/src/games/gametree.h index 70862c7de..80e311860 100644 --- a/src/games/gametree.h +++ b/src/games/gametree.h @@ -213,7 +213,7 @@ class GameTreeRep : public GameExplicitRep { mutable bool m_computedValues{false}, m_doCanon{true}; GameTreeNodeRep *m_root; GamePlayerRep *m_chance; - std::size_t m_numNodes; + std::size_t m_numNodes = 1; /// @name Private auxiliary functions //@{ From 05ac5528724ef27edca531f3143a4d8f0da4dcd4 Mon Sep 17 00:00:00 2001 From: drdkad Date: Fri, 11 Apr 2025 21:05:07 +0100 Subject: [PATCH 6/7] Revert "change order of nodes while traversing GameNodes" This reverts commit 4f4f0b40241705602c1779dcfb766c8e8015aff7. --- tests/test_behav.py | 88 ++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/tests/test_behav.py b/tests/test_behav.py index fd6c669e8..6636cf04c 100644 --- a/tests/test_behav.py +++ b/tests/test_behav.py @@ -584,64 +584,64 @@ def test_set_probabilities_player_by_label(game: gbt.Game, player_label: str, be @pytest.mark.parametrize( "game,node_idx,realiz_prob,rational_flag", - [(games.create_mixed_behav_game_efg(), 0, "1/8", True), - (games.create_mixed_behav_game_efg(), 1, "1/8", True), + [(games.create_mixed_behav_game_efg(), 0, "1", True), + (games.create_mixed_behav_game_efg(), 1, "1/2", True), (games.create_mixed_behav_game_efg(), 2, "1/4", True), (games.create_mixed_behav_game_efg(), 3, "1/8", True), (games.create_mixed_behav_game_efg(), 4, "1/8", True), (games.create_mixed_behav_game_efg(), 5, "1/4", True), - (games.create_mixed_behav_game_efg(), 6, "1/2", True), + (games.create_mixed_behav_game_efg(), 6, "1/8", True), (games.create_mixed_behav_game_efg(), 7, "1/8", True), - (games.create_mixed_behav_game_efg(), 8, "1/8", True), + (games.create_mixed_behav_game_efg(), 8, "1/2", True), (games.create_mixed_behav_game_efg(), 9, "1/4", True), (games.create_mixed_behav_game_efg(), 10, "1/8", True), (games.create_mixed_behav_game_efg(), 11, "1/8", True), (games.create_mixed_behav_game_efg(), 12, "1/4", True), - (games.create_mixed_behav_game_efg(), 13, "1/2", True), - (games.create_mixed_behav_game_efg(), 14, "1", True), - (games.create_mixed_behav_game_efg(), 0, 0.125, False), - (games.create_mixed_behav_game_efg(), 1, 0.125, False), + (games.create_mixed_behav_game_efg(), 13, "1/8", True), + (games.create_mixed_behav_game_efg(), 14, "1/8", True), + (games.create_mixed_behav_game_efg(), 0, 1.0, False), + (games.create_mixed_behav_game_efg(), 1, 0.5, False), (games.create_mixed_behav_game_efg(), 2, 0.25, False), (games.create_mixed_behav_game_efg(), 3, 0.125, False), (games.create_mixed_behav_game_efg(), 4, 0.125, False), (games.create_mixed_behav_game_efg(), 5, 0.25, False), - (games.create_mixed_behav_game_efg(), 6, 0.5, False), + (games.create_mixed_behav_game_efg(), 6, 0.125, False), (games.create_mixed_behav_game_efg(), 7, 0.125, False), - (games.create_mixed_behav_game_efg(), 8, 0.125, False), + (games.create_mixed_behav_game_efg(), 8, 0.5, False), (games.create_mixed_behav_game_efg(), 9, 0.25, False), (games.create_mixed_behav_game_efg(), 10, 0.125, False), (games.create_mixed_behav_game_efg(), 11, 0.125, False), (games.create_mixed_behav_game_efg(), 12, 0.25, False), - (games.create_mixed_behav_game_efg(), 13, 0.5, False), - (games.create_mixed_behav_game_efg(), 14, 1, False), - (games.create_myerson_2_card_poker_efg(), 0, "1/8", True), - (games.create_myerson_2_card_poker_efg(), 1, "1/8", True), + (games.create_mixed_behav_game_efg(), 13, 0.125, False), + (games.create_mixed_behav_game_efg(), 14, 0.125, False), + (games.create_myerson_2_card_poker_efg(), 0, "1", True), + (games.create_myerson_2_card_poker_efg(), 1, "1/2", True), (games.create_myerson_2_card_poker_efg(), 2, "1/4", True), - (games.create_myerson_2_card_poker_efg(), 3, "1/4", True), - (games.create_myerson_2_card_poker_efg(), 4, "1/2", True), - (games.create_myerson_2_card_poker_efg(), 5, "1/8", True), - (games.create_myerson_2_card_poker_efg(), 6, "1/8", True), + (games.create_myerson_2_card_poker_efg(), 3, "1/8", True), + (games.create_myerson_2_card_poker_efg(), 4, "1/8", True), + (games.create_myerson_2_card_poker_efg(), 5, "1/4", True), + (games.create_myerson_2_card_poker_efg(), 6, "1/2", True), (games.create_myerson_2_card_poker_efg(), 7, "1/4", True), - (games.create_myerson_2_card_poker_efg(), 8, "1/4", True), - (games.create_myerson_2_card_poker_efg(), 9, "1/2", True), - (games.create_myerson_2_card_poker_efg(), 10, "1", True), - (games.create_myerson_2_card_poker_efg(), 0, 0.125, False), - (games.create_myerson_2_card_poker_efg(), 1, 0.125, False), + (games.create_myerson_2_card_poker_efg(), 8, "1/8", True), + (games.create_myerson_2_card_poker_efg(), 9, "1/8", True), + (games.create_myerson_2_card_poker_efg(), 10, "1/4", True), + (games.create_myerson_2_card_poker_efg(), 0, 1.0, False), + (games.create_myerson_2_card_poker_efg(), 1, 0.5, False), (games.create_myerson_2_card_poker_efg(), 2, 0.25, False), - (games.create_myerson_2_card_poker_efg(), 3, 0.25, False), - (games.create_myerson_2_card_poker_efg(), 4, 0.5, False), - (games.create_myerson_2_card_poker_efg(), 5, 0.125, False), - (games.create_myerson_2_card_poker_efg(), 6, 0.125, False), + (games.create_myerson_2_card_poker_efg(), 3, 0.125, False), + (games.create_myerson_2_card_poker_efg(), 4, 0.125, False), + (games.create_myerson_2_card_poker_efg(), 5, 0.25, False), + (games.create_myerson_2_card_poker_efg(), 6, 0.5, False), (games.create_myerson_2_card_poker_efg(), 7, 0.25, False), - (games.create_myerson_2_card_poker_efg(), 8, 0.25, False), - (games.create_myerson_2_card_poker_efg(), 9, 0.5, False), - (games.create_myerson_2_card_poker_efg(), 10, 1, False)] + (games.create_myerson_2_card_poker_efg(), 8, 0.125, False), + (games.create_myerson_2_card_poker_efg(), 9, 0.125, False), + (games.create_myerson_2_card_poker_efg(), 10, 0.25, False)] ) def test_realiz_prob_nodes_reference(game: gbt.Game, node_idx: int, realiz_prob: typing.Union[str, float], rational_flag: bool): profile = game.mixed_behavior_profile(rational=rational_flag) realiz_prob = (gbt.Rational(realiz_prob) if rational_flag else realiz_prob) - node = list(game.nodes)[node_idx] + node = game.nodes()[node_idx] assert profile.realiz_prob(node) == realiz_prob @@ -890,7 +890,7 @@ def test_martingale_property_of_node_value(game: gbt.Game, rational_flag: bool): realization probabilities of those children """ profile = game.mixed_behavior_profile(rational=rational_flag) - for node in game.nodes: + for node in game.nodes(): if node.is_terminal or node.player.is_chance: continue expected_val = 0 @@ -1080,23 +1080,23 @@ def _get_and_check_answers(game: gbt.Game, action_probs1: tuple, action_probs2: ###################################################################################### # belief (at nodes) (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda x, y: x.belief(y), lambda x: x.nodes), + lambda x, y: x.belief(y), lambda x: x.nodes()), (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.belief(y), lambda x: x.nodes), + lambda x, y: x.belief(y), lambda x: x.nodes()), (games.create_myerson_2_card_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda x, y: x.belief(y), lambda x: x.nodes), + lambda x, y: x.belief(y), lambda x: x.nodes()), (games.create_myerson_2_card_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.belief(y), lambda x: x.nodes), + lambda x, y: x.belief(y), lambda x: x.nodes()), ###################################################################################### # realiz_prob (at nodes) (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda x, y: x.realiz_prob(y), lambda x: x.nodes), + lambda x, y: x.realiz_prob(y), lambda x: x.nodes()), (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.realiz_prob(y), lambda x: x.nodes), + lambda x, y: x.realiz_prob(y), lambda x: x.nodes()), (games.create_myerson_2_card_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda x, y: x.realiz_prob(y), lambda x: x.nodes), + lambda x, y: x.realiz_prob(y), lambda x: x.nodes()), (games.create_myerson_2_card_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.realiz_prob(y), lambda x: x.nodes), + lambda x, y: x.realiz_prob(y), lambda x: x.nodes()), ###################################################################################### # infoset_prob (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, @@ -1141,16 +1141,16 @@ def _get_and_check_answers(game: gbt.Game, action_probs1: tuple, action_probs2: # node_value (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, lambda x, y: x.node_value(player=y[0], node=y[1]), - lambda x: list(product(x.players, x.nodes))), + lambda x: list(product(x.players, x.nodes()))), (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, lambda x, y: x.node_value(player=y[0], node=y[1]), - lambda x: list(product(x.players, x.nodes))), + lambda x: list(product(x.players, x.nodes()))), (games.create_myerson_2_card_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, lambda x, y: x.node_value(player=y[0], node=y[1]), - lambda x: list(product(x.players, x.nodes))), + lambda x: list(product(x.players, x.nodes()))), (games.create_myerson_2_card_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, lambda x, y: x.node_value(player=y[0], node=y[1]), - lambda x: list(product(x.players, x.nodes))), + lambda x: list(product(x.players, x.nodes()))), ###################################################################################### # liap_value (of profile, hence [1] for objects_to_test, any singleton collection would do) (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, From 8de2bdc844339e3e2cf11e3efd4db86a19f1670b Mon Sep 17 00:00:00 2001 From: drdkad Date: Wed, 16 Apr 2025 15:55:04 +0100 Subject: [PATCH 7/7] revert node traversal order back to the correct one --- src/pygambit/game.pxi | 11 +++++++++-- tests/test_behav.py | 28 ++++++++++++++-------------- tests/test_extensive.py | 5 ----- tests/test_node.py | 18 +++++++++--------- 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/pygambit/game.pxi b/src/pygambit/game.pxi index 916466dcb..166701d83 100644 --- a/src/pygambit/game.pxi +++ b/src/pygambit/game.pxi @@ -186,9 +186,9 @@ class GameNodes: def __iter__(self) -> typing.Iterator[Node]: def dfs(node): + yield node for child in node.children: yield from dfs(child) - yield node if not self.game.deref().IsTree(): return yield from dfs(Node.wrap(self.game.deref().GetRoot())) @@ -703,7 +703,14 @@ class Game: @property def nodes(self) -> GameNodes: - """The set of nodes in the game.""" + """The set of nodes in the game. + + Iteration over this property yields the nodes in the order of depth-first search. + + .. versionchanged:: 16.4 + Changed from a method ``nodes()`` to a property. Access as + ``game.nodes`` instead of ``game.nodes()``. + """ return GameNodes.wrap(self.game) @property diff --git a/tests/test_behav.py b/tests/test_behav.py index 6636cf04c..fd7e93e85 100644 --- a/tests/test_behav.py +++ b/tests/test_behav.py @@ -641,7 +641,7 @@ def test_realiz_prob_nodes_reference(game: gbt.Game, node_idx: int, realiz_prob: typing.Union[str, float], rational_flag: bool): profile = game.mixed_behavior_profile(rational=rational_flag) realiz_prob = (gbt.Rational(realiz_prob) if rational_flag else realiz_prob) - node = game.nodes()[node_idx] + node = list(game.nodes)[node_idx] assert profile.realiz_prob(node) == realiz_prob @@ -890,7 +890,7 @@ def test_martingale_property_of_node_value(game: gbt.Game, rational_flag: bool): realization probabilities of those children """ profile = game.mixed_behavior_profile(rational=rational_flag) - for node in game.nodes(): + for node in game.nodes: if node.is_terminal or node.player.is_chance: continue expected_val = 0 @@ -1080,23 +1080,23 @@ def _get_and_check_answers(game: gbt.Game, action_probs1: tuple, action_probs2: ###################################################################################### # belief (at nodes) (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda x, y: x.belief(y), lambda x: x.nodes()), + lambda x, y: x.belief(y), lambda x: x.nodes), (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.belief(y), lambda x: x.nodes()), + lambda x, y: x.belief(y), lambda x: x.nodes), (games.create_myerson_2_card_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda x, y: x.belief(y), lambda x: x.nodes()), + lambda x, y: x.belief(y), lambda x: x.nodes), (games.create_myerson_2_card_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.belief(y), lambda x: x.nodes()), + lambda x, y: x.belief(y), lambda x: x.nodes), ###################################################################################### # realiz_prob (at nodes) (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda x, y: x.realiz_prob(y), lambda x: x.nodes()), + lambda x, y: x.realiz_prob(y), lambda x: x.nodes), (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.realiz_prob(y), lambda x: x.nodes()), + lambda x, y: x.realiz_prob(y), lambda x: x.nodes), (games.create_myerson_2_card_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda x, y: x.realiz_prob(y), lambda x: x.nodes()), + lambda x, y: x.realiz_prob(y), lambda x: x.nodes), (games.create_myerson_2_card_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.realiz_prob(y), lambda x: x.nodes()), + lambda x, y: x.realiz_prob(y), lambda x: x.nodes), ###################################################################################### # infoset_prob (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, @@ -1141,16 +1141,16 @@ def _get_and_check_answers(game: gbt.Game, action_probs1: tuple, action_probs2: # node_value (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, lambda x, y: x.node_value(player=y[0], node=y[1]), - lambda x: list(product(x.players, x.nodes()))), + lambda x: list(product(x.players, x.nodes))), (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, lambda x, y: x.node_value(player=y[0], node=y[1]), - lambda x: list(product(x.players, x.nodes()))), + lambda x: list(product(x.players, x.nodes))), (games.create_myerson_2_card_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, lambda x, y: x.node_value(player=y[0], node=y[1]), - lambda x: list(product(x.players, x.nodes()))), + lambda x: list(product(x.players, x.nodes))), (games.create_myerson_2_card_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, lambda x, y: x.node_value(player=y[0], node=y[1]), - lambda x: list(product(x.players, x.nodes()))), + lambda x: list(product(x.players, x.nodes))), ###################################################################################### # liap_value (of profile, hence [1] for objects_to_test, any singleton collection would do) (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, diff --git a/tests/test_extensive.py b/tests/test_extensive.py index 3e3976521..acde88e93 100644 --- a/tests/test_extensive.py +++ b/tests/test_extensive.py @@ -55,11 +55,6 @@ def test_game_add_players_nolabel(): game.add_player() -def test_game_num_nodes(): - game = games.read_from_file("basic_extensive_game.efg") - assert len(game.nodes) == 15 - - def test_game_is_perfect_recall(): game = games.read_from_file("perfect_recall.efg") assert game.is_perfect_recall diff --git a/tests/test_node.py b/tests/test_node.py index bca4e6732..6d26e9d07 100644 --- a/tests/test_node.py +++ b/tests/test_node.py @@ -202,7 +202,7 @@ def test_copy_tree_onto_nondescendent_terminal_node(): g = games.read_from_file("e01.efg") list_nodes = list(g.nodes) src_node = list_nodes[3] # path=[1, 0] - dest_node = list_nodes[0] # path=[0, 0] + dest_node = list_nodes[2] # path=[0, 0] g.copy_tree(src_node, dest_node) @@ -213,8 +213,8 @@ def test_copy_tree_onto_descendent_terminal_node(): """Test copying a subtree to a node that's a descendent of the original.""" g = games.read_from_file("e01.efg") list_nodes = list(g.nodes) - src_node = list_nodes[4] # path=[0] - dest_node = list_nodes[1] # path=[0, 1, 0] + src_node = list_nodes[1] # path=[0] + dest_node = list_nodes[4] # path=[0, 1, 0] g.copy_tree(src_node, dest_node) @@ -478,7 +478,7 @@ def test_len_after_append_move(): initial_number_of_nodes = len(game.nodes) list_nodes = list(game.nodes) - terminal_node = list_nodes[1] + terminal_node = list_nodes[5] # path=[1, 1, 0] player = game.players[0] actions_to_add = ["T", "M", "B"] @@ -494,10 +494,10 @@ def test_len_after_append_infoset(): initial_number_of_nodes = len(game.nodes) list_nodes = list(game.nodes) - member_node = list_nodes[5] # path=[1] + member_node = list_nodes[2] # path=[1] infoset_to_modify = member_node.infoset number_of_infoset_actions = len(infoset_to_modify.actions) - terminal_node_to_add = list_nodes[3] # path=[1, 1, 1] + terminal_node_to_add = list_nodes[6] # path=[1, 1, 1] game.append_infoset(terminal_node_to_add, infoset_to_modify) @@ -564,9 +564,9 @@ def test_len_after_insert_infoset(): initial_number_of_nodes = len(game.nodes) list_nodes = list(game.nodes) - member_node = list_nodes[4] # path=[1, 1] + member_node = list_nodes[6] # path=[1] infoset_to_modify = member_node.infoset - node_to_insert_above = list_nodes[1] # path=[0, 1] + node_to_insert_above = list_nodes[7] # path=[0, 1] number_of_infoset_actions = len(infoset_to_modify.actions) game.insert_infoset(node_to_insert_above, infoset_to_modify) @@ -581,7 +581,7 @@ def test_len_after_copy_tree(): initial_number_of_nodes = len(game.nodes) list_nodes = list(game.nodes) src_node = list_nodes[3] # path=[1, 0] - dest_node = list_nodes[0] # path=[0, 0] + dest_node = list_nodes[2] # path=[0, 0] number_of_src_ancestors = _count_subtree_nodes(src_node) game.copy_tree(src_node, dest_node)