Dal C al C++/Gioco life di Conway

Wikibooks, manuali e libri di testo liberi.

Di seguito il listato in C++ per un'implementazione testuale del gioco della vita.


Paradigmi utilizzati:

 // Author: BlakWolf, © 2004
 // Released under GPL v.2 or later, at your option
 #include <iostream>
 #include <cstdlib>
 #include <vector>
 #include <set> 
 
 using std::cout;
 using std::cin;
 using std::dec;
 using std::hex;
 
 // Osservabile e osservato contemporaneamente, gestisce le regole "biologiche"
 class Casella{
 public:
     enum stato{vuoto=0,pieno};
     typedef std::set<Casella *> Container;
     typedef Container::iterator Iterator; 
 
 private:
     stato stato_;
     int vicini_futuri;
     int vicini;
     Container osservatori;
 
 public:
     Casella( const Casella::stato & s = Casella::vuoto ) 
      : stato_(s), vicini_futuri(0), vicini(0){}  
 
     //non copia gli osservatori
     Casella(const Casella& c): vicini(c.vicini),
             vicini_futuri(c.vicini_futuri), stato_(c.stato_){}
     virtual ~Casella(){}  
 
     // Registra gli osservatori a cui notificare i cambiamenti
     inline void Registra(Casella& c) {
         if (&c != this){
                osservatori.insert(&c);
         }
     } 
 
 
     inline void Imposta(const Casella::stato& s){
        stato_=s;
     }
 
     inline void Muori(){
       if(stato_==pieno){
        Imposta(Casella::vuoto);
        Notifica(-1);
       }
     }
 
 
     inline void Nasci() {
        if (stato_==vuoto){
         Imposta(Casella::pieno);
         Notifica(1);
        }
     } 
 
     // Aggiorna 
     inline void Ciclo() {
       vicini=vicini_futuri;
     }
 
     // Regole del gioco
     inline void Verifica() {
         if (stato_==pieno){
            if (vicini<2 || vicini>3) // Modificare questa riga per cambiare le regole di morte
               Muori();
         }else {
            if (vicini==3) // Modificare questa riga per cambiare le regole di nascita
               Nasci();
           }
     } 
 
     inline bool Leggi() const{
         return stato_==pieno;
     }
 
 
 private:
     // Notifica ai vicini nascita o morte
     inline void Notifica(int msg) const{
         for (Iterator it = osservatori.begin(); it != osservatori.end(); ++it){
             (*it)->RiceviNotifica(msg);
         }
     }
 
     // Ricevi la notifica dai vicini
     inline void RiceviNotifica(int msg) {
         vicini_futuri += msg;
     }
 }; 
 
 // Contenitore di caselle, che genera automaticamente le corrispondenze tra vicini. 
 // Gestisce le regole "topologiche"
 class Griglia{
 public:
     typedef std::vector<Casella> Container;
     typedef Container::iterator Iterator;
 
 private:
     int righe_;
     int colonne_;
     Container griglia;
 
 public:
     virtual ~Griglia(){}
 
     Griglia(const int& righe, const int& colonne)
      :righe_(righe), colonne_(colonne)    {
       if (righe_ < 5) righe_=5;
       if (colonne_ < 5) righe_=5;
       griglia.reserve(righe_*colonne_);
       griglia.resize(righe_*colonne_);
     }
 
     inline Casella& operator[](const int n) {
       return griglia[n];
     }
 
     inline void Cicla() {    
       for (int i = 0; i < righe_*colonne_;++i)
           griglia[i].Ciclo();
     }
 
     void Verifica(){
       for (int i = 0; i < righe_*colonne_;++i)
           griglia[i].Verifica();
     }
 
     inline void Imposta (const int& pos) {
         if ((pos >=0)&&(pos < griglia.size()))           
           griglia[pos].Nasci();
     }
 
     inline void Resetta (const int& pos) {
         if ((pos >=0)&&(pos < griglia.size()))   
           griglia[pos].Muori();
     }
 
 
     // Modificare questa routine per cambiare le regole topologiche di vicinanza
     inline void Genera(){
       bool primo, ultimo;
       for (int indice=0; indice< righe_*colonne_;++indice){
         primo = ( (indice%colonne_) == 0);
         ultimo = ( (indice%colonne_) == colonne_-1);
 
         if (indice > colonne_){      
           if (!primo)
              griglia[indice].Registra(griglia[indice-colonne_ - 1]);
           griglia[indice].Registra(griglia[indice-colonne_]);
           if (!ultimo)
              griglia[indice].Registra(griglia[indice-colonne_ + 1]);
         }
 
         if (!primo)
           griglia[indice].Registra(griglia[indice-1]);
         if (!ultimo)
           griglia[indice].Registra(griglia[indice+1]);
 
         if (indice < ((colonne_)*(righe_-1)) ){
           if (!ultimo)
             griglia[indice].Registra(griglia[indice+colonne_+1]);
           griglia[indice].Registra(griglia[indice+colonne_]);      
           if (!primo)
             griglia[indice].Registra(griglia[indice+colonne_-1]);
         } 
       } 
     }
 
     inline const int Righe() const { return righe_; }
     inline const int Colonne() const { return colonne_; }
 }; 
 
 
 //Stampa a schermo la griglia. Gestione statica delle dimensioni. Per variare, utilizzare griglia.Righe() e Colonne()
 void Stampa( Griglia& g) {
   cout << "\n\n";
   cout << "+--------------------+\n|";
   for (int indice=0;indice < 400;++indice){
     bool ultimo = ( (indice%20) == 19);
     g[indice].Leggi()?cout << "O":cout << " ";
     if (ultimo){
       if (indice !=399)
         {cout << "|\n|";}
       else
         {cout << "|\n";}
     }
   }
   cout << "+--------------------+\n";
 }
 
 
 int main () {
   Griglia griglia(20,20) ;
   griglia.Genera();
 
   // Inizializza la griglia casualmente, con fattore di 
   // riempimento un ottavo.
   // Gestione statica delle dimensioni. Per variare, utilizzare griglia.Righe() e Colonne()
   srand(time(0));
   for (int i=0; i < 100;++i){
     griglia.Imposta((rand()>>4) %400);
   }
 
   while (1){
     Stampa(griglia);   
     griglia.Cicla();   
     griglia.Verifica();
     // Aspetta la pressione di enter
     cin.get();
   }
   // Mai raggiunto
   return 0;
 }

Strumenti personali
Namespace

Varianti
Azioni
Navigazione
Comunità
Stampa/esporta
Strumenti