From a67c7fa198737938978d7b32ddd4c1b83be420c8 Mon Sep 17 00:00:00 2001 From: Michael Aichmueller Date: Tue, 23 May 2023 17:53:51 +0200 Subject: [PATCH 1/4] add change in interface of istate tree --- open_spiel/algorithms/infostate_tree.h | 77 +++++++++++++++++++------- 1 file changed, 58 insertions(+), 19 deletions(-) diff --git a/open_spiel/algorithms/infostate_tree.h b/open_spiel/algorithms/infostate_tree.h index 2f02dd1041..6d9c60d473 100644 --- a/open_spiel/algorithms/infostate_tree.h +++ b/open_spiel/algorithms/infostate_tree.h @@ -261,6 +261,7 @@ class InfostateNode; // of the game, up to some move limit. std::shared_ptr MakeInfostateTree(const Game& game, Player acting_player, + bool store_world_states = false, int max_move_limit = 1000); // Creates an infostate tree for a player based on some start states, @@ -269,6 +270,7 @@ std::shared_ptr MakeInfostateTree( const std::vector& start_states, const std::vector& chance_reach_probs, std::shared_ptr infostate_observer, Player acting_player, + bool store_world_states = false, int max_move_ahead_limit = 1000); // Creates an infostate tree based on some leaf infostate nodes coming from @@ -276,6 +278,7 @@ std::shared_ptr MakeInfostateTree( // This is useful for easily constructing (depth-limited) tree continuations. std::shared_ptr MakeInfostateTree( const std::vector& start_nodes, + bool store_world_states = false, int max_move_ahead_limit = 1000); // C++17 does not allow implicit conversion of non-const pointers to const @@ -283,6 +286,7 @@ std::shared_ptr MakeInfostateTree( // This just adds const to the pointers and calls the other MakeInfostateTree. std::shared_ptr MakeInfostateTree( const std::vector& start_nodes, + bool store_world_states = false, int max_move_ahead_limit = 1000); class InfostateTree final { @@ -294,15 +298,16 @@ class InfostateTree final { InfostateTree(const std::vector& start_states, const std::vector& chance_reach_probs, std::shared_ptr infostate_observer, - Player acting_player, int max_move_ahead_limit); + Player acting_player, bool store_world_states, + int max_move_ahead_limit); // Friend factories. friend std::shared_ptr MakeInfostateTree(const Game&, Player, - int); + bool, int); friend std::shared_ptr MakeInfostateTree( const std::vector&, const std::vector&, - std::shared_ptr, Player, int); + std::shared_ptr, Player, bool, int); friend std::shared_ptr MakeInfostateTree( - const std::vector&, int); + const std::vector&, bool, int); public: // -- Root accessors --------------------------------------------------------- @@ -381,6 +386,8 @@ class InfostateTree final { return nodes_at_depths_.at(depth); } + bool stores_all_world_states() const { return store_all_world_states_; } + // -- Tree operations -------------------------------------------------------- // Compute best response and value based on gradient from opponents. // This consumes the gradient vector, as it is used to compute the value. @@ -404,6 +411,9 @@ class InfostateTree final { std::vector sequences_; // The last vector corresponds to the leaf nodes. std::vector> nodes_at_depths_; + // whether to store all underlying world states (histories) of the infostate + // nodes + const bool store_all_world_states_; // Utility functions whenever we create a new node for the tree. std::unique_ptr MakeNode(InfostateNode* parent, @@ -420,20 +430,23 @@ class InfostateTree final { // to balance all the leaves. void RebalanceTree(); - void UpdateLeafNode(InfostateNode* node, const State& state, - size_t leaf_depth, double chance_reach_probs); + void UpdateNode(InfostateNode* node, std::shared_ptr state, + size_t node_depth, + double chance_reach_probs); // Build the tree. void RecursivelyBuildTree(InfostateNode* parent, size_t depth, - const State& state, int move_limit, + std::shared_ptr state, int move_limit, double chance_reach_prob); - void BuildTerminalNode(InfostateNode* parent, size_t depth, - const State& state, double chance_reach_prob); - void BuildDecisionNode(InfostateNode* parent, size_t depth, - const State& state, int move_limit, + std::pair BuildTerminalNode(InfostateNode* parent, size_t depth, + const std::shared_ptr& state, double chance_reach_prob); + std::pair + BuildDecisionNode(InfostateNode* parent, size_t depth, + const std::shared_ptr& state, int move_limit, double chance_reach_prob); - void BuildObservationNode(InfostateNode* parent, size_t depth, - const State& state, int move_limit, + std::pair + BuildObservationNode(InfostateNode* parent, size_t depth, + const std::shared_ptr& state, int move_limit, double chance_reach_prob); void CollectNodesAtDepth(InfostateNode* node, size_t depth); @@ -524,7 +537,7 @@ class InfostateNode final { /*const*/ std::vector> children_; // Store States that correspond to a leaf node. // This is not const so that we can add corresponding states. - /*const*/ std::vector> corresponding_states_; + /*const*/ std::vector> corresponding_states_; // Store chance reach probs for States that correspond to a leaf node. // This is not const so that we can add corresponding reaches. /*const*/ std::vector corresponding_ch_reaches_; @@ -550,18 +563,23 @@ class InfostateNode final { const InfostateNodeType& type() const { return type_; } size_t depth() const { return depth_; } bool is_root_node() const { return !parent_; } + bool is_filler_node() const { return infostate_string_ == kFillerInfostate; } bool has_infostate_string() const { return infostate_string_ != kFillerInfostate && infostate_string_ != kDummyRootNodeInfostate; } - const std::string& infostate_string() const { + std::string_view infostate_string() const { // Avoid working with empty infostate strings. SPIEL_DCHECK_TRUE(has_infostate_string()); return infostate_string_; } // -- Children accessors. ---------------------------------------------------- - InfostateNode* child_at(int i) const { return children_.at(i).get(); } + InfostateNode* child_at(int i, bool include_filler_node = true) const { + if(include_filler_node) + return _child_at(i); + return _child_at(i); + } int num_children() const { return children_.size(); } VecWithUniquePtrsIterator child_iterator() const { return VecWithUniquePtrsIterator(children_); @@ -609,11 +627,19 @@ class InfostateNode final { size_t corresponding_states_size() const { return corresponding_states_.size(); } - const std::vector>& corresponding_states() const { + + const std::vector< std::shared_ptr< const State > >& corresponding_states() const { + if(tree().stores_all_world_states()) { + return corresponding_states_; + } SPIEL_CHECK_TRUE(is_leaf_node()); return corresponding_states_; } - const std::vector& corresponding_chance_reach_probs() const { + + const std::vector< double >& corresponding_chance_reach_probs() const { + if(tree().stores_all_world_states()) { + return corresponding_ch_reaches_; + } SPIEL_CHECK_TRUE(is_leaf_node()); return corresponding_ch_reaches_; } @@ -645,7 +671,20 @@ class InfostateNode final { int at_index); InfostateNode* AddChild(std::unique_ptr child); - InfostateNode* GetChild(const std::string& infostate_string) const; + InfostateNode* GetChild(std::string_view infostate_string) const; + + template< bool accept_filler> + InfostateNode* _child_at(int i) const { + if constexpr(accept_filler) + return children_.at(i).get(); + else { + InfostateNode* child = children_.at(i).get(); + if(child->is_filler_node()) { + return child->_child_at(0); + } + return child; + } + } }; namespace internal { From c960967066b81d70a285e216c6b32fd5bea66336 Mon Sep 17 00:00:00 2001 From: Michael Aichmueller Date: Tue, 23 May 2023 17:54:24 +0200 Subject: [PATCH 2/4] implement logic for storing world states everywhere --- open_spiel/algorithms/infostate_tree.cc | 218 +++++++++++++++--------- 1 file changed, 142 insertions(+), 76 deletions(-) diff --git a/open_spiel/algorithms/infostate_tree.cc b/open_spiel/algorithms/infostate_tree.cc index 61584e2d0d..d558e73498 100644 --- a/open_spiel/algorithms/infostate_tree.cc +++ b/open_spiel/algorithms/infostate_tree.cc @@ -62,8 +62,7 @@ InfostateNode* InfostateNode::AddChild(std::unique_ptr child) { return children_.back().get(); } -InfostateNode* InfostateNode::GetChild( - const std::string& infostate_string) const { +InfostateNode* InfostateNode::GetChild(std::string_view infostate_string) const { for (const std::unique_ptr& child : children_) { if (child->infostate_string() == infostate_string) return child.get(); } @@ -161,10 +160,13 @@ void InfostateNode::SwapParent(std::unique_ptr self, InfostateTree::InfostateTree(const std::vector& start_states, const std::vector& chance_reach_probs, std::shared_ptr infostate_observer, - Player acting_player, int max_move_ahead_limit) + Player acting_player, + bool store_world_states, + int max_move_ahead_limit) : acting_player_(acting_player), infostate_observer_(std::move(infostate_observer)), - root_(MakeRootNode()) { + root_(MakeRootNode()), + store_all_world_states_(store_world_states) { SPIEL_CHECK_FALSE(start_states.empty()); SPIEL_CHECK_EQ(start_states.size(), chance_reach_probs.size()); SPIEL_CHECK_GE(acting_player_, 0); @@ -178,7 +180,8 @@ InfostateTree::InfostateTree(const std::vector& start_states, } for (int i = 0; i < start_states.size(); ++i) { - RecursivelyBuildTree(root_.get(), /*depth=*/1, *start_states[i], + RecursivelyBuildTree(root_.get(), /*depth=*/1, + std::shared_ptr(start_states[i]->Clone()), start_max_move_number + max_move_ahead_limit, chance_reach_probs[i]); } @@ -225,11 +228,11 @@ std::unique_ptr InfostateTree::MakeNode( : std::vector(); // Instantiate node using new to make sure that we can call // the private constructor. - auto node = std::unique_ptr(new InfostateNode( + auto node = new InfostateNode( *this, parent, parent->num_children(), type, infostate_string, terminal_utility, terminal_ch_reach_prob, depth, std::move(legal_actions), - std::move(terminal_history))); - return node; + std::move(terminal_history)); + return std::unique_ptr{node}; } std::unique_ptr InfostateTree::MakeRootNode() const { @@ -241,46 +244,56 @@ std::unique_ptr InfostateTree::MakeRootNode() const { /*depth=*/0, /*legal_actions=*/{}, /*terminal_history=*/{})); } -void InfostateTree::UpdateLeafNode(InfostateNode* node, const State& state, - size_t leaf_depth, - double chance_reach_probs) { - tree_height_ = std::max(tree_height_, leaf_depth); - node->corresponding_states_.push_back(state.Clone()); +void InfostateTree::UpdateNode(InfostateNode* node, + std::shared_ptr state, + size_t node_depth, + double chance_reach_probs) { + tree_height_ = std::max(tree_height_, node_depth); + node->corresponding_states_.push_back(std::move(state)); node->corresponding_ch_reaches_.push_back(chance_reach_probs); } void InfostateTree::RecursivelyBuildTree(InfostateNode* parent, size_t depth, - const State& state, int move_limit, + std::shared_ptr state, + int move_limit, double chance_reach_prob) { - if (state.IsTerminal()) - return BuildTerminalNode(parent, depth, state, chance_reach_prob); - else if (state.IsPlayerActing(acting_player_)) - return BuildDecisionNode(parent, depth, state, move_limit, - chance_reach_prob); - else - return BuildObservationNode(parent, depth, state, move_limit, - chance_reach_prob); + auto [child_node, leaf_update] = std::invoke([&] { + if (state->IsTerminal()) + return BuildTerminalNode(parent, depth, state, chance_reach_prob); + else if (state->IsPlayerActing(acting_player_)) + return BuildDecisionNode(parent, depth, state, move_limit, + chance_reach_prob); + else + return BuildObservationNode(parent, depth, state, move_limit, + chance_reach_prob); + }); + if(store_all_world_states_ or leaf_update) { + UpdateNode(child_node, std::move(state), depth, chance_reach_prob); + } } -void InfostateTree::BuildTerminalNode(InfostateNode* parent, size_t depth, - const State& state, +std::pair +InfostateTree::BuildTerminalNode(InfostateNode* parent, size_t depth, + const std::shared_ptr& state, double chance_reach_prob) { - const double terminal_utility = state.Returns()[acting_player_]; + const double terminal_utility = state->Returns()[acting_player_]; InfostateNode* terminal_node = parent->AddChild( MakeNode(parent, kTerminalInfostateNode, - infostate_observer_->StringFrom(state, acting_player_), - terminal_utility, chance_reach_prob, depth, &state)); - UpdateLeafNode(terminal_node, state, depth, chance_reach_prob); + infostate_observer_->StringFrom(*state, acting_player_), + terminal_utility, chance_reach_prob, depth, state.get())); + return {terminal_node, true}; } -void InfostateTree::BuildDecisionNode(InfostateNode* parent, size_t depth, - const State& state, int move_limit, - double chance_reach_prob) { +std::pair +InfostateTree::BuildDecisionNode(InfostateNode* parent, + size_t depth, + const std::shared_ptr& state, + int move_limit, double chance_reach_prob) { SPIEL_DCHECK_EQ(parent->type(), kObservationInfostateNode); std::string info_state = - infostate_observer_->StringFrom(state, acting_player_); + infostate_observer_->StringFrom(*state, acting_player_); InfostateNode* decision_node = parent->GetChild(info_state); - const bool is_leaf_node = state.MoveNumber() >= move_limit; + const bool is_leaf_node = state->MoveNumber() >= move_limit; if (decision_node) { // The decision node has been already constructed along with children @@ -289,40 +302,64 @@ void InfostateTree::BuildDecisionNode(InfostateNode* parent, size_t depth, SPIEL_DCHECK_EQ(decision_node->type(), kDecisionInfostateNode); if (is_leaf_node) { // Do not build deeper. - return UpdateLeafNode(decision_node, state, depth, chance_reach_prob); + return {decision_node, true}; } - if (state.IsSimultaneousNode()) { - const ActionView action_view(state); - for (int i = 0; i < action_view.legal_actions[acting_player_].size(); + if (state->IsSimultaneousNode()) { + const ActionView action_view(*state); + for (int i = 0; + i < action_view.legal_actions[acting_player_].size(); ++i) { InfostateNode* observation_node = decision_node->child_at(i); SPIEL_DCHECK_EQ(observation_node->type(), kObservationInfostateNode); for (Action flat_actions : action_view.fixed_action(acting_player_, i)) { - std::unique_ptr child = state.Child(flat_actions); - RecursivelyBuildTree(observation_node, depth + 2, *child, move_limit, + auto child_state = std::shared_ptr{state->Child(flat_actions)}; + // Only now we can advance the state, when we have all actions. + RecursivelyBuildTree(observation_node, + depth + 2, + child_state, + move_limit, chance_reach_prob); + if(store_all_world_states_ and not observation_node->is_filler_node()) { + UpdateNode(observation_node, + std::move(child_state), + depth + 2, + chance_reach_prob); + } } } } else { - std::vector legal_actions = state.LegalActions(acting_player_); + std::vector legal_actions = state->LegalActions(acting_player_); for (int i = 0; i < legal_actions.size(); ++i) { InfostateNode* observation_node = decision_node->child_at(i); SPIEL_DCHECK_EQ(observation_node->type(), kObservationInfostateNode); - std::unique_ptr child = state.Child(legal_actions.at(i)); - RecursivelyBuildTree(observation_node, depth + 2, *child, move_limit, + auto child_state = std::shared_ptr{state->Child(legal_actions.at(i))}; + // Only now we can advance the state, when we have all actions. + RecursivelyBuildTree(observation_node, + depth + 2, + child_state, + move_limit, chance_reach_prob); + if(store_all_world_states_ and not observation_node->is_filler_node()) { + UpdateNode(observation_node, + std::move(child_state), + depth, + chance_reach_prob); + } } } } else { // The decision node was not found yet. decision_node = parent->AddChild(MakeNode( parent, kDecisionInfostateNode, info_state, - /*terminal_utility=*/NAN, /*chance_reach_prob=*/NAN, depth, &state)); + /*terminal_utility=*/NAN, + /*chance_reach_prob=*/NAN, + depth, + state.get())); if (is_leaf_node) { // Do not build deeper. - return UpdateLeafNode(decision_node, state, depth, chance_reach_prob); + return {decision_node, true}; } // Build observation nodes right away after the decision node. @@ -330,9 +367,10 @@ void InfostateTree::BuildDecisionNode(InfostateNode* parent, size_t depth, // each time it might get some observations that branch the infostate // tree. - if (state.IsSimultaneousNode()) { - ActionView action_view(state); - for (int i = 0; i < action_view.legal_actions[acting_player_].size(); + if (state->IsSimultaneousNode()) { + ActionView action_view(*state); + for (int i = 0; + i < action_view.legal_actions[acting_player_].size(); ++i) { // We build a dummy observation node. // We can't ask for a proper infostate string or an originating state, @@ -344,64 +382,87 @@ void InfostateTree::BuildDecisionNode(InfostateNode* parent, size_t depth, /*infostate_string=*/kFillerInfostate, /*terminal_utility=*/NAN, /*chance_reach_prob=*/NAN, depth, /*originating_state=*/nullptr)); - for (Action flat_actions : action_view.fixed_action(acting_player_, i)) { + auto child_state = std::shared_ptr{state->Child(flat_actions)}; // Only now we can advance the state, when we have all actions. - std::unique_ptr child = state.Child(flat_actions); - RecursivelyBuildTree(observation_node, depth + 2, *child, move_limit, + RecursivelyBuildTree(observation_node, + depth + 2, + child_state, + move_limit, chance_reach_prob); + if(store_all_world_states_ and not observation_node->is_filler_node()) { + UpdateNode(observation_node, + std::move(child_state), + depth, + chance_reach_prob); + } } } } else { // Not a sim move node. - for (Action a : state.LegalActions()) { - std::unique_ptr child = state.Child(a); + for (Action a : state->LegalActions()) { + std::shared_ptr child = state->Child(a); InfostateNode* observation_node = decision_node->AddChild( MakeNode(decision_node, kObservationInfostateNode, infostate_observer_->StringFrom(*child, acting_player_), /*terminal_utility=*/NAN, /*chance_reach_prob=*/NAN, depth, child.get())); - RecursivelyBuildTree(observation_node, depth + 2, *child, move_limit, + RecursivelyBuildTree(observation_node, + depth + 2, + child, + move_limit, chance_reach_prob); + if(store_all_world_states_ and not observation_node->is_filler_node()) { + UpdateNode(observation_node, + std::move(child), + depth, + chance_reach_prob); + } } } } + return {decision_node, false}; } -void InfostateTree::BuildObservationNode(InfostateNode* parent, size_t depth, - const State& state, int move_limit, - double chance_reach_prob) { - SPIEL_DCHECK_TRUE(state.IsChanceNode() || - !state.IsPlayerActing(acting_player_)); - const bool is_leaf_node = state.MoveNumber() >= move_limit; +std::pair +InfostateTree::BuildObservationNode(InfostateNode* parent, size_t depth, + const std::shared_ptr& state, + int move_limit, + double chance_reach_prob) { + SPIEL_DCHECK_TRUE(state->IsChanceNode() || + !state->IsPlayerActing(acting_player_)); + const bool is_leaf_node = state->MoveNumber() >= move_limit; const std::string info_state = - infostate_observer_->StringFrom(state, acting_player_); + infostate_observer_->StringFrom(*state, acting_player_); InfostateNode* observation_node = parent->GetChild(info_state); if (!observation_node) { observation_node = parent->AddChild(MakeNode( parent, kObservationInfostateNode, info_state, - /*terminal_utility=*/NAN, /*chance_reach_prob=*/NAN, depth, &state)); + /*terminal_utility=*/NAN, /*chance_reach_prob=*/NAN, depth, state.get())); } SPIEL_DCHECK_EQ(observation_node->type(), kObservationInfostateNode); if (is_leaf_node) { // Do not build deeper. - return UpdateLeafNode(observation_node, state, depth, chance_reach_prob); + return {observation_node, true}; } - if (state.IsChanceNode()) { - for (std::pair action_prob : state.ChanceOutcomes()) { - std::unique_ptr child = state.Child(action_prob.first); - RecursivelyBuildTree(observation_node, depth + 1, *child, move_limit, + if (state->IsChanceNode()) { + for (std::pair action_prob : state->ChanceOutcomes()) { + RecursivelyBuildTree(observation_node, depth + 1, + state->Child(action_prob.first), move_limit, chance_reach_prob * action_prob.second); } } else { - for (Action a : state.LegalActions()) { - std::unique_ptr child = state.Child(a); - RecursivelyBuildTree(observation_node, depth + 1, *child, move_limit, + for (Action a : state->LegalActions()) { + RecursivelyBuildTree(observation_node, + depth + 1, + state->Child(a), + move_limit, chance_reach_prob); } } + return {observation_node, false}; } int InfostateTree::root_branching_factor() const { return root_->num_children(); @@ -409,23 +470,27 @@ int InfostateTree::root_branching_factor() const { std::shared_ptr MakeInfostateTree(const Game& game, Player acting_player, + bool store_world_states, int max_move_limit) { // Uses new instead of make_shared, because shared_ptr is not a friend and // can't call private constructors. return std::shared_ptr(new InfostateTree( {game.NewInitialState().get()}, /*chance_reach_probs=*/{1.}, - game.MakeObserver(kInfoStateObsType, {}), acting_player, max_move_limit)); + game.MakeObserver(kInfoStateObsType, {}), acting_player, + store_world_states, max_move_limit)); } std::shared_ptr MakeInfostateTree( - const std::vector& start_nodes, int max_move_ahead_limit) { + const std::vector& start_nodes, + bool store_world_states, int max_move_ahead_limit) { std::vector const_nodes(start_nodes.begin(), start_nodes.end()); - return MakeInfostateTree(const_nodes, max_move_ahead_limit); + return MakeInfostateTree(const_nodes, store_world_states, max_move_ahead_limit); } std::shared_ptr MakeInfostateTree( const std::vector& start_nodes, + bool store_world_states, int max_move_ahead_limit) { SPIEL_CHECK_FALSE(start_nodes.empty()); const InfostateNode* some_node = start_nodes[0]; @@ -458,17 +523,18 @@ std::shared_ptr MakeInfostateTree( // can't call private constructors. return std::shared_ptr(new InfostateTree( start_states, chance_reach_probs, originating_tree.infostate_observer_, - originating_tree.acting_player_, max_move_ahead_limit)); + originating_tree.acting_player_, store_world_states, max_move_ahead_limit)); } std::shared_ptr MakeInfostateTree( const std::vector& start_states, const std::vector& chance_reach_probs, std::shared_ptr infostate_observer, Player acting_player, - int max_move_ahead_limit) { + bool store_world_states, int max_move_ahead_limit) { return std::shared_ptr( - new InfostateTree(start_states, chance_reach_probs, infostate_observer, - acting_player, max_move_ahead_limit)); + new InfostateTree(start_states, chance_reach_probs, + std::move(infostate_observer), acting_player, + store_world_states, max_move_ahead_limit)); } SequenceId InfostateTree::empty_sequence() const { return root().sequence_id(); From 809a9b40570ec046519c2d437f38b71803c04274 Mon Sep 17 00:00:00 2001 From: Michael Aichmueller Date: Tue, 23 May 2023 17:54:47 +0200 Subject: [PATCH 3/4] adapt tests to use non-storing states maintains expected certificates --- open_spiel/algorithms/infostate_tree_test.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/open_spiel/algorithms/infostate_tree_test.cc b/open_spiel/algorithms/infostate_tree_test.cc index d284340c6f..63c8e71db0 100644 --- a/open_spiel/algorithms/infostate_tree_test.cc +++ b/open_spiel/algorithms/infostate_tree_test.cc @@ -62,7 +62,7 @@ std::shared_ptr MakeTree(const std::string& game_name, Player player, int max_move_limit = 1000) { std::shared_ptr tree = - MakeInfostateTree(*LoadGame(game_name), player, max_move_limit); + MakeInfostateTree(*LoadGame(game_name), player, false, max_move_limit); SPIEL_CHECK_TRUE(RecomputeBalance(*tree)); return tree; } @@ -86,7 +86,7 @@ std::shared_ptr MakeTree( std::shared_ptr tree = MakeInfostateTree(start_state_ptrs, start_reaches, infostate_observer, - player, max_move_limit); + player, false, max_move_limit); SPIEL_CHECK_TRUE(RecomputeBalance(*tree)); return tree; } @@ -297,8 +297,7 @@ void CheckTreeLeaves(const InfostateTree& tree, int move_limit) { int terminal_cnt = 0; int max_move_number = std::numeric_limits::min(); int min_move_number = std::numeric_limits::max(); - for (const std::unique_ptr& state : - leaf_node->corresponding_states()) { + for (const auto& state : leaf_node->corresponding_states()) { if (state->IsTerminal()) terminal_cnt++; max_move_number = std::max(max_move_number, state->MoveNumber()); min_move_number = std::min(min_move_number, state->MoveNumber()); From a14b01adcb6e1503e1130a69339b9f5028eba9f1 Mon Sep 17 00:00:00 2001 From: Michael Aichmueller Date: Wed, 24 May 2023 13:33:04 +0200 Subject: [PATCH 4/4] revert returning string_view from infostate_string() --- open_spiel/algorithms/infostate_tree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/open_spiel/algorithms/infostate_tree.h b/open_spiel/algorithms/infostate_tree.h index 6d9c60d473..63d7090e64 100644 --- a/open_spiel/algorithms/infostate_tree.h +++ b/open_spiel/algorithms/infostate_tree.h @@ -568,7 +568,7 @@ class InfostateNode final { return infostate_string_ != kFillerInfostate && infostate_string_ != kDummyRootNodeInfostate; } - std::string_view infostate_string() const { + const std::string& infostate_string() const { // Avoid working with empty infostate strings. SPIEL_DCHECK_TRUE(has_infostate_string()); return infostate_string_;