|
25 | 25 | #include <numeric> |
26 | 26 | #include <stack> |
27 | 27 | #include <set> |
| 28 | +#include <variant> |
28 | 29 |
|
29 | 30 | #include "gambit.h" |
30 | 31 | #include "gametree.h" |
@@ -736,48 +737,9 @@ bool GameTreeRep::IsConstSum() const |
736 | 737 |
|
737 | 738 | bool GameTreeRep::IsPerfectRecall() const |
738 | 739 | { |
739 | | - if (m_infosetParents.empty() && !GetRoot()->IsTerminal()) { |
740 | | - const_cast<GameTreeRep *>(this)->BuildInfosetParents(); |
741 | | - } |
742 | | - |
743 | | - // ==================================================================== |
744 | | - |
745 | | - std::cerr << "\n--- m_infosetParents ---\n"; |
746 | | - |
747 | 740 | if (m_infosetParents.empty() && !m_root->IsTerminal()) { |
748 | | - std::cerr << " (Cache is empty or game is trivial)\n"; |
749 | | - } |
750 | | - |
751 | | - // Iterate through the map to print its contents. |
752 | | - // Assumes m_infosetParents uses raw pointers as keys/values now. |
753 | | - for (const auto &[infoset_ptr, parent_actions_set] : m_infosetParents) { |
754 | | - // Print the information set identifier. |
755 | | - std::cerr << " - Infoset " << infoset_ptr->GetPlayer()->GetNumber() << "." |
756 | | - << infoset_ptr->GetNumber() << " (Player '" << infoset_ptr->GetPlayer()->GetLabel() |
757 | | - << "'):\n"; |
758 | | - |
759 | | - if (parent_actions_set.empty()) { |
760 | | - std::cerr << " - (No parent actions recorded)\n"; |
761 | | - } |
762 | | - |
763 | | - // Print each recorded parent action for this infoset. |
764 | | - for (const auto &action_ptr : parent_actions_set) { |
765 | | - if (action_ptr) { |
766 | | - // If the action is not null, print its label and the infoset it belongs to. |
767 | | - std::cerr << " - Reached via Action '" << action_ptr->GetLabel() << "' (from Infoset " |
768 | | - << action_ptr->GetInfoset()->GetPlayer()->GetNumber() << "." |
769 | | - << action_ptr->GetInfoset()->GetNumber() << ")\n"; |
770 | | - } |
771 | | - else { |
772 | | - // This case is for the root or for players who haven't acted yet on a path. |
773 | | - std::cerr << " - Reached via null action\n"; |
774 | | - } |
775 | | - } |
| 741 | + const_cast<GameTreeRep *>(this)->BuildInfosetParents(); |
776 | 742 | } |
777 | | - std::cerr << "---------------------------\n"; |
778 | | - // ==================================================================== |
779 | | - // DEBUGGING PRINTS END HERE |
780 | | - // ==================================================================== |
781 | 743 |
|
782 | 744 | if (GetRoot()->IsTerminal()) { |
783 | 745 | return true; |
@@ -901,76 +863,71 @@ std::vector<GameNodeRep *> GameTreeRep::BuildConsistentPlaysRecursiveImpl(GameNo |
901 | 863 |
|
902 | 864 | void GameTreeRep::BuildInfosetParents() |
903 | 865 | { |
904 | | - // The main traversal stack. It holds iterators that explore the children nodes. |
905 | | - // It does not contain entries for the nodes that are skipped over |
906 | | - // or where the previously taken decision is taken again due to absent-mindedness. |
907 | | - std::stack<GameNodeRep::Actions::iterator> position; |
908 | | - // tracks actions taken by each player on the current path |
909 | | - std::map<GamePlayer, std::stack<GameAction>> prior_actions; |
910 | | - // stores the first action choice made for an infoset on a given exploration path |
911 | | - std::map<GameInfoset, std::pair<GameNode, GameAction>> initial_choice; |
912 | | - |
913 | 866 | if (m_root->IsTerminal()) { |
914 | | - m_infosetParents[m_root->GetInfoset()].insert(nullptr); |
| 867 | + m_infosetParents[m_root->m_infoset].insert(nullptr); |
915 | 868 | return; |
916 | 869 | } |
917 | 870 |
|
918 | | - for (auto player : m_players) { |
919 | | - prior_actions[player].emplace(nullptr); |
| 871 | + using AbsentMindedEdge = std::pair<GameAction, GameNode>; |
| 872 | + using ActiveEdge = std::variant<GameNodeRep::Actions::iterator, AbsentMindedEdge>; |
| 873 | + std::stack<ActiveEdge> position; |
| 874 | + |
| 875 | + std::map<GamePlayer, std::stack<GameAction>> prior_actions; |
| 876 | + std::map<GameInfoset, GameAction> path_choices; |
| 877 | + |
| 878 | + for (auto player_rep : m_players) { |
| 879 | + prior_actions[GamePlayer(player_rep)].emplace(nullptr); |
920 | 880 | } |
921 | | - prior_actions[m_chance].emplace(nullptr); |
| 881 | + prior_actions[GamePlayer(m_chance)].emplace(nullptr); |
922 | 882 |
|
923 | 883 | position.emplace(m_root->GetActions().begin()); |
924 | | - prior_actions[m_root->GetPlayer()].emplace(nullptr); |
925 | | - m_infosetParents[m_root->GetInfoset()].insert(nullptr); |
| 884 | + prior_actions[m_root->m_infoset->m_player].emplace(nullptr); |
| 885 | + if (m_root->m_infoset) { |
| 886 | + m_infosetParents[m_root->m_infoset].insert(nullptr); |
| 887 | + } |
926 | 888 |
|
927 | 889 | while (!position.empty()) { |
928 | | - auto ¤t_it = position.top(); |
929 | | - auto parent = current_it.GetOwner(); |
930 | | - |
931 | | - if (current_it != parent->GetActions().end()) { |
932 | | - auto [action, child] = *current_it; |
933 | | - |
934 | | - prior_actions[parent->GetPlayer()].top() = action; |
935 | | - initial_choice[parent->GetInfoset()] = {parent, action}; |
936 | | - |
937 | | - // records every emplace made onto the prior_actions stack during a fast-forward. |
938 | | - std::vector<GamePlayer> fast_forward_history; |
939 | | - |
940 | | - // fast forward absent-minded child nodes |
941 | | - auto initial_choice_it = initial_choice.find(child->GetInfoset()); |
942 | | - while (initial_choice_it != initial_choice.end()) { |
943 | | - auto initial_action = initial_choice_it->second.second; |
944 | | - auto prior_action_ff = prior_actions[child->GetPlayer()].top(); |
945 | | - m_infosetParents[child->GetInfoset()].insert(prior_action_ff); |
| 890 | + ActiveEdge ¤t_edge = position.top(); |
| 891 | + GameNode child, node; |
| 892 | + GameAction action; |
| 893 | + |
| 894 | + if (std::holds_alternative<GameNodeRep::Actions::iterator>(current_edge)) { |
| 895 | + auto ¤t_it = std::get<GameNodeRep::Actions::iterator>(current_edge); |
| 896 | + node = current_it.GetOwner(); |
| 897 | + |
| 898 | + if (current_it == node->GetActions().end()) { |
| 899 | + prior_actions.at(node->m_infoset->m_player).pop(); |
| 900 | + position.pop(); |
| 901 | + path_choices.erase(node->m_infoset); |
| 902 | + continue; |
| 903 | + } |
| 904 | + else { |
| 905 | + std::tie(action, child) = *current_it; |
| 906 | + ++current_it; |
| 907 | + path_choices[node->m_infoset] = action; |
| 908 | + } |
| 909 | + } |
| 910 | + else { |
| 911 | + std::tie(action, node) = std::get<AbsentMindedEdge>(current_edge); |
| 912 | + position.pop(); |
| 913 | + child = node->GetChild(action); |
| 914 | + } |
946 | 915 |
|
947 | | - auto newchild = child->GetChild(initial_action); |
| 916 | + prior_actions.at(node->m_infoset->m_player).top() = action; |
948 | 917 |
|
949 | | - prior_actions[child->GetPlayer()].emplace(initial_action); |
950 | | - fast_forward_history.emplace_back(child->GetPlayer()); |
| 918 | + if (!child->IsTerminal()) { |
| 919 | + auto child_player = child->m_infoset->m_player; |
| 920 | + auto prior_action = prior_actions.at(child_player).top(); |
| 921 | + m_infosetParents[child->m_infoset].insert(prior_action); |
951 | 922 |
|
952 | | - child = newchild; |
953 | | - initial_choice_it = initial_choice.find(newchild->GetInfoset()); |
| 923 | + if (path_choices.find(child->m_infoset) != path_choices.end()) { |
| 924 | + const GameAction replay_action = path_choices.at(child->m_infoset); |
| 925 | + position.emplace(AbsentMindedEdge{replay_action, child}); |
954 | 926 | } |
955 | | - |
956 | | - if (!child->IsTerminal()) { |
957 | | - auto child_player = child->GetPlayer(); |
958 | | - auto prior_action_desc = prior_actions[child_player].top(); |
959 | | - m_infosetParents[child->GetInfoset()].insert(prior_action_desc); |
| 927 | + else { |
960 | 928 | position.emplace(child->GetActions().begin()); |
961 | | - prior_actions[child_player].emplace(nullptr); |
962 | | - } |
963 | | - |
964 | | - ++current_it; |
965 | | - |
966 | | - for (auto it = fast_forward_history.rbegin(); it != fast_forward_history.rend(); ++it) { |
967 | | - prior_actions.at(*it).pop(); |
968 | 929 | } |
969 | | - } |
970 | | - else { |
971 | | - prior_actions.at(parent->GetPlayer()).pop(); |
972 | | - position.pop(); |
973 | | - initial_choice.erase(parent->GetInfoset()); |
| 930 | + prior_actions.at(child_player).emplace(nullptr); |
974 | 931 | } |
975 | 932 | } |
976 | 933 | } |
|
0 commit comments