@@ -324,6 +324,16 @@ GameAction GameNodeRep::GetPriorAction() const
324324 return nullptr ;
325325}
326326
327+ GameAction GameNodeRep::GetOwnPriorAction () const
328+ {
329+ return m_game->GetOwnPriorAction (std::const_pointer_cast<GameNodeRep>(shared_from_this ()));
330+ }
331+
332+ std::set<GameAction> GameInfosetRep::GetOwnPriorActions () const
333+ {
334+ return m_game->GetOwnPriorActions (std::const_pointer_cast<GameInfosetRep>(shared_from_this ()));
335+ }
336+
327337void GameNodeRep::DeleteOutcome (GameOutcomeRep *outc)
328338{
329339 m_game->IncrementVersion ();
@@ -394,7 +404,7 @@ bool GameNodeRep::IsStrategyReachable() const
394404 auto tree_game = static_cast <GameTreeRep *>(m_game);
395405
396406 if (!tree_game->m_unreachableNodes ) {
397- tree_game->BuildInfosetParents ();
407+ tree_game->BuildUnreachableNodes ();
398408 }
399409
400410 // A node is reachable if it is NOT in the set of unreachable nodes.
@@ -786,15 +796,16 @@ Rational GameTreeRep::GetPlayerMaxPayoff(const GamePlayer &p_player) const
786796
787797bool GameTreeRep::IsPerfectRecall () const
788798{
789- if (m_infosetParents. empty () && !m_root->IsTerminal ()) {
790- const_cast <GameTreeRep *>( this )-> BuildInfosetParents ();
799+ if (!m_ownPriorActionInfo && !m_root->IsTerminal ()) {
800+ BuildOwnPriorActions ();
791801 }
792802
793803 if (GetRoot ()->IsTerminal ()) {
794804 return true ;
795805 }
796806
797- return std::all_of (m_infosetParents.cbegin (), m_infosetParents.cend (),
807+ return std::all_of (m_ownPriorActionInfo->infoset_map .cbegin (),
808+ m_ownPriorActionInfo->infoset_map .cend (),
798809 [](const auto &pair) { return pair.second .size () <= 1 ; });
799810}
800811
@@ -847,7 +858,7 @@ void GameTreeRep::ClearComputedValues() const
847858 player->m_strategies .clear ();
848859 }
849860 const_cast <GameTreeRep *>(this )->m_nodePlays .clear ();
850- const_cast <GameTreeRep *>( this )-> m_infosetParents . clear () ;
861+ m_ownPriorActionInfo = nullptr ;
851862 const_cast <GameTreeRep *>(this )->m_unreachableNodes = nullptr ;
852863 m_computedValues = false ;
853864}
@@ -889,34 +900,119 @@ std::vector<GameNodeRep *> GameTreeRep::BuildConsistentPlaysRecursiveImpl(GameNo
889900 return consistent_plays;
890901}
891902
892- void GameTreeRep::BuildInfosetParents ()
903+ void GameTreeRep::BuildOwnPriorActions () const
893904{
894- m_infosetParents.clear ();
895- m_unreachableNodes = std::make_unique<std::set<GameNodeRep *>>();
905+ auto info = std::make_shared<OwnPriorActionInfo>();
896906
897907 if (m_root->IsTerminal ()) {
898- m_infosetParents[m_root-> m_infoset ]. insert ( nullptr ) ;
908+ m_ownPriorActionInfo = info ;
899909 return ;
900910 }
901911
902- using AbsentMindedEdge = std::pair<GameAction, GameNode>;
903- using ActiveEdge = std::variant<GameNodeRep::Actions::iterator, AbsentMindedEdge>;
904- std::stack<ActiveEdge> position;
912+ info->node_map [m_root.get ()] = nullptr ;
913+ if (m_root->m_infoset ) {
914+ info->infoset_map [m_root->m_infoset ].insert (nullptr );
915+ }
905916
917+ using ActiveEdge = GameNodeRep::Actions::iterator;
918+
919+ std::stack<ActiveEdge> position;
906920 std::map<GamePlayer, std::stack<GameAction>> prior_actions;
907- std::map<GameInfoset, GameAction> path_choices;
908921
909922 for (auto player_rep : m_players) {
910923 prior_actions[GamePlayer (player_rep)].emplace (nullptr );
911924 }
912925 prior_actions[GamePlayer (m_chance)].emplace (nullptr );
913926
914927 position.emplace (m_root->GetActions ().begin ());
915- prior_actions[m_root->m_infoset ->m_player ->shared_from_this ()].emplace (nullptr );
916928 if (m_root->m_infoset ) {
917- m_infosetParents[m_root->m_infoset ].insert (nullptr );
929+ prior_actions[m_root->m_infoset ->m_player ->shared_from_this ()].emplace (nullptr );
930+ }
931+
932+ while (!position.empty ()) {
933+ ActiveEdge ¤t_edge = position.top ();
934+ auto node = current_edge.GetOwner ();
935+
936+ if (current_edge == node->GetActions ().end ()) {
937+ if (node->m_infoset ) {
938+ prior_actions.at (node->m_infoset ->m_player ->shared_from_this ()).pop ();
939+ }
940+ position.pop ();
941+ continue ;
942+ }
943+
944+ auto [action, child] = *current_edge;
945+ ++current_edge;
946+
947+ if (node->m_infoset ) {
948+ prior_actions.at (node->m_infoset ->m_player ->shared_from_this ()).top () = action;
949+ }
950+
951+ if (!child->IsTerminal ()) {
952+ if (child->m_infoset ) {
953+ auto child_player = child->m_infoset ->m_player ->shared_from_this ();
954+ auto prior_action = prior_actions.at (child_player).top ();
955+ GameActionRep *raw_prior = prior_action ? prior_action.get () : nullptr ;
956+
957+ info->node_map [child.get ()] = raw_prior;
958+ info->infoset_map [child->m_infoset ].insert (raw_prior);
959+
960+ position.emplace (child->GetActions ().begin ());
961+ prior_actions.at (child_player).emplace (nullptr );
962+ }
963+ else {
964+ position.emplace (child->GetActions ().begin ());
965+ }
966+ }
967+ }
968+ m_ownPriorActionInfo = info;
969+ }
970+
971+ GameAction GameTreeRep::GetOwnPriorAction (const GameNode &p_node) const
972+ {
973+ if (!m_ownPriorActionInfo) {
974+ BuildOwnPriorActions ();
975+ }
976+
977+ auto it = m_ownPriorActionInfo->node_map .find (p_node.get ());
978+ if (it != m_ownPriorActionInfo->node_map .end () && it->second ) {
979+ return it->second ->shared_from_this ();
980+ }
981+ return nullptr ;
982+ }
983+
984+ std::set<GameAction> GameTreeRep::GetOwnPriorActions (const GameInfoset &p_infoset) const
985+ {
986+ if (!m_ownPriorActionInfo) {
987+ BuildOwnPriorActions ();
988+ }
989+
990+ std::set<GameAction> result;
991+ auto it = m_ownPriorActionInfo->infoset_map .find (p_infoset.get ());
992+
993+ if (it != m_ownPriorActionInfo->infoset_map .end ()) {
994+ for (auto *ptr : it->second ) {
995+ result.insert (ptr ? ptr->shared_from_this () : nullptr );
996+ }
997+ }
998+ return result;
999+ }
1000+
1001+ void GameTreeRep::BuildUnreachableNodes ()
1002+ {
1003+ m_unreachableNodes = std::make_unique<std::set<GameNodeRep *>>();
1004+
1005+ if (m_root->IsTerminal ()) {
1006+ return ;
9181007 }
9191008
1009+ using AbsentMindedEdge = std::pair<GameAction, GameNode>;
1010+ using ActiveEdge = std::variant<GameNodeRep::Actions::iterator, AbsentMindedEdge>;
1011+
1012+ std::stack<ActiveEdge> position;
1013+ std::map<GameInfoset, GameAction> path_choices;
1014+ position.emplace (m_root->GetActions ().begin ());
1015+
9201016 while (!position.empty ()) {
9211017 ActiveEdge ¤t_edge = position.top ();
9221018 GameNode child, node;
@@ -927,7 +1023,6 @@ void GameTreeRep::BuildInfosetParents()
9271023 node = current_it.GetOwner ();
9281024
9291025 if (current_it == node->GetActions ().end ()) {
930- prior_actions.at (node->m_infoset ->m_player ->shared_from_this ()).pop ();
9311026 position.pop ();
9321027 path_choices.erase (node->m_infoset ->shared_from_this ());
9331028 continue ;
@@ -944,40 +1039,31 @@ void GameTreeRep::BuildInfosetParents()
9441039 child = node->GetChild (action);
9451040 }
9461041
947- prior_actions.at (node->m_infoset ->m_player ->shared_from_this ()).top () = action;
9481042 if (!child->IsTerminal ()) {
949- auto child_player = child->m_infoset ->m_player ->shared_from_this ();
950- auto prior_action = prior_actions.at (child_player).top ();
951- m_infosetParents[child->m_infoset ].insert (prior_action ? prior_action.get () : nullptr );
952-
1043+ // Check for Absent-Minded Re-entry of the infoset
9531044 if (path_choices.find (child->m_infoset ->shared_from_this ()) != path_choices.end ()) {
9541045 const GameAction replay_action = path_choices.at (child->m_infoset ->shared_from_this ());
9551046 position.emplace (AbsentMindedEdge{replay_action, child});
9561047
957- // Start of the traversal of unreachable subtrees
1048+ // Mark siblings and the nodes in their subtrees as unreachable
9581049 for (const auto &[current_action, subtree_root] : child->GetActions ()) {
9591050 if (current_action != replay_action) {
960-
9611051 std::stack<GameNodeRep *> nodes_to_visit;
9621052 nodes_to_visit.push (subtree_root.get ());
963-
9641053 while (!nodes_to_visit.empty ()) {
9651054 GameNodeRep *current_unreachable_node = nodes_to_visit.top ();
9661055 nodes_to_visit.pop ();
9671056 m_unreachableNodes->insert (current_unreachable_node);
968-
9691057 for (const auto &unreachable_child : current_unreachable_node->GetChildren ()) {
9701058 nodes_to_visit.push (unreachable_child.get ());
9711059 }
9721060 }
9731061 }
9741062 }
975- // End of the traversal of unreachable subtrees
9761063 }
9771064 else {
9781065 position.emplace (child->GetActions ().begin ());
9791066 }
980- prior_actions.at (child_player).emplace (nullptr );
9811067 }
9821068 }
9831069}
0 commit comments