Cas 4 – système à forte dynamique, modèle par arbre d'exécution

Le cas 4, « système à forte dynamique, modèle par arbre d'exécution », tel que proposé dans vos notes de cours, se présente comme suit.

class Bonhomme;
struct AffecterBonhomme;

#include "Mutex.h"
#include "Incopiable.h"
#include <algorithm>
#include <vector>
#include <memory>

class Population
   : Incopiable
{
   std::vector<std::shared_ptr<Bonhomme>> individus_;
   Population()
      { }
   Mutex mutex_;
public:
   void inscrire(std::shared_ptr<Bonhomme> p)
   {
      Autoverrou av(mutex_);
      individus_.push_back(p);
   }
   void desinscrire(std::shared_ptr<Bonhomme> p)
   {
      Autoverrou av(mutex_);
      using std::find;
      using std::begin;
      using std::end;
      auto itt = find(begin(individus_), end(individus_), p);
      if (itt != end(individus_))
         individus_.erase(itt);
   }
   static Population &get()
   {
      static Population singleton;
      return singleton;
   }
   template <class Pred>
      std::shared_ptr<Bonhomme> trouver(Pred);
   void agir_sur_survivant(AffecterBonhomme &);
   void afficher_etat() const;
   void apocalypse() throw()
   {
      Autoverrou av(mutex_);
      individus_.clear();
   }
};

#include <string>
#include <sstream>
template <class T>
   std::string ttos(const T &val)
   {
      using std::stringstream;
      stringstream sstr;
      sstr << val;
      return sstr.str();
   }

#include "Autonome.h"

class Bonhomme
   : public Acteur
{
   enum { VIE_DEFAUT = 10 };
   int vie_;
public:
   typedef std::string str_type;
private:
   const str_type nom_;
   void agir()
      { if (est_vivant()) agir_impl(); }
protected:
   virtual void agir_impl() = 0;
public:
   Bonhomme(const str_type &nom, const int vie = VIE_DEFAUT)
      : vie_(vie), nom_(nom)
   {
   }
   virtual bool peut_blesser() const throw()
      { return true; }
   void blesser(const int degats) throw()
      { vie_ -= degats; }
   void soigner(const int soins) throw()
      { vie_ += soins; }
   int vie() const throw()
      { return vie_; }
   str_type nom() const
      { return nom_; }
   bool est_vivant() const throw()
      { return vie() > 0; }
   bool est_mort() const throw()
      { return !est_vivant(); }
   virtual str_type  descriptif() const
      { return nom() + " (" + ttos(vie()) + ")"; }
};

struct AffecterBonhomme
{
   virtual void operation(std::shared_ptr<Bonhomme>) = 0;
   virtual ~AffecterBonhomme() throw()
      { }
};

class SoignerBonhomme
   : public AffecterBonhomme
{
   const int soins_;
public:
   SoignerBonhomme(const int soins)
      : soins_(soins)
   {
   }
   virtual bool peut_blesser() const throw()
      { return false; }
   int soins() const throw()
      { return soins_; }
   void operation(std::shared_ptr<Bonhomme> b)
      { b->soigner(soins()); }
};
class BlesserBonhomme
   : public AffecterBonhomme
{
   const int degats_;
public:
   BlesserBonhomme(const int degats)
      : degats_(degats)
   {
   }
   int degats() const throw()
      { return degats_; }
   void operation(std::shared_ptr<Bonhomme> b)
      { b->blesser(degats()); }
};

template <class Pred>
   std::shared_ptr<Bonhomme> Population::trouver(Pred pred)
   {
      using std::shared_ptr;
      using std::begin;
      using std::end;
      using std::find_if;
      Autoverrou av(mutex_);
      auto itt = find_if(begin(individus_), end(individus_), pred);
      return itt != end(individus_)? *itt : 0;
   }

#include <iterator>
void Population::agir_sur_survivant(AffecterBonhomme &ab)
{
   using std::vector;
   using std::back_inserter;
   using std::copy_if;
   using std::remove_if;
   using std::random_shuffle;
   using std::shared_ptr;
   vector<shared_ptr<Bonhomme>> v;
   {
      Autoverrou av(mutex_);
      copy_if(begin(individus_), end(individus_), back_inserter(v), [](shared_ptr<Bonhomme> b) {
         return b->est_vivant();
      });
   }
   if (v.empty()) return;
   random_shuffle(begin(v), end(v));
   ab.operation(v.front());
}

#include <iostream>
void Population::afficher_etat() const
{
   using std::begin;
   using std::end;
   using std::cout;
   using std::endl;
   using std::for_each;
   using std::shared_ptr;
   Autoverrou av(mutex_);
   for_each(begin(individus_),end(individus_), [](shared_ptr<Bonhomme> b) {
      if (b->est_vivant()) cout << b->descriptif() << ", ";
   });
   cout << endl<< endl;
}
#include <cstdlib>

class Tireur
   : public Bonhomme
{
   Tireur(const str_type &nom)
      : Bonhomme(nom)
   {
   }
public:
   static std::shared_ptr<Bonhomme> creer(const str_type &nom)
      { return std::shared_ptr<Bonhomme>(new Tireur(nom)); }
private:
   void agir_impl()
   {
      using std::rand;
      const int BOBO = rand() % 5 + 1;
      Population::get().agir_sur_survivant(BlesserBonhomme(BOBO));
   }
public:
   str_type descriptif() const
      { return str_type("Tireur: ") + Bonhomme::descriptif(); }
};

class Soigneur
   : public Bonhomme
{
   Soigneur(const str_type &nom)
      : Bonhomme(nom)
   {
   }
public:
   static std::shared_ptr<Bonhomme> creer(const str_type &nom)
      { return std::shared_ptr<Bonhomme>(new Soigneur(nom)); }
private:
   bool peut_blesser() const throw()
      { return false; }
   void agir_impl()
   {
      using std::rand;
      const int OUF = rand() % 3 + 1;
      Population::get().agir_sur_survivant(SoignerBonhomme(OUF));
   }
public:
   str_type descriptif() const
      { return str_type("Soigneur: ") + Bonhomme::descriptif(); }
};

class Surveillant
   : public Autonome
{
   enum { SUSPENS_SURVEILLANT = 1000 };
   Surveillant()
      : Autonome(SUSPENS_SURVEILLANT)
   {
   }
public:
   static std::unique_ptr<Surveillant> creer()
   {
      using std::unique_ptr;
      unique_ptr<Surveillant> p(new Surveillant);
      p->demarrer();
      return p;
   }
   void agir ()
   {
      Population::get().afficher_etat();
      using std::cout;
      using std::endl;
      using std::shared_ptr;
      auto survivant = [](shared_ptr<Bonhomme> b) {
         return b->est_vivant();
      };
      auto tireur = [](shared_ptr<Bonhomme> b) {
         return b->est_vivant() && b->peut_blesser();
      };
      if (!Population::get().trouver(survivant))
      {
         cout << "Ce fut un carnage..." << endl;
         demander_arret();
      }
      else if (!Population::get().trouver(tireur))
      {
         cout << "Il ne reste que des soigneurs!" << endl;
         demander_arret();
      }
   }
};

class StrateInvalide {};
class ObjetInvalide {};
class ObjetIntrouvable {};

#include <vector>
#include <cmath>
#include <cassert>
#include <array>

template <int B, int E>
   struct puissance
   {
      enum { VAL = B * puissance<B, E-1>::VAL };
   };
template <int B>
   struct puissance<B,0>
   {
      static_assert(B!=0, "0^0 est indéfini");
      enum { VAL = 1 };
   };
   
template <int Prof>
   class Moteur
      : public Autonome
   {
      class StrateBase
      {
         std::vector<std::shared_ptr<Acteur>> acteurs_;
         Mutex mutex_;
      public:
         void abonner(std::shared_ptr<Acteur> p)
         {
            if (!p) throw ObjetInvalide();
            Autoverrou av(mutex_);
            acteurs_.push_back(p);
         }
         void desabonner(std::shared_ptr<Acteur> p)
         {
            if (!p) throw ObjetInvalide();
            using std::find;
            Autoverrou av(mutex_);
            auto itt = find(begin(acteurs_), end(acteurs_), p);
            if (itt != end(acteurs_)) acteurs_.erase(itt);
         }
         void operer()
         {
            using std::begin;
            using std::end;
            using std::for_each;
            using std::shared_ptr;
            Autoverrou av(mutex_);
            for_each(begin(acteurs_), end(acteurs_), [](shared_ptr<Acteur> p) {
               p->agir();
            });
         }
      };
      template <int N>
         class Strate
            : StrateBase
         {
            std::array<Strate<N-1>, puissance<2,Prof-N>::VAL> enfants_;
         public:
            typedef typename
               std::array<Strate<N-1>, puissance<2,Prof-N>::VAL>::size_type size_type;
         private:
            size_type cur_;
         public:
            Strate()
               : cur_(size_type())
            {
            }
            void abonner(std::shared_ptr<Acteur> p, int strate)
            {
               if (strate)
                  enfants_[rand() % enfants_.size()].abonner(p, strate - 1);
               else
                  StrateBase::abonner(p);
            }
            void desabonner(std::shared_ptr<Acteur> p, int strate)
            {
               if (strate)
               {
                  using std::for_each;
                  using std::begin;
                  using std::end;
                  for_each(begin(enfants_), end(enfants_), [&](Strate &s) {
                     s.desabonner(p, strate - 1);
                  });
               }
               else
                  StrateBase::desabonner(p);
            }
            void operer()
            {
               StrateBase::operer();
               enfants_[cur_].operer();
               cur_ = (cur_ + 1) % enfants_.size();
            }
         };
      template <>
         class Strate<0>
            : StrateBase
         {
         public:
            void abonner(std::shared_ptr<Acteur> p, int strate)
            {
               if (strate) throw StrateInvalide();
               StrateBase::abonner(p);
            }
            void desabonner(std::shared_ptr<Acteur> p, int strate)
            {
               if (strate) throw StrateInvalide();
               StrateBase::desabonner(p);
            }
            using StrateBase::operer;
         };
      Strate<Prof> strates_;
      Moteur(const std::vector<std::pair<std::shared_ptr<Acteur>, int>> &acteurs)
         : Autonome(500)
      {
         using std::begin;
         using std::end;
         using std::for_each;
         using std::pair;
         using std::shared_ptr;
         for_each(begin(acteurs), end(acteurs), [&](pair<shared_ptr<Acteur>,int> acteur) {
            strates_.abonner(acteur.first, acteur.second);
         });
      }
   public:
      void abonner(std::shared_ptr<Acteur> p, const int strate)
      {
         assert(strate >= 0);
         strates_.abonner(p, strate);
      }
      void desabonner(std::shared_ptr<Acteur> p, const int strate)
      {
         assert(strate >= 0);
         strates_.desabonner(p);
      }
      static std::unique_ptr<Moteur> creer(const std::vector<std::pair<std::shared_ptr<Acteur>, int>> &acteurs)
      {
         using std::unique_ptr;
         unique_ptr<Moteur> p(new Moteur(acteurs));
         p->demarrer();
         return p;
      }
      void agir()
         { strates_.operer(); }
   };

int main()
{
   using std::srand;
   using std::time;
   using std::shared_ptr;
   using std::vector;
   using std::pair;
   using std::make_pair;
   srand(static_cast<unsigned int>(time(0)));
   const int NB_BONSHOMMES = 20;
   const Bonhomme::str_type NOMS[] =
   {
      "Adele", "Bob", "Chuck", "Diane", "Ed",
      "Fred",  "Gus", "Ida",   "Jack",  "Ken"
   };
   enum { NB_NOMS = sizeof(NOMS)/sizeof(*NOMS) };
   vector<pair<shared_ptr<Acteur>, int>> v;
   for (int i = 0; i < NB_BONSHOMMES; ++i)
   {
      auto nom = NOMS[i % NB_NOMS] + " " + ttos(i);
      shared_ptr<Bonhomme> p;
      if (rand() % 4 != 0)
      {
         p = Tireur::creer(nom);
         v.push_back(make_pair(p,1));
         Population::get().inscrire(p);
      }
      else
      {
         p= Soigneur::creer(nom);
         v.push_back(make_pair(p,2));
         Population::get().inscrire(p);
      }
   }
   auto moteur = Moteur<4>::creer(v);
   auto surveillant = Surveillant::creer();
   surveillant->attendre_fin();
}

Sur mon petit ordinateur portatif, compilé avec Visual Studio 2010, configuration Release, et en prenant bien entendu soin d'ajouter _SECURE_SCL=0 aux options du préprocesseur pour désactiver les (franchement inutiles et nuisibles) Checked Iterators, j'obtiens évidemment plusieurs résultats, comme par exemple :

Tireur: Adele 0 (10), Tireur: Bob 1 (10), Soigneur: Chuck 2 (10), Tireur: Diane
3 (10), Tireur: Ed 4 (10), Soigneur: Fred 5 (10), Tireur: Gus 6 (10), Soigneur:
Ida 7 (10), Soigneur: Jack 8 (10), Tireur: Ken 9 (10), Soigneur: Adele 10 (10),
Tireur: Bob 11 (10), Tireur: Chuck 12 (10), Tireur: Diane 13 (10), Tireur: Ed 14
 (10), Tireur: Fred 15 (10), Tireur: Gus 16 (10), Tireur: Ida 17 (10), Soigneur:
 Jack 18 (10), Tireur: Ken 19 (10),

Tireur: Bob 1 (10), Soigneur: Chuck 2 (7), Tireur: Diane 3 (6), Tireur: Ed 4 (2)
, Soigneur: Fred 5 (1), Tireur: Gus 6 (6), Soigneur: Jack 8 (6), Tireur: Ken 9 (
9), Soigneur: Adele 10 (10), Tireur: Bob 11 (8), Tireur: Chuck 12 (4), Tireur: D
iane 13 (6), Tireur: Ed 14 (6), Tireur: Fred 15 (3), Tireur: Gus 16 (7), Tireur:
 Ida 17 (10), Soigneur: Jack 18 (10), Tireur: Ken 19 (10),

Tireur: Bob 1 (4), Tireur: Diane 3 (6), Soigneur: Fred 5 (1), Soigneur: Jack 8 (
6), Tireur: Ken 9 (3), Soigneur: Adele 10 (8), Tireur: Bob 11 (8), Tireur: Diane
 13 (10), Tireur: Ed 14 (5), Tireur: Fred 15 (3), Tireur: Gus 16 (6), Tireur: Id
a 17 (2), Soigneur: Jack 18 (7),

Tireur: Diane 3 (6), Soigneur: Fred 5 (1), Soigneur: Jack 8 (8), Tireur: Ken 9 (
2), Soigneur: Adele 10 (6), Tireur: Diane 13 (10), Tireur: Fred 15 (5), Tireur:
Gus 16 (6),

Soigneur: Fred 5 (4), Soigneur: Jack 8 (5), Soigneur: Adele 10 (5), Tireur: Dian
e 13 (10), Tireur: Gus 16 (4),

Soigneur: Jack 8 (5), Tireur: Diane 13 (9), Tireur: Gus 16 (5),

Soigneur: Jack 8 (4), Tireur: Diane 13 (1), Tireur: Gus 16 (6),

Tireur: Gus 16 (4),

Ce fut un carnage...
Appuyez sur une touche pour continuer...

Valid XHTML 1.0 Transitional

CSS Valide !