Dal C al C++/Gioco life di Conway
Di seguito il listato in C++ per un'implementazione testuale del gioco della vita.
#include <iostream>
#include <cstdlib>
#include <vector>
#include <set>
#include <ctime>
//Ho usato quasi solo funzioni "void" perché sono mooolto più belle e perché le funzioni "normali" sono sopravvalutate.
using namespace std;
/*Ho provato a dare dei nomi a tutte le funzioni e variabili che aiutani la spiegazione di ciò che succede all'interno di tale funzione
o a cosa serve tale variabile, molti sono in inglese perché sono più brevi in inglese e perché l'inglese è una lingua più bella.*/
// Osservabile e osservato contemporaneamente :D, gestisce le regole "biologiche"
class Casella{
public: //accessibili dall’esterno e in ogni punto del programma.
enum stato{vuoto=0,pieno}; //Questa è una enumerazioni, essa è esattamente come i tipi fondamentali e composti, un altro "tipo"
//essa nello specifico tratta solo valori che possono essere assunti da una variabile enumerazione e questi
//sono ristretti ad un insieme di valori interi costanti, ad ognuno dei quali viene associato un nome.
typedef std::set<Casella *> Container; //Typedef ha come scopo quello di semplificare la vita, esso infatti,
//a differenza di una dichiarazione standard troppo fastidiosa, riesce a rendere il codice moooolto più riutilizzabile
//tra un'implementazione e un'altra.
typedef Container::iterator Iterator;
private: //utilizzabili soltanto all’interno della classe stessa.
stato stato_;
int vicini_futuri;
int vicini;
Container osservatori;
public:
Casella( const Casella::stato & s = Casella::vuoto ) //const non ha bisogno di essere spiegato.
: 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(){} //Virtual è un pò più difficile da spiegare...è anche questa come int, double, typedef, enum un "tipo" di dichiarazione
//virtual però è diferso dagli altri tipi di dichiarazioni poiché consente di condizionare l’esecuzione del codice
//secondo il tipo dell’istanza oggetto cui si fa riferimento. Scusi se non è molto chiaro ma non ho idea di come spiegarlo...
// Registra gli osservatori a cui notificare i cambiamenti
inline void Registra(Casella& c) { //Riguardo ad "inline" non sono ancora sicuro cosa faccia, ma credo aumenti le prestazioni...
//ma ripeto che non sono sicurissimo su questo fatto, scusi se non sono ferrato ma me l'ha consigliato di usare un amico.
if (&c != this){ //this identifica un puntatore speciale che contiene l’indirizzo dell’istanza della classe
//che ha invocato il metodo.
osservatori.insert(&c);
}
}
inline void Imposta(const Casella::stato& s){
stato_=s;
}
inline void Muori(){
if(stato_==pieno){
Imposta(Casella::vuoto);
Notifica(-1);//Utile per la notifica, vedi dopo.
}
}
inline void Nasci() {
if (stato_==vuoto){
Imposta(Casella::pieno);
Notifica(1);//Utile per la notifica, vedi dopo.
}
}
// 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){ //A fare sta parte ci ho messo 2 giorni.
(*it)->RiceviNotifica(msg);//Per questa infatti mi sono dovuto studiare una libreria intera.
}
}
// Ricevi la notifica dai vicini
inline void RiceviNotifica(int msg) {
vicini_futuri += msg; //Stessa cosa per sta dannata parte
}
};
// Contenitore di caselle, che genera automaticamente le corrispondenze tra vicini.
// Gestisce le regole "topologiche" --> termine preso da wikipedia
class Griglia{
public:
typedef std::vector<Casella> Container;
typedef Container::iterator Iterator;
private: //utilizzabili soltanto all’interno della classe stessa.
int righe_;
int colonne_;
int time;
Container griglia;
public: //accessibili dall’esterno e in ogni punto del programma.
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 per cambiare le regole topologiche di vicinanza
inline void Genera(){ //Scusi se questa parte è un poco incasinata ma non avevo trovato modi per scrivere più "ristretto"
//e comunque in un modo "chiaro e capibile".
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"; //ne abbiamo messi 2 per bellezza
cout << "+--------------------+\n|";
for (int indice=0;indice < 400;++indice){ //Caratteri.
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(); //Comando che è stra utile poiché registra la pressione del tasto, e quindi è stra utile.
}
// Mai raggiunto
return 0;
}