@@ -732,50 +732,63 @@ bool GameTreeRep::IsConstSum() const
732732 }
733733}
734734
735- bool GameTreeRep::IsPerfectRecall (GameInfoset &s1, GameInfoset &s2 ) const
735+ bool GameTreeRep::IsPerfectRecall () const
736736{
737- for (auto player : m_players) {
738- for (size_t i = 1 ; i <= player->m_infosets .size (); i++) {
739- auto *iset1 = player->m_infosets [i - 1 ];
740- for (size_t j = 1 ; j <= player->m_infosets .size (); j++) {
741- auto *iset2 = player->m_infosets [j - 1 ];
742-
743- bool precedes = false ;
744- GameAction action = nullptr ;
745-
746- for (size_t m = 1 ; m <= iset2->m_members .size (); m++) {
747- size_t n;
748- for (n = 1 ; n <= iset1->m_members .size (); n++) {
749- if (iset2->GetMember (m)->IsSuccessorOf (iset1->GetMember (n)) &&
750- iset1->GetMember (n) != iset2->GetMember (m)) {
751- precedes = true ;
752- for (const auto &act : iset1->GetActions ()) {
753- if (iset2->GetMember (m)->IsSuccessorOf (iset1->GetMember (n)->GetChild (act))) {
754- if (action != nullptr && action != act) {
755- s1 = iset1;
756- s2 = iset2;
757- return false ;
758- }
759- action = act;
760- }
761- }
762- break ;
763- }
764- }
737+ using ChildIterator = GameNodeRep::Children::iterator;
738+ using ActionIterator = GameInfosetRep::Actions::iterator;
765739
766- if (i == j && precedes) {
767- s1 = iset1;
768- s2 = iset2;
769- return false ;
770- }
740+ std::map<GamePlayer, std::stack<GameAction>> prior_actions;
741+ std::map<GameInfoset, std::vector<GameAction>> infoset_parents;
742+ std::stack<std::tuple<GameNode, ChildIterator, ActionIterator>> position;
771743
772- if (n > iset1->m_members .size () && precedes) {
773- s1 = iset1;
774- s2 = iset2;
775- return false ;
776- }
777- }
744+ for (auto player : GetPlayers ()) {
745+ prior_actions[player].emplace (nullptr );
746+ }
747+ prior_actions[GetChance ()].emplace (nullptr );
748+ prior_actions[GetRoot ()->GetPlayer ()].emplace (nullptr );
749+
750+ if (GetRoot ()->IsTerminal ()) {
751+ return true ;
752+ }
753+
754+ position.emplace (GetRoot (), GetRoot ()->GetChildren ().begin (),
755+ GetRoot ()->GetInfoset ()->GetActions ().begin ());
756+
757+ infoset_parents[GetRoot ()->GetInfoset ()].emplace_back (nullptr );
758+
759+ while (!position.empty ()) {
760+ auto &[parent, child_it, action_it] = position.top ();
761+
762+ if (child_it != parent->GetChildren ().end ()) {
763+ const GameNode child = *child_it;
764+ const GameAction action = *action_it;
765+
766+ prior_actions[parent->GetPlayer ()].top () = action;
767+
768+ if (!child->IsTerminal ()) {
769+ infoset_parents[child->GetInfoset ()].push_back (prior_actions[child->GetPlayer ()].top ());
770+ position.emplace (child, child->GetChildren ().begin (),
771+ child->GetInfoset ()->GetActions ().begin ());
772+ prior_actions[child->GetPlayer ()].emplace (nullptr );
778773 }
774+ ++child_it;
775+ ++action_it;
776+ }
777+
778+ else {
779+ prior_actions[parent->GetPlayer ()].pop ();
780+ position.pop ();
781+ }
782+ }
783+
784+ for (const auto &[infoset, parent_action_options] : infoset_parents) {
785+ if (parent_action_options.size () == 1 ) {
786+ continue ;
787+ }
788+ const std::set<std::optional<GameAction>> unique_parents (parent_action_options.begin (),
789+ parent_action_options.end ());
790+ if (unique_parents.size () > 1 ) {
791+ return false ;
779792 }
780793 }
781794
0 commit comments