diff --git a/src/games/behavpure.cc b/src/games/behavpure.cc index e023785e0..f62af5fd1 100644 --- a/src/games/behavpure.cc +++ b/src/games/behavpure.cc @@ -98,30 +98,16 @@ MixedBehaviorProfile PureBehaviorProfile::ToMixedBehaviorProfile() con // class BehaviorContingencies //======================================================================== -BehaviorContingencies::BehaviorContingencies(const BehaviorSupportProfile &p_support, - const std::set &p_reachable, - const std::vector &p_frozen) - : m_support(p_support), m_frozen(p_frozen) +BehaviorContingencies::BehaviorContingencies(const BehaviorSupportProfile &p_support) + : m_support(p_support) { - if (!p_reachable.empty()) { - for (const auto &infoset : p_reachable) { - m_activeInfosets.push_back(infoset); - } - } - else { - for (const auto &player : m_support.GetGame()->GetPlayers()) { - for (const auto &infoset : player->GetInfosets()) { - if (p_support.IsReachable(infoset)) { - m_activeInfosets.push_back(infoset); - } + for (const auto &player : m_support.GetGame()->GetPlayers()) { + for (const auto &infoset : player->GetInfosets()) { + if (p_support.IsReachable(infoset)) { + m_reachableInfosets.push_back(infoset); } } } - for (const auto &action : m_frozen) { - m_activeInfosets.erase(std::find_if( - m_activeInfosets.begin(), m_activeInfosets.end(), - [action](const GameInfoset &infoset) { return infoset == action->GetInfoset(); })); - } } BehaviorContingencies::iterator::iterator(BehaviorContingencies *p_cont, bool p_end) @@ -136,15 +122,12 @@ BehaviorContingencies::iterator::iterator(BehaviorContingencies *p_cont, bool p_ m_profile.SetAction(*m_currentBehav[infoset]); } } - for (const auto &action : m_cont->m_frozen) { - m_profile.SetAction(action); - } } BehaviorContingencies::iterator &BehaviorContingencies::iterator::operator++() { - for (auto infoset = m_cont->m_activeInfosets.crbegin(); - infoset != m_cont->m_activeInfosets.crend(); ++infoset) { + for (auto infoset = m_cont->m_reachableInfosets.crbegin(); + infoset != m_cont->m_reachableInfosets.crend(); ++infoset) { ++m_currentBehav[*infoset]; if (m_currentBehav.at(*infoset) != m_cont->m_support.GetActions(*infoset).end()) { m_profile.SetAction(*m_currentBehav[*infoset]); diff --git a/src/games/behavpure.h b/src/games/behavpure.h index 885517004..00db37469 100644 --- a/src/games/behavpure.h +++ b/src/games/behavpure.h @@ -32,7 +32,6 @@ namespace Gambit { /// It specifies exactly one strategy for each information set in the /// game. class PureBehaviorProfile { -private: Game m_efg; std::map m_profile; @@ -86,14 +85,11 @@ template <> inline std::string PureBehaviorProfile::GetPayoff(const GamePlayer & } class BehaviorContingencies { -private: BehaviorSupportProfile m_support; - std::vector m_frozen; - std::list m_activeInfosets; + std::list m_reachableInfosets; public: class iterator { - private: BehaviorContingencies *m_cont; bool m_atEnd; std::map m_currentBehav; @@ -127,10 +123,8 @@ class BehaviorContingencies { }; /// @name Lifecycle //@{ - /// Construct a new iterator on the support, holding the listed actions fixed - explicit BehaviorContingencies(const BehaviorSupportProfile &, - const std::set &p_active = {}, - const std::vector &p_frozen = {}); + /// Construct a new iterator on the support + explicit BehaviorContingencies(const BehaviorSupportProfile &); //@} iterator begin() { return {this, false}; } iterator end() { return {this, true}; } diff --git a/src/games/behavspt.cc b/src/games/behavspt.cc index be017148e..81cd29c9f 100644 --- a/src/games/behavspt.cc +++ b/src/games/behavspt.cc @@ -106,106 +106,6 @@ std::list BehaviorSupportProfile::GetInfosets(const GamePlayer &p_p return answer; } -namespace { - -void ReachableInfosets(const BehaviorSupportProfile &p_support, const GameNode &p_node, - std::set &p_reached) -{ - if (p_node->IsTerminal()) { - return; - } - - const GameInfoset infoset = p_node->GetInfoset(); - if (!infoset->GetPlayer()->IsChance()) { - p_reached.insert(infoset); - for (const auto &action : p_support.GetActions(infoset)) { - ReachableInfosets(p_support, p_node->GetChild(action), p_reached); - } - } - else { - for (const auto &child : p_node->GetChildren()) { - ReachableInfosets(p_support, child, p_reached); - } - } -} - -} // end anonymous namespace - -bool BehaviorSupportProfile::Dominates(const GameAction &a, const GameAction &b, - bool p_strict) const -{ - const GameInfoset infoset = a->GetInfoset(); - if (infoset != b->GetInfoset()) { - throw UndefinedException(); - } - - GamePlayer player = infoset->GetPlayer(); - int thesign = 0; - - auto nodelist = GetMembers(infoset); - for (const auto &node : GetMembers(infoset)) { - std::set reachable; - ReachableInfosets(*this, node->GetChild(a), reachable); - ReachableInfosets(*this, node->GetChild(b), reachable); - - auto contingencies = BehaviorContingencies(*this, reachable); - if (p_strict) { - if (!std::all_of(contingencies.begin(), contingencies.end(), - [&](const PureBehaviorProfile &profile) { - return profile.GetPayoff(node->GetChild(a), player) > - profile.GetPayoff(node->GetChild(b), player); - })) { - return false; - } - } - else { - for (const auto &iter : contingencies) { - auto newsign = sign(iter.GetPayoff(node->GetChild(a), player) - - iter.GetPayoff(node->GetChild(b), player)); - if (newsign < 0) { - return false; - } - thesign = std::max(thesign, newsign); - } - } - } - return p_strict || thesign > 0; -} - -bool BehaviorSupportProfile::IsDominated(const GameAction &p_action, const bool p_strict) const -{ - const auto &actions = GetActions(p_action->GetInfoset()); - return std::any_of(actions.begin(), actions.end(), [&](const GameAction &a) { - return a != p_action && Dominates(a, p_action, p_strict); - }); -} - -BehaviorSupportProfile BehaviorSupportProfile::Undominated(const bool p_strict) const -{ - BehaviorSupportProfile result(*this); - for (const auto &player : m_efg->GetPlayers()) { - for (const auto &infoset : player->GetInfosets()) { - const auto &actions = GetActions(infoset); - std::set dominated; - for (const auto &action1 : actions) { - if (contains(dominated, action1)) { - continue; - } - for (const auto &action2 : actions) { - if (action1 == action2 || contains(dominated, action2)) { - continue; - } - if (Dominates(action1, action2, p_strict)) { - dominated.insert(action2); - result.RemoveAction(action2); - } - } - } - } - } - return result; -} - bool BehaviorSupportProfile::HasReachableMembers(const GameInfoset &p_infoset) const { const auto &members = p_infoset->GetMembers(); diff --git a/src/games/layout.cc b/src/games/layout.cc index 74910f610..297ffbc46 100644 --- a/src/games/layout.cc +++ b/src/games/layout.cc @@ -24,8 +24,7 @@ namespace Gambit { -void Layout::LayoutSubtree(const GameNode &p_node, const BehaviorSupportProfile &p_support, - int p_level, double &p_offset) +void Layout::LayoutSubtree(const GameNode &p_node, int p_level, double &p_offset) { const auto entry = std::make_shared(p_level); m_nodeMap[p_node] = entry; @@ -46,9 +45,9 @@ void Layout::LayoutSubtree(const GameNode &p_node, const BehaviorSupportProfile return; } if (p_node->GetInfoset() && !p_node->GetInfoset()->GetPlayer()->IsChance()) { - const auto actions = p_support.GetActions(p_node->GetInfoset()); - for (const auto &action : actions) { - LayoutSubtree(p_node->GetChild(action), p_support, p_level + 1, p_offset); + const auto actions = p_node->GetInfoset()->GetActions(); + for (const auto &action : p_node->GetInfoset()->GetActions()) { + LayoutSubtree(p_node->GetChild(action), p_level + 1, p_offset); } entry->m_offset = (m_nodeMap.at(p_node->GetChild(actions.front()))->m_offset + m_nodeMap.at(p_node->GetChild(actions.back()))->m_offset) / @@ -56,7 +55,7 @@ void Layout::LayoutSubtree(const GameNode &p_node, const BehaviorSupportProfile } else { for (const auto &child : p_node->GetChildren()) { - LayoutSubtree(child, p_support, p_level + 1, p_offset); + LayoutSubtree(child, p_level + 1, p_offset); } entry->m_offset = (m_nodeMap.at(p_node->GetChildren().front())->m_offset + m_nodeMap.at(p_node->GetChildren().back())->m_offset) / @@ -64,14 +63,14 @@ void Layout::LayoutSubtree(const GameNode &p_node, const BehaviorSupportProfile } } -void Layout::LayoutTree(const BehaviorSupportProfile &p_support) +void Layout::LayoutTree(const Game &p_game) { m_nodeMap.clear(); m_numSublevels.clear(); m_infosetSublevels.clear(); m_maxOffset = 0; - LayoutSubtree(m_game->GetRoot(), p_support, 0, m_maxOffset); + LayoutSubtree(p_game->GetRoot(), 0, m_maxOffset); } } // namespace Gambit diff --git a/src/games/layout.h b/src/games/layout.h index 9fa4ad53f..a699d7d27 100644 --- a/src/games/layout.h +++ b/src/games/layout.h @@ -47,13 +47,13 @@ class Layout { double m_maxOffset{0}; - void LayoutSubtree(const GameNode &, const BehaviorSupportProfile &, int, double &); + void LayoutSubtree(const GameNode &, int, double &); public: explicit Layout(const Game &p_game) : m_game(p_game) {} ~Layout() = default; - void LayoutTree(const BehaviorSupportProfile &); + void LayoutTree(const Game &); const std::map> &GetNodeMap() const { return m_nodeMap; } int GetNodeLevel(const GameNode &p_node) const { return m_nodeMap.at(p_node)->m_level; } @@ -68,7 +68,7 @@ class Layout { inline std::shared_ptr CreateLayout(const Game &p_game) { auto layout = std::make_shared(p_game); - layout->LayoutTree(BehaviorSupportProfile(p_game)); + layout->LayoutTree(p_game); return layout; } diff --git a/src/gui/efgdisplay.cc b/src/gui/efgdisplay.cc index 5894affff..46e1673e6 100644 --- a/src/gui/efgdisplay.cc +++ b/src/gui/efgdisplay.cc @@ -592,8 +592,7 @@ void EfgDisplay::OnUpdate() void EfgDisplay::RefreshTree() { - m_layout.BuildNodeList(m_doc->GetEfgSupport()); - m_layout.Layout(m_doc->GetEfgSupport()); + m_layout.Layout(m_doc->GetGame()); Refresh(); } diff --git a/src/gui/efglayout.cc b/src/gui/efglayout.cc index 50604ad70..d0d3c570b 100644 --- a/src/gui/efglayout.cc +++ b/src/gui/efglayout.cc @@ -608,53 +608,37 @@ void TreeLayout::ComputeRenderedParents() const } } -void TreeLayout::BuildNodeList(const GameNode &p_node, const BehaviorSupportProfile &p_support) +void TreeLayout::BuildNodeList(const GameNode &p_node) { const auto entry = std::make_shared(p_node); m_nodeList.push_back(entry); m_nodeMap[p_node] = entry; entry->m_size = m_doc->GetStyle().GetNodeSize(); entry->m_branchLength = m_doc->GetStyle().GetBranchLength(); - if (m_doc->GetStyle().RootReachable()) { - if (const GameInfoset infoset = p_node->GetInfoset()) { - if (infoset->GetPlayer()->IsChance()) { - for (const auto &child : p_node->GetChildren()) { - BuildNodeList(child, p_support); - } - } - else { - for (const auto &action : p_support.GetActions(infoset)) { - BuildNodeList(p_node->GetChild(action), p_support); - } - } - } - } - else { - for (const auto &child : p_node->GetChildren()) { - BuildNodeList(child, p_support); - } + for (const auto &child : p_node->GetChildren()) { + BuildNodeList(child); } } -void TreeLayout::BuildNodeList(const BehaviorSupportProfile &p_support) +void TreeLayout::BuildNodeList(const Game &p_game) { m_nodeList.clear(); m_nodeMap.clear(); - BuildNodeList(m_doc->GetGame()->GetRoot(), p_support); + BuildNodeList(p_game->GetRoot()); } -void TreeLayout::Layout(const BehaviorSupportProfile &p_support) +void TreeLayout::Layout(const Game &p_game) { m_infosetSpacing = (m_doc->GetStyle().GetInfosetJoin() == GBT_INFOSET_JOIN_LINES) ? 10 : 40; if (m_nodeList.size() != m_doc->GetGame()->NumNodes()) { // We only rebuild the node list if the number of nodes changes. If we only have // information set changes this can be handled just by the traversal below - BuildNodeList(p_support); + BuildNodeList(p_game); } auto layout = Gambit::Layout(m_doc->GetGame()); - layout.LayoutTree(p_support); + layout.LayoutTree(p_game); const auto spacing = m_doc->GetStyle().GetTerminalSpacing(); for (auto [node, entry] : layout.GetNodeMap()) { diff --git a/src/gui/efglayout.h b/src/gui/efglayout.h index 823482365..6f9ae0d0d 100644 --- a/src/gui/efglayout.h +++ b/src/gui/efglayout.h @@ -114,7 +114,7 @@ class TreeLayout final : public GameView { std::shared_ptr ComputeNextInInfoset(const std::shared_ptr &) const; - void BuildNodeList(const GameNode &, const BehaviorSupportProfile &); + void BuildNodeList(const GameNode &); /// Based on node levels and information set sublevels, compute the depth /// (X coordinate) of all nodes @@ -135,6 +135,7 @@ class TreeLayout final : public GameView { void DrawOutcome(wxDC &, const std::shared_ptr &, bool p_noHints) const; bool NodeHitTest(const std::shared_ptr &p_entry, int p_x, int p_y) const; + void BuildNodeList(const Game &); public: explicit TreeLayout(GameDocument *p_doc) : GameView(p_doc) {} @@ -143,8 +144,7 @@ class TreeLayout final : public GameView { GameNode PriorSameLevel(const GameNode &) const; GameNode NextSameLevel(const GameNode &) const; - void BuildNodeList(const BehaviorSupportProfile &); - void Layout(const BehaviorSupportProfile &); + void Layout(const Game &); void GenerateLabels() const; std::shared_ptr GetNodeEntry(const GameNode &p_node) const diff --git a/src/gui/efgpanel.cc b/src/gui/efgpanel.cc index 81e8dc5ba..2ae4bd00f 100644 --- a/src/gui/efgpanel.cc +++ b/src/gui/efgpanel.cc @@ -36,141 +36,6 @@ #include "edittext.h" namespace Gambit::GUI { -//===================================================================== -// class gbtBehavDominanceToolbar -//===================================================================== - -//! -//! This panel serves as a toolbar for interactively viewing -//! dominance information on extensive forms. -//! -class gbtBehavDominanceToolbar : public wxPanel, public GameView { -private: - wxButton *m_topButton, *m_prevButton, *m_nextButton, *m_allButton; - wxStaticText *m_level; - - // Overriding GameView members - void OnUpdate() override; - - // Event handlers - void OnStrength(wxCommandEvent &); - void OnTopLevel(wxCommandEvent &); - void OnPreviousLevel(wxCommandEvent &); - void OnNextLevel(wxCommandEvent &); - void OnLastLevel(wxCommandEvent &); - void OnShowReachable(wxCommandEvent &); - -public: - gbtBehavDominanceToolbar(wxWindow *p_parent, GameDocument *p_doc); - ~gbtBehavDominanceToolbar() override = default; -}; - -#include "bitmaps/next.xpm" -#include "bitmaps/prev.xpm" -#include "bitmaps/tobegin.xpm" -#include "bitmaps/toend.xpm" - -gbtBehavDominanceToolbar::gbtBehavDominanceToolbar(wxWindow *p_parent, GameDocument *p_doc) - : wxPanel(p_parent, wxID_ANY), GameView(p_doc) -{ - auto *topSizer = new wxBoxSizer(wxHORIZONTAL); - - topSizer->Add(new wxStaticText(this, wxID_STATIC, wxT("Hide actions which are ")), 0, - wxALL | wxALIGN_CENTER, 5); - - wxString domChoices[] = {wxT("strictly"), wxT("strictly or weakly")}; - auto *choice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 2, domChoices); - choice->SetSelection(0); - Connect(choice->GetId(), wxEVT_COMMAND_CHOICE_SELECTED, - wxCommandEventHandler(gbtBehavDominanceToolbar::OnStrength)); - topSizer->Add(choice, 0, wxALL | wxALIGN_CENTER, 5); - - topSizer->Add(new wxStaticText(this, wxID_STATIC, wxT("dominated:")), 0, wxALL | wxALIGN_CENTER, - 5); - - m_topButton = new wxBitmapButton(this, wxID_ANY, wxBitmap(tobegin_xpm)); - m_topButton->SetToolTip(_("Show all strategies")); - Connect(m_topButton->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, - wxCommandEventHandler(gbtBehavDominanceToolbar::OnTopLevel)); - topSizer->Add(m_topButton, 0, wxALL | wxALIGN_CENTER, 5); - - m_prevButton = new wxBitmapButton(this, wxID_ANY, wxBitmap(prev_xpm)); - m_prevButton->SetToolTip(_("Previous round of elimination")); - Connect(m_prevButton->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, - wxCommandEventHandler(gbtBehavDominanceToolbar::OnPreviousLevel)); - topSizer->Add(m_prevButton, 0, wxALL | wxALIGN_CENTER, 5); - - m_level = new wxStaticText(this, wxID_STATIC, wxT("All actions shown"), wxDefaultPosition, - wxDefaultSize, wxALIGN_CENTER | wxST_NO_AUTORESIZE); - topSizer->Add(m_level, 0, wxALL | wxALIGN_CENTER, 5); - - m_nextButton = new wxBitmapButton(this, wxID_ANY, wxBitmap(next_xpm)); - m_nextButton->SetToolTip(_("Next round of elimination")); - Connect(m_nextButton->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, - wxCommandEventHandler(gbtBehavDominanceToolbar::OnNextLevel)); - topSizer->Add(m_nextButton, 0, wxALL | wxALIGN_CENTER, 5); - - m_allButton = new wxBitmapButton(this, wxID_ANY, wxBitmap(toend_xpm)); - m_allButton->SetToolTip(_("Eliminate iteratively")); - Connect(m_allButton->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, - wxCommandEventHandler(gbtBehavDominanceToolbar::OnLastLevel)); - topSizer->Add(m_allButton, 0, wxALL | wxALIGN_CENTER, 5); - - auto *showReachable = new wxCheckBox(this, wxID_ANY, wxT("Show only reachable nodes")); - showReachable->SetValue(m_doc->GetStyle().RootReachable()); - Connect(showReachable->GetId(), wxEVT_COMMAND_CHECKBOX_CLICKED, - wxCommandEventHandler(gbtBehavDominanceToolbar::OnShowReachable)); - topSizer->Add(showReachable, 0, wxALL | wxALIGN_CENTER, 5); - - SetSizer(topSizer); - wxWindowBase::Layout(); -} - -void gbtBehavDominanceToolbar::OnStrength(wxCommandEvent &p_event) -{ - m_doc->SetBehavElimStrength(p_event.GetSelection() == 0); -} - -void gbtBehavDominanceToolbar::OnTopLevel(wxCommandEvent &) { m_doc->TopBehavElimLevel(); } - -void gbtBehavDominanceToolbar::OnPreviousLevel(wxCommandEvent &) -{ - m_doc->PreviousBehavElimLevel(); -} - -void gbtBehavDominanceToolbar::OnNextLevel(wxCommandEvent &) { m_doc->NextBehavElimLevel(); } - -void gbtBehavDominanceToolbar::OnLastLevel(wxCommandEvent &) -{ - while (m_doc->NextBehavElimLevel()) - ; -} - -void gbtBehavDominanceToolbar::OnShowReachable(wxCommandEvent &) -{ - TreeRenderConfig style = m_doc->GetStyle(); - style.SetRootReachable(!style.RootReachable()); - m_doc->SetStyle(style); -} - -void gbtBehavDominanceToolbar::OnUpdate() -{ - m_topButton->Enable(m_doc->GetBehavElimLevel() > 1); - m_prevButton->Enable(m_doc->GetBehavElimLevel() > 1); - m_nextButton->Enable(m_doc->CanBehavElim()); - m_allButton->Enable(m_doc->CanBehavElim()); - if (m_doc->GetBehavElimLevel() == 1) { - m_level->SetLabel(wxT("All actions shown")); - } - else if (m_doc->GetBehavElimLevel() == 2) { - m_level->SetLabel(wxT("Eliminated 1 level")); - } - else { - m_level->SetLabel( - wxString::Format(wxT("Eliminated %d levels"), m_doc->GetBehavElimLevel() - 1)); - } - GetSizer()->Layout(); -} #include "bitmaps/color.xpm" #include "bitmaps/person.xpm" @@ -610,7 +475,6 @@ void gbtTreePlayerToolbar::PostPendingChanges() //===================================================================== BEGIN_EVENT_TABLE(EfgPanel, wxPanel) -EVT_MENU(GBT_MENU_TOOLS_DOMINANCE, EfgPanel::OnToolsDominance) EVT_MENU(GBT_MENU_VIEW_ZOOMIN, EfgPanel::OnViewZoomIn) EVT_MENU(GBT_MENU_VIEW_ZOOMOUT, EfgPanel::OnViewZoomOut) EVT_MENU(GBT_MENU_VIEW_ZOOM100, EfgPanel::OnViewZoom100) @@ -619,12 +483,9 @@ END_EVENT_TABLE() EfgPanel::EfgPanel(wxWindow *p_parent, GameDocument *p_doc) : wxPanel(p_parent, wxID_ANY), GameView(p_doc), m_treeWindow(new EfgDisplay(this, m_doc)), - m_dominanceToolbar(new gbtBehavDominanceToolbar(this, m_doc)), m_playerToolbar(new gbtTreePlayerToolbar(this, m_doc)) { auto *topSizer = new wxBoxSizer(wxVERTICAL); - topSizer->Add(m_dominanceToolbar, 0, wxEXPAND, 0); - topSizer->Show(m_dominanceToolbar, false); auto *treeSizer = new wxBoxSizer(wxHORIZONTAL); treeSizer->Add(m_playerToolbar, 0, wxEXPAND, 0); @@ -635,12 +496,6 @@ EfgPanel::EfgPanel(wxWindow *p_parent, GameDocument *p_doc) wxWindowBase::Layout(); } -void EfgPanel::OnToolsDominance(wxCommandEvent &p_event) -{ - GetSizer()->Show(m_dominanceToolbar, p_event.IsChecked(), true); - GetSizer()->Layout(); -} - void EfgPanel::OnViewZoomIn(wxCommandEvent &) { int zoom = m_treeWindow->GetZoom(); diff --git a/src/gui/efgpanel.h b/src/gui/efgpanel.h index b990e0590..8bcbfcb80 100644 --- a/src/gui/efgpanel.h +++ b/src/gui/efgpanel.h @@ -32,14 +32,13 @@ class EfgDisplay; class EfgPanel final : public wxPanel, public GameView { EfgDisplay *m_treeWindow; - wxWindow *m_dominanceToolbar, *m_playerToolbar; + wxWindow *m_playerToolbar; // Overriding GameView members void OnUpdate() override {} /// @name Command event handlers //@{ - void OnToolsDominance(wxCommandEvent &); void OnViewZoomIn(wxCommandEvent &); void OnViewZoomOut(wxCommandEvent &); void OnViewZoom100(wxCommandEvent &); diff --git a/src/gui/gamedoc.cc b/src/gui/gamedoc.cc index 49d51662c..6f9273ae6 100644 --- a/src/gui/gamedoc.cc +++ b/src/gui/gamedoc.cc @@ -33,68 +33,6 @@ #include "gamedoc.h" namespace Gambit::GUI { -//========================================================================= -// class BehaviorDominanceStack -//========================================================================= - -BehaviorDominanceStack::BehaviorDominanceStack(GameDocument *p_doc, bool p_strict) - : m_doc(p_doc), m_strict(p_strict), m_noFurther(false) -{ - Reset(); -} - -void BehaviorDominanceStack::SetStrict(bool p_strict) -{ - if (m_strict != p_strict) { - Reset(); - } - m_strict = p_strict; -} - -void BehaviorDominanceStack::Reset() -{ - m_supports.clear(); - if (m_doc->IsTree()) { - m_supports.push_back(std::make_shared(m_doc->GetGame())); - m_current = 1; - } - m_noFurther = false; -} - -bool BehaviorDominanceStack::NextLevel() -{ - if (m_current < m_supports.size()) { - m_current++; - return true; - } - - if (m_noFurther) { - return false; - } - - const BehaviorSupportProfile newSupport = m_supports[m_current]->Undominated(m_strict); - - if (newSupport != *m_supports[m_current]) { - m_supports.push_back(std::make_shared(newSupport)); - m_current++; - return true; - } - else { - m_noFurther = true; - return false; - } -} - -bool BehaviorDominanceStack::PreviousLevel() -{ - if (m_current > 1) { - m_current--; - return true; - } - else { - return false; - } -} //========================================================================= // class StrategyDominanceStack @@ -162,8 +100,8 @@ bool StrategyDominanceStack::PreviousLevel() //========================================================================= GameDocument::GameDocument(Game p_game) - : m_game(p_game), m_selectNode(nullptr), m_modified(false), m_behavSupports(this, true), - m_stratSupports(this, true), m_currentProfileList(0) + : m_game(p_game), m_selectNode(nullptr), m_modified(false), m_stratSupports(this, true), + m_currentProfileList(0) { wxGetApp().AddDocument(this); @@ -221,7 +159,6 @@ bool GameDocument::LoadDocument(const wxString &p_filename, bool p_saveUndo) return false; } - m_behavSupports.Reset(); m_stratSupports.Reset(); m_profiles.clear(); @@ -335,7 +272,6 @@ void GameDocument::UpdateViews(GameModificationType p_modifications) } if (p_modifications == GBT_DOC_MODIFIED_GAME || p_modifications == GBT_DOC_MODIFIED_PAYOFFS) { - m_behavSupports.Reset(); m_stratSupports.Reset(); // Even though modifications only to payoffs doesn't make the @@ -440,35 +376,6 @@ void GameDocument::AddProfile(const MixedStrategyProfile &p_profile) } */ -void GameDocument::SetBehavElimStrength(bool p_strict) -{ - m_behavSupports.SetStrict(p_strict); - UpdateViews(GBT_DOC_MODIFIED_VIEWS); -} - -bool GameDocument::NextBehavElimLevel() -{ - const bool ret = m_behavSupports.NextLevel(); - UpdateViews(GBT_DOC_MODIFIED_VIEWS); - return ret; -} - -void GameDocument::PreviousBehavElimLevel() -{ - m_behavSupports.PreviousLevel(); - UpdateViews(GBT_DOC_MODIFIED_VIEWS); -} - -void GameDocument::TopBehavElimLevel() -{ - m_behavSupports.TopLevel(); - UpdateViews(GBT_DOC_MODIFIED_VIEWS); -} - -bool GameDocument::CanBehavElim() const { return m_behavSupports.CanEliminate(); } - -int GameDocument::GetBehavElimLevel() const { return m_behavSupports.GetLevel(); } - void GameDocument::SetStrategyElimStrength(bool p_strict) { m_stratSupports.SetStrict(p_strict); diff --git a/src/gui/gamedoc.h b/src/gui/gamedoc.h index 2864a3ebc..0701d6dcf 100644 --- a/src/gui/gamedoc.h +++ b/src/gui/gamedoc.h @@ -32,76 +32,6 @@ namespace Gambit::GUI { class GameView; class GameDocument; -//! -//! This class manages the "stack" of supports obtained by eliminating -//! dominated actions from consideration. -//! -class BehaviorDominanceStack { - GameDocument *m_doc; - bool m_strict; - Array> m_supports; - size_t m_current{0}; - bool m_noFurther; - -public: - BehaviorDominanceStack(GameDocument *p_doc, bool p_strict); - ~BehaviorDominanceStack() = default; - - //! - //! Returns the number of supports in the stack - //! - int NumSupports() const { return m_supports.size(); } - - //! - //! Get the i'th support in the stack - //! (where i=1 is always the "full" support) - //! - const BehaviorSupportProfile &GetSupport(int i) const { return *m_supports[i]; } - - //! - //! Get the current support - //! - const BehaviorSupportProfile &GetCurrent() const { return *m_supports[m_current]; } - - //! - //! Get the level of iteration (1 = no iteration) - //! - int GetLevel() const { return m_current; } - - //! - //! Sets whether elimination is strict or weak. If this changes the - //! internal setting, a Reset() is done - //! - void SetStrict(bool p_strict); - - //! - //! Reset the stack by clearing out all supports - //! - void Reset(); - - //! - //! Go to the next level of iteration. Returns 'true' if successful, - //! 'false' if no effect (i.e., no further actions could be eliminated) - //! - bool NextLevel(); - - //! - //! Go to the previous level of iteration. Returns 'true' if successful, - //! 'false' if no effect (i.e., already at the full support) - //! - bool PreviousLevel(); - - //! - //! Go to the top level (the full support) - //! - void TopLevel() { m_current = 1; } - - //! - //! Returns 'false' if it is known that no further eliminations can be done - //! - bool CanEliminate() const { return (m_current < m_supports.size() || !m_noFurther); } -}; - //! //! This class manages the "stack" of supports obtained by eliminating //! dominated strategies from consideration. @@ -226,7 +156,6 @@ class GameDocument { GameNode m_selectNode; bool m_modified; - BehaviorDominanceStack m_behavSupports; StrategyDominanceStack m_stratSupports; Array> m_profiles; @@ -293,13 +222,7 @@ class GameDocument { //! @name Handling of behavior supports //! //@{ - const BehaviorSupportProfile &GetEfgSupport() const { return m_behavSupports.GetCurrent(); } - void SetBehavElimStrength(bool p_strict); - bool NextBehavElimLevel(); - void PreviousBehavElimLevel(); - void TopBehavElimLevel(); - bool CanBehavElim() const; - int GetBehavElimLevel() const; + BehaviorSupportProfile GetEfgSupport() const { return BehaviorSupportProfile(m_game); } //@} //! diff --git a/src/gui/gameframe.cc b/src/gui/gameframe.cc index 20702510d..f6d356416 100644 --- a/src/gui/gameframe.cc +++ b/src/gui/gameframe.cc @@ -264,7 +264,7 @@ GameFrame::GameFrame(wxWindow *p_parent, GameDocument *p_doc) } else { m_efgPanel = nullptr; - m_nfgPanel = new NfgPanel(m_splitter, p_doc); + m_nfgPanel = new NfgPanel(m_splitter, p_doc, false); m_nfgPanel->Show(true); m_splitter->Initialize(m_nfgPanel); } @@ -488,6 +488,10 @@ void GameFrame::MakeMenus() viewMenu->Append(GBT_MENU_VIEW_STRATEGIC, _("&Strategic game"), wxT("Display the reduced strategic representation ") wxT("of the game"), true); + if (!m_doc->GetGame()->IsTree()) { + viewMenu->Check(GBT_MENU_VIEW_STRATEGIC, true); + viewMenu->Enable(GBT_MENU_VIEW_STRATEGIC, false); + } auto *formatMenu = new wxMenu; AppendBitmapItem(formatMenu, GBT_MENU_FORMAT_LAYOUT, _("&Layout"), @@ -500,6 +504,9 @@ void GameFrame::MakeMenus() auto *toolsMenu = new wxMenu; toolsMenu->Append(GBT_MENU_TOOLS_DOMINANCE, _("&Dominance"), _("Find undominated actions"), true); + if (m_doc->GetGame()->IsTree()) { + toolsMenu->Enable(GBT_MENU_TOOLS_DOMINANCE, false); + } AppendBitmapItem(toolsMenu, GBT_MENU_TOOLS_EQUILIBRIUM, _("&Equilibrium"), _("Compute Nash equilibria and refinements"), wxBitmap(calc_xpm)); @@ -1117,7 +1124,8 @@ void GameFrame::OnViewStrategic(wxCommandEvent &p_event) } if (!m_nfgPanel) { - m_nfgPanel = new NfgPanel(m_splitter, m_doc); + m_nfgPanel = + new NfgPanel(m_splitter, m_doc, GetMenuBar()->IsChecked(GBT_MENU_TOOLS_DOMINANCE)); } m_doc->BuildNfg(); @@ -1143,6 +1151,7 @@ void GameFrame::OnViewStrategic(wxCommandEvent &p_event) GetMenuBar()->Check(GBT_MENU_VIEW_STRATEGIC, m_nfgPanel->IsShown()); GetMenuBar()->Enable(GBT_MENU_VIEW_ZOOMIN, !p_event.IsChecked()); GetMenuBar()->Enable(GBT_MENU_VIEW_ZOOMOUT, !p_event.IsChecked()); + GetMenuBar()->Enable(GBT_MENU_TOOLS_DOMINANCE, m_nfgPanel->IsShown()); GetToolBar()->ToggleTool(GBT_MENU_VIEW_STRATEGIC, p_event.IsChecked()); GetToolBar()->EnableTool(GBT_MENU_VIEW_ZOOMIN, !p_event.IsChecked()); @@ -1212,14 +1221,10 @@ void GameFrame::OnFormatDecimalsDelete(wxCommandEvent &) void GameFrame::OnToolsDominance(wxCommandEvent &p_event) { - if (m_efgPanel) { - wxPostEvent(m_efgPanel, p_event); - } if (m_nfgPanel) { wxPostEvent(m_nfgPanel, p_event); } if (!p_event.IsChecked()) { - m_doc->TopBehavElimLevel(); m_doc->TopStrategyElimLevel(); } } diff --git a/src/gui/nfgpanel.cc b/src/gui/nfgpanel.cc index e49d75eea..f42f58769 100644 --- a/src/gui/nfgpanel.cc +++ b/src/gui/nfgpanel.cc @@ -296,10 +296,10 @@ void TablePlayerToolbar::PostPendingChanges() } //===================================================================== -// class gbtStrategyDominanceToolbar +// class StrategyDominanceToolbar //===================================================================== -class gbtStrategyDominanceToolbar : public wxPanel, public GameView { +class StrategyDominanceToolbar final : public wxPanel, public GameView { private: wxButton *m_topButton, *m_prevButton, *m_nextButton, *m_allButton; wxStaticText *m_level; @@ -315,8 +315,8 @@ class gbtStrategyDominanceToolbar : public wxPanel, public GameView { void OnLastLevel(wxCommandEvent &); public: - gbtStrategyDominanceToolbar(wxWindow *p_parent, GameDocument *p_doc); - ~gbtStrategyDominanceToolbar() override = default; + StrategyDominanceToolbar(wxWindow *p_parent, GameDocument *p_doc); + ~StrategyDominanceToolbar() override = default; }; #include "bitmaps/next.xpm" @@ -324,7 +324,7 @@ class gbtStrategyDominanceToolbar : public wxPanel, public GameView { #include "bitmaps/tobegin.xpm" #include "bitmaps/toend.xpm" -gbtStrategyDominanceToolbar::gbtStrategyDominanceToolbar(wxWindow *p_parent, GameDocument *p_doc) +StrategyDominanceToolbar::StrategyDominanceToolbar(wxWindow *p_parent, GameDocument *p_doc) : wxPanel(p_parent, wxID_ANY), GameView(p_doc) { auto *topSizer = new wxBoxSizer(wxHORIZONTAL); @@ -336,7 +336,7 @@ gbtStrategyDominanceToolbar::gbtStrategyDominanceToolbar(wxWindow *p_parent, Gam auto *choice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 2, domChoices); choice->SetSelection(0); Connect(choice->GetId(), wxEVT_COMMAND_CHOICE_SELECTED, - wxCommandEventHandler(gbtStrategyDominanceToolbar::OnStrength)); + wxCommandEventHandler(StrategyDominanceToolbar::OnStrength)); topSizer->Add(choice, 0, wxALL | wxALIGN_CENTER, 5); topSizer->Add(new wxStaticText(this, wxID_STATIC, wxT("dominated:")), 0, wxALL | wxALIGN_CENTER, @@ -345,13 +345,13 @@ gbtStrategyDominanceToolbar::gbtStrategyDominanceToolbar(wxWindow *p_parent, Gam m_topButton = new wxBitmapButton(this, wxID_ANY, wxBitmap(tobegin_xpm)); m_topButton->SetToolTip(_("Show all strategies")); Connect(m_topButton->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, - wxCommandEventHandler(gbtStrategyDominanceToolbar::OnTopLevel)); + wxCommandEventHandler(StrategyDominanceToolbar::OnTopLevel)); topSizer->Add(m_topButton, 0, wxALL | wxALIGN_CENTER, 5); m_prevButton = new wxBitmapButton(this, wxID_ANY, wxBitmap(prev_xpm)); m_prevButton->SetToolTip(_("Previous round of elimination")); Connect(m_prevButton->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, - wxCommandEventHandler(gbtStrategyDominanceToolbar::OnPreviousLevel)); + wxCommandEventHandler(StrategyDominanceToolbar::OnPreviousLevel)); topSizer->Add(m_prevButton, 0, wxALL | wxALIGN_CENTER, 5); m_level = new wxStaticText(this, wxID_STATIC, wxT("All strategies shown"), wxDefaultPosition, @@ -361,40 +361,40 @@ gbtStrategyDominanceToolbar::gbtStrategyDominanceToolbar(wxWindow *p_parent, Gam m_nextButton = new wxBitmapButton(this, wxID_ANY, wxBitmap(next_xpm)); m_nextButton->SetToolTip(_("Next round of elimination")); Connect(m_nextButton->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, - wxCommandEventHandler(gbtStrategyDominanceToolbar::OnNextLevel)); + wxCommandEventHandler(StrategyDominanceToolbar::OnNextLevel)); topSizer->Add(m_nextButton, 0, wxALL | wxALIGN_CENTER, 5); m_allButton = new wxBitmapButton(this, wxID_ANY, wxBitmap(toend_xpm)); m_allButton->SetToolTip(_("Eliminate iteratively")); Connect(m_allButton->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, - wxCommandEventHandler(gbtStrategyDominanceToolbar::OnLastLevel)); + wxCommandEventHandler(StrategyDominanceToolbar::OnLastLevel)); topSizer->Add(m_allButton, 0, wxALL | wxALIGN_CENTER, 5); SetSizer(topSizer); wxWindowBase::Layout(); } -void gbtStrategyDominanceToolbar::OnStrength(wxCommandEvent &p_event) +void StrategyDominanceToolbar::OnStrength(wxCommandEvent &p_event) { m_doc->SetStrategyElimStrength(p_event.GetSelection() == 0); } -void gbtStrategyDominanceToolbar::OnTopLevel(wxCommandEvent &) { m_doc->TopStrategyElimLevel(); } +void StrategyDominanceToolbar::OnTopLevel(wxCommandEvent &) { m_doc->TopStrategyElimLevel(); } -void gbtStrategyDominanceToolbar::OnPreviousLevel(wxCommandEvent &) +void StrategyDominanceToolbar::OnPreviousLevel(wxCommandEvent &) { m_doc->PreviousStrategyElimLevel(); } -void gbtStrategyDominanceToolbar::OnNextLevel(wxCommandEvent &) { m_doc->NextStrategyElimLevel(); } +void StrategyDominanceToolbar::OnNextLevel(wxCommandEvent &) { m_doc->NextStrategyElimLevel(); } -void gbtStrategyDominanceToolbar::OnLastLevel(wxCommandEvent &) +void StrategyDominanceToolbar::OnLastLevel(wxCommandEvent &) { while (m_doc->NextStrategyElimLevel()) ; } -void gbtStrategyDominanceToolbar::OnUpdate() +void StrategyDominanceToolbar::OnUpdate() { m_topButton->Enable(m_doc->GetStrategyElimLevel() > 1); m_prevButton->Enable(m_doc->GetStrategyElimLevel() > 1); @@ -421,9 +421,9 @@ BEGIN_EVENT_TABLE(NfgPanel, wxPanel) EVT_MENU(GBT_MENU_TOOLS_DOMINANCE, NfgPanel::OnToolsDominance) END_EVENT_TABLE() -NfgPanel::NfgPanel(wxWindow *p_parent, GameDocument *p_doc) +NfgPanel::NfgPanel(wxWindow *p_parent, GameDocument *p_doc, bool p_showDominance) : wxPanel(p_parent, wxID_ANY), GameView(p_doc), - m_dominanceToolbar(new gbtStrategyDominanceToolbar(this, m_doc)), + m_dominanceToolbar(new StrategyDominanceToolbar(this, m_doc)), m_playerToolbar(new TablePlayerToolbar(this, m_doc)), m_tableWidget(new TableWidget(this, wxID_ANY, m_doc)) { @@ -433,7 +433,7 @@ NfgPanel::NfgPanel(wxWindow *p_parent, GameDocument *p_doc) auto *topSizer = new wxBoxSizer(wxVERTICAL); topSizer->Add(m_dominanceToolbar, 0, wxEXPAND, 0); - topSizer->Show(m_dominanceToolbar, false); + topSizer->Show(m_dominanceToolbar, p_showDominance); topSizer->Add(playerSizer, 1, wxEXPAND, 0); SetSizer(topSizer); wxWindowBase::Layout(); diff --git a/src/gui/nfgpanel.h b/src/gui/nfgpanel.h index 9453ecdc1..6d0c35db4 100644 --- a/src/gui/nfgpanel.h +++ b/src/gui/nfgpanel.h @@ -39,7 +39,7 @@ class NfgPanel final : public wxPanel, public GameView { void OnToolsDominance(wxCommandEvent &); public: - NfgPanel(wxWindow *p_parent, GameDocument *p_doc); + NfgPanel(wxWindow *p_parent, GameDocument *p_doc, bool p_showDominance); ~NfgPanel() override = default; bool IsDominanceShown() const { return m_dominanceToolbar->IsShown(); } diff --git a/src/gui/style.cc b/src/gui/style.cc index ebac051bc..ce030f731 100644 --- a/src/gui/style.cc +++ b/src/gui/style.cc @@ -77,7 +77,6 @@ void TreeRenderConfig::SetDefaults() m_chanceToken = GBT_NODE_TOKEN_DOT; m_playerToken = GBT_NODE_TOKEN_DOT; m_terminalToken = GBT_NODE_TOKEN_DOT; - m_rootReachable = false; m_branchLength = 60; m_tineLength = 20; m_branchStyle = GBT_BRANCH_STYLE_FORKTINE; diff --git a/src/gui/style.h b/src/gui/style.h index 61edcdb25..43d4c1455 100644 --- a/src/gui/style.h +++ b/src/gui/style.h @@ -76,7 +76,6 @@ class TreeRenderConfig { int m_nodeSize{10}, m_terminalSpacing{50}; NodeTokenStyle m_chanceToken{GBT_NODE_TOKEN_DOT}, m_playerToken{GBT_NODE_TOKEN_DOT}, m_terminalToken{GBT_NODE_TOKEN_DOT}; - bool m_rootReachable{false}; // Branch styling int m_branchLength{60}, m_tineLength{20}; @@ -96,7 +95,7 @@ class TreeRenderConfig { // Colors for nodes wxColour m_chanceColor, m_terminalColor; - mutable Gambit::Array m_playerColors; + mutable Array m_playerColors; // Decimal places to display int m_numDecimals{4}; @@ -121,9 +120,6 @@ class TreeRenderConfig { NodeTokenStyle GetTerminalToken() const { return m_terminalToken; } void SetTerminalToken(NodeTokenStyle p_token) { m_terminalToken = p_token; } - bool RootReachable() const { return m_rootReachable; } - void SetRootReachable(bool p_reachable) { m_rootReachable = p_reachable; } - // Branch styling int GetBranchLength() const { return m_branchLength; } void SetBranchLength(int p_length) { m_branchLength = p_length; }