Logo Search packages:      
Sourcecode: gambit version File versions  Download package

gamedoc.cc

//
// $Source: /cvsroot/gambit/gambit/sources/gui/gamedoc.cc,v $
// $Date: 2006/01/17 16:23:19 $
// $Revision: 1.65 $
//
// DESCRIPTION:
// Implementation of game document class
//
// This file is part of Gambit
// Copyright (c) 2005, The Gambit Project
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//

#include <sstream>
#include <fstream>

#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif  // WX_PRECOMP
#include <wx/filename.h>       // used to create temp files for undo/redo

#include "libgambit/libgambit.h"
#include "tinyxml.h"    // for XML parser for LoadDocument()

#include "gambit.h"     // for wxGetApp()
#include "gamedoc.h"

//=========================================================================
//                       class gbtBehavDominanceStack
//=========================================================================

gbtBehavDominanceStack::gbtBehavDominanceStack(gbtGameDocument *p_doc,
                                     bool p_strict)
  : m_doc(p_doc), m_strict(p_strict), m_noFurther(false)
{
  Reset();
}

gbtBehavDominanceStack::~gbtBehavDominanceStack()
{
  for (int i = 1; i <= m_supports.Length(); delete m_supports[i++]);
}

00058 void gbtBehavDominanceStack::SetStrict(bool p_strict)
{
  if (m_strict != p_strict)  Reset();
  m_strict = p_strict;
}

00064 void gbtBehavDominanceStack::Reset(void)
{
  for (int i = 1; i <= m_supports.Length(); delete m_supports[i++]);
  m_supports = Gambit::Array<Gambit::BehavSupport *>();
  if (m_doc->IsTree()) {
    m_supports.Append(new Gambit::BehavSupport(m_doc->GetGame()));
    m_current = 1;
  }
}

00074 bool gbtBehavDominanceStack::NextLevel(void)
{
  if (m_current < m_supports.Length()) {
    m_current++;
    return true;
  }

  if (m_noFurther) {
    return false;
  }

  Gambit::Array<int> players;
  for (int pl = 1; pl <= m_doc->GetGame()->NumPlayers(); pl++) {
    players.Append(pl);
  }
  
  std::ostringstream gnull;
  Gambit::BehavSupport newSupport = 
    m_supports[m_current]->Undominated(m_strict, true, players, gnull);

  if (newSupport != *m_supports[m_current]) {
    m_supports.Append(new Gambit::BehavSupport(newSupport));
    m_current++;
    return true;
  }
  else {
    m_noFurther = true;
    return false;
  }
}

00105 bool gbtBehavDominanceStack::PreviousLevel(void)
{
  if (m_current > 1) {
    m_current--;
    return true;
  }
  else {
    return false;
  }
}

//=========================================================================
//                   class gbtStrategyDominanceStack
//=========================================================================

gbtStrategyDominanceStack::gbtStrategyDominanceStack(gbtGameDocument *p_doc,
                                         bool p_strict)
  : m_doc(p_doc), m_strict(p_strict), m_noFurther(false)
{
  Reset();
}

gbtStrategyDominanceStack::~gbtStrategyDominanceStack()
{
  for (int i = 1; i <= m_supports.Length(); delete m_supports[i++]);
}

00132 void gbtStrategyDominanceStack::SetStrict(bool p_strict)
{
  if (m_strict != p_strict)  Reset();
  m_strict = p_strict;
}

00138 void gbtStrategyDominanceStack::Reset(void)
{
  for (int i = 1; i <= m_supports.Length(); delete m_supports[i++]);
  m_supports = Gambit::Array<Gambit::StrategySupport *>();
  m_supports.Append(new Gambit::StrategySupport(m_doc->GetGame()));
  m_current = 1;
  m_noFurther = false;
}

00147 bool gbtStrategyDominanceStack::NextLevel(void)
{
  if (m_current < m_supports.Length()) {
    m_current++;
    return true;
  }

  if (m_noFurther) {
    return false;
  }

  Gambit::Array<int> players;
  for (int pl = 1; pl <= m_doc->GetGame()->NumPlayers(); pl++) {
    players.Append(pl);
  }

  std::ostringstream gnull;
  Gambit::StrategySupport newSupport = 
    m_supports[m_current]->Undominated(m_strict, players, gnull);

  if (newSupport != *m_supports[m_current]) {
    m_supports.Append(new Gambit::StrategySupport(newSupport));
    m_current++;
    return true;
  }
  else {
    m_noFurther = true;
    return false;
  }
}

00178 bool gbtStrategyDominanceStack::PreviousLevel(void)
{
  if (m_current > 1) {
    m_current--;
    return true;
  }
  else {
    return false;
  }
}

//=========================================================================
//                          class gbtGameDocument
//=========================================================================

gbtGameDocument::gbtGameDocument(Gambit::Game p_game) 
  : m_game(p_game),
    m_selectNode(0), m_modified(false),
    m_behavSupports(this, true), m_stratSupports(this, true),
    m_currentProfileList(0)
{
  m_game->Canonicalize();
  m_game->BuildComputedValues();
  wxGetApp().AddDocument(this);

  std::ostringstream s;
  SaveDocument(s);
  m_undoList.Append(s.str());
}

gbtGameDocument::~gbtGameDocument()
{
  wxGetApp().RemoveDocument(this);
}

bool gbtGameDocument::LoadDocument(const wxString &p_filename,
                           bool p_saveUndo)
{
  TiXmlDocument doc((const char *) p_filename.mb_str());
  if (!doc.LoadFile()) {
    // Some error occurred.  Do something smart later.
    return false;
  }

  TiXmlNode *game = doc.FirstChild("game");
  if (!game) {
    // There ought to be at least one game child.  If not... umm...
    return false;
  }

  TiXmlNode *efgfile = game->FirstChild("efgfile");
  if (efgfile) {
    try {
      std::istringstream s(efgfile->FirstChild()->Value());
      m_game = Gambit::ReadGame(s);
    }
    catch (...) {
      return false;
    }
  }
  
  TiXmlNode *nfgfile = game->FirstChild("nfgfile");
  if (nfgfile) {
    try {
      std::istringstream s(nfgfile->FirstChild()->Value());
      m_game = Gambit::ReadGame(s);
    }
    catch (...) {
      return false;
    }
  }

  if (!efgfile && !nfgfile) {
    // No game representation... punt!
    return false;
  }
  
  m_behavSupports.Reset();
  m_stratSupports.Reset();

  m_profiles = Gambit::List<gbtAnalysisOutput *>();

  for (TiXmlNode *analysis = game->FirstChild("analysis");
       analysis; analysis = analysis->NextSibling()) {
    const char *type = analysis->ToElement()->Attribute("type");
    const char *rep = analysis->ToElement()->Attribute("rep");
    if (type && !strcmp(type, "list")) {
      // Read in a list of profiles
      // We need to try to guess whether the profiles are float or rational
      bool isFloat = false;
      for (TiXmlNode *profile = analysis->FirstChild("profile");
         profile; profile = profile->NextSiblingElement()) {
      if (std::string(profile->FirstChild()->Value()).find('.') != -1 or
          std::string(profile->FirstChild()->Value()).find('e') != -1) {
        isFloat = true;
        break;
      }
      }
      
      if (isFloat) {
      gbtAnalysisProfileList<double> *plist = 
        new gbtAnalysisProfileList<double>(this, false);
      plist->Load(analysis);
      m_profiles.Append(plist);
      }
      else {
      gbtAnalysisProfileList<Rational> *plist =
        new gbtAnalysisProfileList<Rational>(this, false);
      plist->Load(analysis);
      m_profiles.Append(plist);
      }
    }
  }

  m_currentProfileList = m_profiles.Length();

  TiXmlNode *colors = doc.FirstChild("colors");
  if (colors)  m_style.SetColorXML(colors);
  TiXmlNode *font = doc.FirstChild("font");
  if (font)    m_style.SetFontXML(font);
  TiXmlNode *layout = doc.FirstChild("autolayout");
  if (layout)  m_style.SetLayoutXML(layout);
  TiXmlNode *labels = doc.FirstChild("labels");
  if (labels)  m_style.SetLabelXML(labels);
  TiXmlNode *numbers = doc.FirstChild("numbers");
  if (numbers) {
    int numDecimals = 4;
    numbers->ToElement()->QueryIntAttribute("decimals", &numDecimals);
    m_style.SetNumDecimals(numDecimals);
  }

  if (p_saveUndo) {
    std::ostringstream s;
    SaveDocument(s);
    m_undoList.Append(s.str());
  }

  return true;
}

void gbtGameDocument::SaveDocument(std::ostream &p_file) const
{
  p_file << "<?xml version=\"1.0\" standalone=no>\n";

  p_file << m_style.GetColorXML();
  p_file << m_style.GetFontXML();

  if (m_game->IsTree()) {
    p_file << m_style.GetLayoutXML();
    p_file << m_style.GetLabelXML();
  }

  p_file << "<numbers decimals=\"" << m_style.NumDecimals() << "\"/>\n";

  p_file << "<game>\n";

  if (m_game->IsTree()) {
    p_file << "<efgfile>\n";
    m_game->WriteEfgFile(p_file);
    p_file << "</efgfile>\n";

  }
  else {
    p_file << "<nfgfile>\n";
    m_game->WriteNfgFile(p_file);
    p_file << "</nfgfile>\n";
  }

  for (int i = 1; i <= m_profiles.Length(); i++) {
    m_profiles[i]->Save(p_file);
  }

  p_file << "</game>\n";

}

void gbtGameDocument::UpdateViews(gbtGameModificationType p_modifications)
{
  if (p_modifications != GBT_DOC_MODIFIED_NONE) {
    m_modified = true;
    m_game->Canonicalize();
    m_game->BuildComputedValues();
    m_redoList = Gambit::List<std::string>();

    std::ostringstream s;
    SaveDocument(s);
    m_undoList.Append(s.str());
  }

  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
    // computed profiles invalid for the edited game, it does mean
    // that, in general, they won't be Nash.  For now, to avoid confusion,
    // we will wipe them out.
    while (m_profiles.Length() > 0) {
      delete m_profiles.Remove(1);
    }
    m_currentProfileList = 0;
  }

  for (int i = 1; i <= m_views.Length(); m_views[i++]->OnUpdate());
}

void gbtGameDocument::PostPendingChanges(void)
{
  for (int i = 1; i <= m_views.Length(); m_views[i++]->PostPendingChanges());
}

void gbtGameDocument::BuildNfg(void)
{ 
  if (m_game->IsTree()) {
    m_game->BuildComputedValues();
    m_stratSupports.Reset();
    for (int i = 1; i <= m_profiles.Length(); m_profiles[i++]->BuildNfg());
  }
}

//
// A word about the undo and redo features:
// We store a list of the textual representation of games.  We don't
// store other aspects of the state (e.g., profiles) as yet.
// The "undo" list includes the representation of the current state
// of the game (hence, CanUndo() only returns true when the list has
// more than one element.
//
void gbtGameDocument::Undo(void)
{
  // The current game is at the end of the undo list; move it to the redo list
  m_redoList.Append(m_undoList[m_undoList.Length()]);
  m_undoList.Remove(m_undoList.Length());

  m_game = 0;

  while (m_profiles.Length() > 0) {
    delete m_profiles.Remove(1);
  }
  m_currentProfileList = 0;

  wxString tempfile = wxFileName::CreateTempFileName(wxT("gambit"));
  std::ofstream f((const char *) tempfile.mb_str());
  f << m_undoList[m_undoList.Length()] << std::endl;
  f.close();

  LoadDocument(tempfile, false);
  wxRemoveFile(tempfile);

  for (int i = 1; i <= m_views.Length(); m_views[i++]->OnUpdate());
}

void gbtGameDocument::Redo(void)
{
  m_undoList.Append(m_redoList[m_redoList.Length()]);
  m_redoList.Remove(m_redoList.Length());

  m_game = 0;

  while (m_profiles.Length() > 0) {
    delete m_profiles.Remove(1);
  }
  m_currentProfileList = 0;

  wxString tempfile = wxFileName::CreateTempFileName(wxT("gambit"));
  std::ofstream f((const char *) tempfile.mb_str());
  f << m_undoList[m_undoList.Length()] << std::endl;
  f.close();

  LoadDocument(tempfile, false);
  wxRemoveFile(tempfile);

  for (int i = 1; i <= m_views.Length(); m_views[i++]->OnUpdate());
}


void gbtGameDocument::SetCurrentProfile(int p_profile)
{
  m_profiles[m_currentProfileList]->SetCurrent(p_profile);
  UpdateViews(GBT_DOC_MODIFIED_VIEWS);
}

void gbtGameDocument::AddProfileList(gbtAnalysisOutput *p_profs)
{
  m_profiles.Append(p_profs);
  m_currentProfileList = m_profiles.Length();
  UpdateViews(GBT_DOC_MODIFIED_VIEWS);
}

void gbtGameDocument::SetProfileList(int p_index)
{
  m_currentProfileList = p_index;
  UpdateViews(GBT_DOC_MODIFIED_VIEWS);
}

/*
void gbtGameDocument::AddProfiles(const Gambit::List<Gambit::MixedBehavProfile<double> > &p_profiles)
{
  for (int i = 1; i <= p_profiles.Length(); i++) {
    m_profiles[m_currentProfileList].Append(p_profiles[i]);
  }

  m_profiles[m_currentProfileList].SetCurrent(m_profiles[m_currentProfileList].NumProfiles());
  UpdateViews(GBT_DOC_MODIFIED_VIEWS);
}

void gbtGameDocument::AddProfile(const Gambit::MixedBehavProfile<double> &p_profile)
{
  m_profiles[m_currentProfileList].Append(p_profile);
  m_profiles[m_currentProfileList].SetCurrent(m_profiles[m_currentProfileList].NumProfiles());
  UpdateViews(GBT_DOC_MODIFIED_VIEWS);
}

void gbtGameDocument::AddProfiles(const Gambit::List<Gambit::MixedStrategyProfile<double> > &p_profiles)
{
  for (int i = 1; i <= p_profiles.Length(); i++) {
    m_profiles[m_currentProfileList].Append(p_profiles[i]);
  }

  m_profiles[m_currentProfileList].SetCurrent(m_profiles[m_currentProfileList].NumProfiles());
  UpdateViews(GBT_DOC_MODIFIED_VIEWS);
}

void gbtGameDocument::AddProfile(const Gambit::MixedStrategyProfile<double> &p_profile)
{
  m_profiles[m_currentProfileList].Append(p_profile);
  m_profiles[m_currentProfileList].SetCurrent(m_profiles[m_currentProfileList].NumProfiles());
  UpdateViews(GBT_DOC_MODIFIED_VIEWS);
}
*/

void gbtGameDocument::SetBehavElimStrength(bool p_strict)
{
  m_behavSupports.SetStrict(p_strict);
  UpdateViews(GBT_DOC_MODIFIED_VIEWS);
}

bool gbtGameDocument::NextBehavElimLevel(void)
{
  bool ret = m_behavSupports.NextLevel();
  UpdateViews(GBT_DOC_MODIFIED_VIEWS);
  return ret;
}

void gbtGameDocument::PreviousBehavElimLevel(void)
{
  m_behavSupports.PreviousLevel();
  UpdateViews(GBT_DOC_MODIFIED_VIEWS);
}

void gbtGameDocument::TopBehavElimLevel(void)
{
  m_behavSupports.TopLevel();
  UpdateViews(GBT_DOC_MODIFIED_VIEWS);
}

bool gbtGameDocument::CanBehavElim(void) const
{
  return m_behavSupports.CanEliminate();
}

int gbtGameDocument::GetBehavElimLevel(void) const
{
  return m_behavSupports.GetLevel();
}


void gbtGameDocument::SetStrategyElimStrength(bool p_strict)
{
  m_stratSupports.SetStrict(p_strict);
  UpdateViews(GBT_DOC_MODIFIED_VIEWS);
}

bool gbtGameDocument::GetStrategyElimStrength(void) const
{
  return m_stratSupports.GetStrict();
}

bool gbtGameDocument::NextStrategyElimLevel(void)
{
  bool ret = m_stratSupports.NextLevel();
  UpdateViews(GBT_DOC_MODIFIED_VIEWS);
  return ret;
}

void gbtGameDocument::PreviousStrategyElimLevel(void)
{
  m_stratSupports.PreviousLevel();
  UpdateViews(GBT_DOC_MODIFIED_VIEWS);
}

void gbtGameDocument::TopStrategyElimLevel(void)
{
  m_stratSupports.TopLevel();
  UpdateViews(GBT_DOC_MODIFIED_VIEWS);
}

bool gbtGameDocument::CanStrategyElim(void) const
{
  return m_stratSupports.CanEliminate();
}

int gbtGameDocument::GetStrategyElimLevel(void) const
{
  return m_stratSupports.GetLevel();
}


void gbtGameDocument::SetSelectNode(Gambit::GameNode p_node)
{
  m_selectNode = p_node;
  UpdateViews(GBT_DOC_MODIFIED_VIEWS);
}


Generated by  Doxygen 1.6.0   Back to index