Nel precedente articolo abbiamo posto le basi per la creazione procedurale di una mappa utilizzando il Cellular Automata e le sue regole. Riassumendo brevemente abbiamo creato una griglia di celle disposte randomicamente e ci proponiamo di fare evolvere il sistema basandoci su due semplici regole. Supposto che in un dato istante ogni cella può assumere due stati (viva o morta) nell’istante successivo la stessa cella si comporterà nel seguente modo

  • Una cella morta torna in vita se è circondata da 3 vicini
  • Una cella viva sopravvive solo se è circondata da 2 o 3 vicini, altrimenti muore

Abbiamo quindi bisogno di due metodi, uno per gestire la transizione di stato del sistema e un’altro per calcolare quanti vicini ha una data cella. Cominciamo con il secondo metodo che chiameremo getNeighboursCount. Una piccola notazione va fatto sul nome dei metodi e delle variabili e alla scelta dell’inglese per i loro nomi. Conoscere e comprendere l’inglese è la base di ogni programmatore: forum, articoli tecnici, manualistica avanzata è tutta scritta in inglese e privarsi di queste opportunità di conoscenza perché non si comprende l’inglese è veramente un delitto. Ogni programmatore dovrebbe affiancare allo studio dei linguaggi di programmazione, lo studio della lingua inglese. Di più, ogni programmatore dovrebbe abituarsi, a mio parere, a mantenere in lingua inglese anche il suo ambiente di sviluppo.

Ma torniamo al nostro metodo di calcolo dei vicini della cella


private int getNeighboursCount(int x, int y)
{
   int count = 0;
   for (int nx = x-1; nx <= x+1; nx++)
   {
      for (int ny = y - 1; ny &amp;amp;lt;= y + 1; ny++) 
      { 
         if (nx >= 0 && nx < width && ny >= 0 && ny < height)
         {
            if (nx != x || ny != y)
            {
               count += map[nx, ny]?1:0;
            }
         }
         else
         {
            count++;
         }
      }
   }
   return count;
}

Quindi, passando al metodo le coordinate della cella in esame controlliamo lo stato delle celle adiacenti aggiungendo 1 ad una variabile count che memorizza il numero di vicini vivi della cella in esame. Notiamo come nel caso in cui la cella adiacente si trovi fuori dalla griglia viene aggiunto, per coerenza, un vicino vivo al contatore.

A questo punto cicliamo la nostra griglia e applichiamo ad ogni cella le regole del nostro modello introducendo il metodo ExecuteTransaction a cui passeremo un valore intero che rappresenta il numero di volte che vogliamo che il processo venga ripetuto


public void ExecuteTransaction(int iterations)
{
    bool[,] newmap = new bool[width, height];
    for (int iteration = 0; iteration <= iterations; iteration++)
    {
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                int neighbours = getNeighboursCount(x, y);
                if (map[x,y])
                {
                    if (neighbours < 2 || neighbours> 3)
                        newmap[x, y] = false;
                    else
                        newmap[x, y] = true;
                }
                else
                {
                    if (neighbours == 3)
                        newmap[x, y] = true;
                    else
                        newmap[x, y] = false;
                }
            }
        }
    }
    map = newmap;
}

Vediamo ora come si sviluppa il nostro sistema. Creiamo un progetto Windows Form e alla form aggiungiamo una TextBox con la proprietà MultiLine impostata a true, e un Button. Modifichiamo ora il costruttore della nostra form in questo modo:

MapGenerator mg;
public Form1()
{
   InitializeComponent();
   mg = new MapGenerator(100, 30, 40);
   textBox1.Text = mg.DrawMap();
}

Ed infine modifichiamo l’evento collegato al click del Button perchè esegua le transazioni sulla nostra mappa.

private void button1_Click(object sender, EventArgs e)
{
   textBox1.Text = "";
   mg.ExecuteTransaction(5);
   textBox1.Text = mg.DrawMap();
}

All’avvio del programma il costruttore della form istanzia una variabile mg di tipo MapGenerator affinché crei una griglia 100×30 le cui celle abbiano una percentuale di vita iniziale del 40% e produce l’output della griglia stessa all’interno della TextBox. Il risultato sarà più o meno simile a questo

Ancora una mappa dominata dal caos

Ancora una mappa dominata dal caos

Clicchiamo il nostro button e lasciamo che il nostro MapGenerator esegua 5 iterazioni sulla mappa. Oltre a questo l’evento svuoterà la TextBox e ridisegnerà al suo interno la griglia modificata che si presenta ora così

E' diversa ma...

E’ diversa ma…

Le due griglie sono sicuramente diverse ma siamo molto lontani dal risultato che volevamo ottenere. Il modello che abbiamo utilizzato è ancora valido ma dobbiamo evidentemente mettere mano alle regole del gioco della vita se vogliamo raggiungere il nostro obiettivo che è, ricordiamocelo, quello di avere una mappa percorribile. Una cosa però è sicura: il grosso del lavoro ce lo siamo messi alle spalle. Si tratta solo di limare il tutto

 

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *