Menu.

In alcuni degli esempi proposti nelle pagine precedenti si è visto come particolare procedure siano attivate da click su bottoni. Ma un'applicazione minimamente articolata deve permettere all'utente di scegliere tra procedure e modalità di esecuzione alternative. Questa capacità di solito è realizzata dotando l'applicazione di una barra di menu, cioè dell'esplicitazione di una lista di possibilità di scelta ognuna mostrata da una stringa cliccando sulla quale si apre una tendina che elenca le varie modalità di esecuzione di quella scelta. Ognuna di queste modalità è, a sua volta, sensibile ai click e, cliccata, genera un evento che, captato dal metodo actionPerformed(ActionEvent e), attiva un appropriato metodo dell'applicazione.

In Java ciò si realizza tramite l'uso della classe JMenuBar. Si crea un oggetto, ad es. barra_menu di questa classe per popolarlo con oggetti di classe JMenu.
Ogni oggetto di classe JMenu a sua volta va popolato con uno o più oggetti di classe JMenuItem. Questi ultimi oggetti sono quelli effettivamente attivi nel controllo dell'applicazione. Ad ognuno di essi il programmatore associa un ActionCommand da emettere quando l'item viene cliccato. Sarà compito della funzione actionPerformed rilevare l'evento e attivare l'opportuna procedura.
Una volta preparata la barra del menu la si assegna al suo contenitore con l'istruzione setMenuBar(JMenuBar barra_menu).

Come esempio di questa metodologia si propone l'applicazione Vita, uno dei primi esempi di automi cellulari.

Vita è una simulazione, proposta nel 1970 dal matematico J. H. Conway, dell'evoluzione di una colonia di semplici organismi viventi (ad esempio cellule) dotati dell'unica proprietà di nascere o morire.

Lo spazio vitale è rappresentato da una matrice bidimensionale di celle in ognuna delle quali può vivere un organismo.

L'evoluzione del sistema avviene secondo le seguenti regole:

In rete ci sono numerosissime versioni di Vita (immettere in un motore di ricerca le parole chiave "Conway life"). È didatticamente interessante il sito math.com

Nell'implementazione di Vita proposta nell'applicazione seguente, per ottenere che ogni cella sia circondata da otto celle, la matrice è circolare, cioè le celle della prima riga sono adiacenti a quelle dell'ultima (e viceversa). Lo stesso vale per le celle della prima e dell'ultima colonna.

La realizzazione grafica dell'applicazione si vuole che sia la seguente.

fig01.gif


La classe Vita è derivata dalla classe JFrame, e deve captare i segnali del mouse. Dunque va dichiarata nel seguente modo:

public class Vita
extends JFrame
implements ActionListener

La grafica presenta tre sezioni:

L'applicazione deve inoltre ricalcolare lo stato del sistema e ripresentarlo graficamente ad intervalli regolari. La si dota quindi di un temporizzatore, cioè di un oggetto di classe Timer sincronizzato sull'intervallo voluto espresso in millisecondi.

Bisogna quindi che la classe Vita crei oggetti in grado di codificare queste entità e i loro contenuti.

Saranno inoltre necessari campi per contenere i parametri globali dell'applicazione.

Il costruttore Vita() può essere

  public Vita()
    {
      setTitle("Vita");
      n_righe = 20;
      n_colonne = 20;
      lato = misuraLato();
      n_vive = 0;
      n_iniziali = n_righe*n_colonne/2;
      distribuzione = false;
      evoluzione = false;
      intervallo = 500;
      vicina = new int[8][2];

      timer = new Timer(intervallo, this);
      jmb = creaMenu();
      setJMenuBar(jmb);

      client = getContentPane();

      Font font = new java.awt.Font("Verdana",Font.PLAIN,10);
      JPanel messaggi = new JPanel();
      messaggi.setLayout(new BoxLayout(messaggi,BoxLayout.X_AXIS));
      mess_stato = new Messaggio("Stato");
      lb_stato = new JLabel("stato",JLabel.CENTER);
      lb_stato.setFont(font);
      mess_stato.add(lb_stato);
      
      mess_vive = new Messaggio("Celle vive");
      lb_vive = new JLabel("vive",JLabel.CENTER);
      lb_vive.setFont(font);
      mess_vive.add(lb_vive);

      messaggi.add(mess_stato);
      messaggi.add(mess_vive);

      pannello = new Pannello(n_righe,n_colonne,lato);
      pannello.addMouseListener
        (
          new MouseAdapter()
            {
              public void mousePressed(MouseEvent e)
                {
                  if (distribuzione)
                    {
                      statoCella(e.getX(),e.getY());
                    }
                }
            }
        );

      Border vuoto = BorderFactory.createEmptyBorder(8,8,8,8);
      sfondo = new Sfondo();
      sfondo.setBorder(vuoto);
      sfondo.setLayout(new BoxLayout(sfondo,BoxLayout.Y_AXIS));
      sfondo.add(messaggi);
      sfondo.add(Box.createRigidArea(new Dimension(0,4)));
      sfondo.add(pannello);
      client.add(sfondo);
    }

La creazione della barra del menù è espressa dall'istruzione setJMenuBar(jmb); jmb è definita dalla funzione creaMenu().

  JMenuBar creaMenu()
    {
      JMenuBar jmb = new JMenuBar();
      jmb.setBackground(Color.cyan);

// prima voce nella JMenuBar      
      JMenu m_parametri = new JMenu("Parametri");
      m_parametri.setBackground(Color.cyan);
      m_parametri.setMnemonic(KeyEvent.VK_P); 
// primo item della prima voce
      JMenuItem mi_n_righe = new JMenuItem("N. di Righe",KeyEvent.VK_R);
      mi_n_righe.setActionCommand("11");
      mi_n_righe.addActionListener(this);
      m_parametri.add(mi_n_righe);
// secondo item della prima voce
      JMenuItem mi_n_colonne = new JMenuItem("N. di Colonne",KeyEvent.VK_C);
      mi_n_colonne.setActionCommand("12");
      mi_n_colonne.addActionListener(this);
      m_parametri.add(mi_n_colonne);
// terzo item della prima voce
      JMenuItem mi_n_vive = new JMenuItem("Celle inizialmente vive",KeyEvent.VK_V);
      mi_n_vive.setActionCommand("13");
      mi_n_vive.addActionListener(this);
      m_parametri.add(mi_n_vive);
// quarto item della prima voce
      JMenuItem mi_intervallo = new JMenuItem("Intervallo (ms) ",KeyEvent.VK_I);
      mi_intervallo.setActionCommand("14");
      mi_intervallo.addActionListener(this);
      m_parametri.add(mi_intervallo);
      
      jmb.add(m_parametri);

// Seconda voce nella JMenuBar 
      JMenu m_inizio = new JMenu("Inizio");
      m_inizio.setBackground(Color.cyan);
      m_inizio.setMnemonic(KeyEvent.VK_I);

      JMenuItem mi_casuale = new JMenuItem("Casuale",KeyEvent.VK_C);
      mi_casuale.setActionCommand("21");
      mi_casuale.addActionListener(this);
      m_inizio.add(mi_casuale);

      JMenuItem mi_manuale = new JMenuItem("Manuale",KeyEvent.VK_M);
      mi_manuale.setActionCommand("22");
      mi_manuale.addActionListener(this);
      m_inizio.add(mi_manuale); 

      jmb.add(m_inizio);

// Terza voce nella JMenuBar
      JMenu m_evoluzione = new JMenu("Evoluzione");
      m_evoluzione.setBackground(Color.cyan);
      m_evoluzione.setMnemonic(KeyEvent.VK_E);

      JMenuItem mi_avvio = new JMenuItem("Avvio",KeyEvent.VK_A);
      mi_avvio.setActionCommand("31");
      mi_avvio.addActionListener(this);
      m_evoluzione.add(mi_avvio);

      JMenuItem mi_stop = new JMenuItem("Stop",KeyEvent.VK_S);
      mi_stop.setActionCommand("32");
      mi_stop.addActionListener(this);
      m_evoluzione.add(mi_stop);

      jmb.add(m_evoluzione);

      return jmb;
    }

Ogni JMenu è creato da new JMenu passando come argomento la stringa da visualizzare e associandogli la lettere che apparirà sottolineata.

Ognuno dei JMenuItem è creato da new JMenuItem assegnandogli:

Il costruttore prevede la classe Messaggio che viene successivamente definita come classe interna.

class Messaggio
extends JPanel

{
  Border blackline;
  TitledBorder border;
 
  Messaggio(String intestazione)
    {
      Font font = new java.awt.Font("Verdana",Font.PLAIN,10);
      blackline = BorderFactory.createLineBorder(Color.black);
      border = BorderFactory.createTitledBorder(blackline,intestazione);
      border.setTitleJustification(TitledBorder.CENTER);
      border.setTitlePosition(TitledBorder.DEFAULT_POSITION);
      border.setTitleFont(font);
      setBorder(border);
      setBackground(Color.yellow);
      setLayout(new FlowLayout(FlowLayout.CENTER,0,0));
      setPreferredSize(new Dimension(40,36));
    }
}

Il costruttore prevede la classe Pannello che viene successivamente definita come classe interna.
Pannello è la zona grafica che rappresenta tutte le celle ad ogni passo dell'evoluzione della simulazione.
Se l'inizializzazzione della simulazione viene fatta manualmente dall'utente, Pannello deve essere sensibile ai click del mouse su di esso in modo tale che, se una cella vuota viene cliccata diventi viva e viceversa. Per questo viene dotato di un MouseAdapter attivo solo nella fase di inizializzazione manuale che invoca la funzione statoCella() che andrà opportunamente redatta.
Pannello deve essere dotato di un metodo paint(Graphics g) attivato ad intervalli prestabiliti dal Timer.

class Pannello
extends JPanel
{
  int n_c, n_r,l;
  Pannello(int nc, int nr, int lato)
    {
      setPreferredSize(new Dimension(360,360));
      n_c=nc;
      n_r=nr;
      l = lato;
    }    

//----------------------------------------------------------------------
  public void paint(Graphics g)
    {
      Casella c;
      Rectangle r;
                                
      setBackground(Color.green);
      super.paintComponent(g);

      if (schema ==null) return;

      for (int i=0;i<n_c;i++)
        for (int j=0;j<n_r;j++)
          {
            c = schema.s[i][j];
            g.setColor(Color.black);
            r = c.r;
            g.drawRoundRect(r.x,r.y,r.width,r.height,l/2,l/2);
            if (c.viva) g.setColor(Color.red);
            else g.setColor(Color.white);
            g.fillRoundRect(r.x+1,r.y+1,r.width-2,r.height-2,l/2,l/2);
          }
    }

//----------------------------------------------------------------------
  void setParametri(int nc, int nr, int lato)
    {
      n_c = nc;
      n_r = nr;
      l = lato;
    }
}

Pannello richiede a sua volta la classe Casella che viene successivamente definita come classe interna.

class Casella
  {
    boolean viva,futuro;
    Rectangle r;

//----------------------------------------------------------------------
    Casella(int i, int j, int l)
      {
        viva = false;
        futuro = false;
        r = new Rectangle(i*l,j*l,l,l);
      }
  }

Vita richiede anche la classe Schema che viene successivamente definita come classe interna.
Anche Schema richiede la classe Casella.

class Schema
{
  int n_c, n_r, l;
  Casella[][] s;

//----------------------------------------------------------------------
  Schema(int nc, int nr, int lt)
    {
      n_c = nc;
      n_r = nr;
      l = lt;
      s = new Casella[nc][nr];
      for (int i=0;i<nc;i++)
        for (int j=0;j<nr;j++)
          s[i][j]=new Casella(i,j,l);
    }
}

Per reagire ai click sugli item del menù e agli impulsi del Timer, Vita deve essere dotata di una funzione actionPerformed(ActionEvent e).


  public void actionPerformed(ActionEvent e)
    {
      String azione = e.getActionCommand();
      if (azione==null)
        {
// evento Timer
          if (evoluzione)
            scenario();
        }
      else
        {
// eventi da menu
          int az = Integer.parseInt(azione);
          if (az < 20)
            {
              String aux$;
              int aux;
              switch (az)
                {
                  case 11:
                    aux$ = String.valueOf(n_righe);
                    aux$ = JOptionPane.showInputDialog(this,"Numero di righe?",aux$);
                    aux  = leggeIntero(aux$);
                    if (aux>0) n_righe=aux;
                    if (n_righe>MAX_DIM) n_righe=MAX_DIM;
                    break;
                  case 12:
                    aux$ = String.valueOf(n_colonne);
                    aux$ = JOptionPane.showInputDialog(this,"Numero di colonne?",aux$);
                    aux  = leggeIntero(aux$);
                    if (aux>0) n_colonne=aux;
                    if (n_colonne>MAX_DIM) n_colonne=MAX_DIM;
                    break;
                  case 13:
                    aux$ = String.valueOf(n_iniziali);
                    aux$ = JOptionPane.showInputDialog(this,"Celle inizialmente vive",aux$);
                    aux  = leggeIntero(aux$);
                    if (aux>0) n_iniziali=aux;
                    break;
                  case 14:
                    aux$ = String.valueOf(intervallo);
                    aux$ = JOptionPane.showInputDialog(this,"Intervallo? (ms)",aux$);
                    aux  = leggeIntero(aux$);
                    if (aux>0) intervallo=aux;
                    timer.setDelay(intervallo);
                }
              pannello.setParametri(n_colonne, n_righe, lato);
            }            
          else
            switch(az)
              {
                case 21:
                  inizioCasuale();
                  break;
                case 22:
                  inizioManuale();
                  break;
                case 31:
                  animazione();
                  break;
                case 32:
                  stopAnimazione();
              }
      
        }
    }

I casi 11-12-13-14 di questo metodo invocano la funzione JOptionPane.showInputDialog, metodo della classe JOptionPane che genera sullo schermo una finestra interattiva che permette all'utente di specificare da tastiera il valore di un parametro.

Il metodo principale della classe Vita è scenario() invocato dal timer nella fase di evoluzione della simulazione. scenario() provvede a ridisegnare l'output grafico e a calcolare lo stato successivo di ogni singola cella secondo le regole di Conway.

  public void scenario()
    {  
      Casella c;
      int i,j, v;
      
      pannello.repaint();

      conta++;
      n_vive = 0;
      for (i=0; i<n_colonne; i++)
        for (j=0; j<n_righe; j++)
          {
            schema.s[i][j].viva = schema.s[i][j].futuro;
            if (schema.s[i][j].viva) n_vive++;
          };
      for (i=0; i<n_colonne; i++)
        for (j=0; j<n_righe; j++ )
          {
            v = sommaVicine(i,j);
            c = schema.s[i][j];
            if (c.viva)
              {
                if ((v!=2)&&(v!=3))
                  schema.s[i][j].futuro = false;
              }
            else
              {
                if (v==3)
                  schema.s[i][j].futuro = true;
              };
          };
      lb_vive.setText(" "+n_vive);
      lb_stato.setText(" "+conta);
    }

Un passo dell'applicazione.

fig02.gif

 


Il testo completo del sorgente.

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.border.*;

/***********************************************************************************/
public class Vita
extends JFrame
implements ActionListener
{
  static final int MAX_DIM = 50;
  Container client;
  Timer timer;
  JMenuBar jmb;
  Pannello pannello;
  Sfondo sfondo;
  JPanel messaggi;
  Messaggio mess_stato, mess_vive;
  JLabel lb_stato, lb_vive;
  int lato, n_righe, n_colonne, n_vive, n_iniziali, intervallo, conta;
  boolean distribuzione, evoluzione;
  Schema schema;
  int[][] vicina;

//=====================================================================

  Vita()
    {
      setTitle("Vita");
      n_righe = 20;
      n_colonne = 20;
      lato = misuraLato();
      n_vive = 0;
      n_iniziali = n_righe*n_colonne/2;
      distribuzione = false;
      evoluzione = false;
      intervallo = 500;
      vicina = new int[8][2];

      timer = new Timer(intervallo, this);
      jmb = creaMenu();
      setJMenuBar(jmb);

      client = getContentPane();

      Font font = new java.awt.Font("Verdana",Font.PLAIN,10);
      JPanel messaggi = new JPanel();
      messaggi.setLayout(new BoxLayout(messaggi,BoxLayout.X_AXIS));
      mess_stato = new Messaggio("Stato");
      lb_stato = new JLabel("stato",JLabel.CENTER);
      lb_stato.setFont(font);
      mess_stato.add(lb_stato);
      
      mess_vive = new Messaggio("Celle vive");
      lb_vive = new JLabel("vive",JLabel.CENTER);
      lb_vive.setFont(font);
      mess_vive.add(lb_vive);

      messaggi.add(mess_stato);
      messaggi.add(mess_vive);

      pannello = new Pannello(n_righe,n_colonne,lato);
      pannello.addMouseListener
        (
          new MouseAdapter()
            {
              public void mousePressed(MouseEvent e)
                {
                  if (distribuzione)
                    {
                      statoCella(e.getX(),e.getY());
                    }
                }
            }
        );

      Border vuoto = BorderFactory.createEmptyBorder(8,8,8,8);
      sfondo = new Sfondo();
      sfondo.setBorder(vuoto);
      sfondo.setLayout(new BoxLayout(sfondo,BoxLayout.Y_AXIS));
      sfondo.add(messaggi);
      sfondo.add(Box.createRigidArea(new Dimension(0,4)));
      sfondo.add(pannello);
      client.add(sfondo);
    }

//----------------------------------------------------------------------
  JMenuBar creaMenu()
    {
      JMenuBar jmb = new JMenuBar();
      jmb.setBackground(Color.cyan);
      
      JMenu m_parametri = new JMenu("Parametri");
      m_parametri.setBackground(Color.cyan);
      m_parametri.setMnemonic(KeyEvent.VK_P); 

      JMenuItem mi_n_righe = new JMenuItem("N. di Righe",KeyEvent.VK_R);
      mi_n_righe.setActionCommand("11");
      mi_n_righe.addActionListener(this);
      m_parametri.add(mi_n_righe);

      JMenuItem mi_n_colonne = new JMenuItem("N. di Colonne",KeyEvent.VK_C);
      mi_n_colonne.setActionCommand("12");
      mi_n_colonne.addActionListener(this);
      m_parametri.add(mi_n_colonne);

      JMenuItem mi_n_vive = new JMenuItem("Celle inizialmente vive",KeyEvent.VK_V);
      mi_n_vive.setActionCommand("13");
      mi_n_vive.addActionListener(this);
      m_parametri.add(mi_n_vive);

      JMenuItem mi_intervallo = new JMenuItem("Intervallo (ms) ",KeyEvent.VK_I);
      mi_intervallo.setActionCommand("14");
      mi_intervallo.addActionListener(this);
      m_parametri.add(mi_intervallo);
      
      jmb.add(m_parametri);
 
      JMenu m_inizio = new JMenu("Inizio");
      m_inizio.setBackground(Color.cyan);
      m_inizio.setMnemonic(KeyEvent.VK_I);

      JMenuItem mi_casuale = new JMenuItem("Casuale",KeyEvent.VK_C);
      mi_casuale.setActionCommand("21");
      mi_casuale.addActionListener(this);
      m_inizio.add(mi_casuale);

      JMenuItem mi_manuale = new JMenuItem("Manuale",KeyEvent.VK_M);
      mi_manuale.setActionCommand("22");
      mi_manuale.addActionListener(this);
      m_inizio.add(mi_manuale); 

      jmb.add(m_inizio);

      JMenu m_evoluzione = new JMenu("Evoluzione");
       m_evoluzione.setBackground(Color.cyan);
      m_evoluzione.setMnemonic(KeyEvent.VK_E);

      JMenuItem mi_avvio = new JMenuItem("Avvio",KeyEvent.VK_A);
      mi_avvio.setActionCommand("31");
      mi_avvio.addActionListener(this);
      m_evoluzione.add(mi_avvio);

      JMenuItem mi_stop = new JMenuItem("Stop",KeyEvent.VK_S);
      mi_stop.setActionCommand("32");
      mi_stop.addActionListener(this);
      m_evoluzione.add(mi_stop);

      jmb.add(m_evoluzione);

      return jmb;
    }

//-----------------------------------------------------------------------
  public void statoCella(int X, int Y)
    {
      Insets insets = pannello.getInsets();

      int x=X-insets.left;
      int y=Y-insets.top;
      int i=x/lato, j=y/lato;
      if ((i>n_colonne-1)||(j>n_righe-1)) return;
      schema.s[i][j].viva = !schema.s[i][j].viva;
      if (schema.s[i][j].viva) n_vive++;
      else n_vive--;
      distribuzione = (n_vive<n_iniziali);
      lb_vive.setText(" "+n_vive);
      lb_stato.setText(" "+conta);
      sfondo.repaint();
    }


//-------------------------------------------------------------------------
  public void stopAnimazione()
    {
      timer.stop();
      evoluzione = false;
    }

//---------------------------------------------------------------
  int leggeIntero(String s)
    {
      int result=0;
      if (s==null) return 0;
      try
        {
          result = Integer.parseInt(s);
        }
      catch(ArithmeticException e)
        {
          result = 0;
        };
      return result;
    }

//---------------------------------------------------------------
  public void actionPerformed(ActionEvent e)
    {
      String azione = e.getActionCommand();
      if (azione==null)
        {
          if (evoluzione)
            scenario();
        }
      else
        {
          int az = Integer.parseInt(azione);
          if (az < 20)
            {
              String aux$;
              int aux;
              switch (az)
                {
                  case 11:
                    aux$ = String.valueOf(n_righe);
                    aux$ = JOptionPane.showInputDialog(this,"Numero di righe?",aux$);
                    aux  = leggeIntero(aux$);
                    if (aux>0) n_righe=aux;
                    if (n_righe>MAX_DIM) n_righe=MAX_DIM;
                    break;
                  case 12:
                    aux$ = String.valueOf(n_colonne);
                    aux$ = JOptionPane.showInputDialog(this,"Numero di colonne?",aux$);
                    aux  = leggeIntero(aux$);
                    if (aux>0) n_colonne=aux;
                    if (n_colonne>MAX_DIM) n_colonne=MAX_DIM;
                    break;
                  case 13:
                    aux$ = String.valueOf(n_iniziali);
                    aux$ = JOptionPane.showInputDialog(this,"Celle inizialmente vive",aux$);
                    aux  = leggeIntero(aux$);
                    if (aux>0) n_iniziali=aux;
                    break;
                  case 14:
                    aux$ = String.valueOf(intervallo);
                    aux$ = JOptionPane.showInputDialog(this,"Intervallo? (ms)",aux$);
                    aux  = leggeIntero(aux$);
                    if (aux>0) intervallo=aux;
                    timer.setDelay(intervallo);
                }
              pannello.setParametri(n_colonne, n_righe, lato);
            }            
          else
            switch(az)
              {
                case 21:
                  inizioCasuale();
                  break;
                case 22:
                  inizioManuale();
                  break;
                case 31:
                  animazione();
                  break;
                case 32:
                  stopAnimazione();
              }
      
        }
    } 

//-------------------------------------------------------------------
  int nCasuale(int max)
    {
      return((int)Math.round(Math.floor(max*Math.random())));
    }

//-------------------------------------------------------------------
  int misuraLato()
    {
      int lc = 360/n_colonne;
      int lr = 360/n_righe;
      int ris=lc;
      if (lrn_righe*n_colonne)
        {
          JOptionPane.showMessageDialog(this,"Numero di cellule eccessivo. Diminuire.");
          return;
        };
      distribuzione=false;
      
      nuovoSchema();
      do
        {
          i = nCasuale(n_colonne);
          j = nCasuale(n_righe);
          if ( !schema.s[i][j].viva)
            {
              schema.s[i][j].viva=true;
              n_vive++;
            }
        } while(n_viven_righe*n_colonne)
        {
          JOptionPane.showMessageDialog(this,"Numero di cellule eccessivo. Diminuire.");
          return;
        };
      evoluzione = true;
      distribuzione=false;
      for (int i=0; i<n_colonne; i++)
        for (int j=0; j<n_righe; j++) schema.s[i][j].futuro = schema.s[i][j].viva;
      sfondo.setVisible(true);
      sfondo.repaint();
      conta = -1;
      timer.setInitialDelay(0);
      timer.start();
    }

//-------------------------------------------------------------------------------
  public void scenario()
    {  
      Casella c;
      int i,j, v;
      
      pannello.repaint();

      conta++;
      n_vive = 0;
      for (i=0; i<n_colonne; i++)
        for (j=0; j<n_righe; j++)
          {
            schema.s[i][j].viva = schema.s[i][j].futuro;
            if (schema.s[i][j].viva) n_vive++;
          };
      for (i=0; i<n_colonne; i++)
        for (j=0; j<n_righe; j++ )
          {
            v = sommaVicine(i,j);
            c = schema.s[i][j];
            if (c.viva)
              {
                if ((v!=2)&&(v!=3))
                  schema.s[i][j].futuro = false;
              }
            else
              {
                if (v==3)
                  schema.s[i][j].futuro = true;
              };
          };
      lb_vive.setText(" "+n_vive);
      lb_stato.setText(" "+conta);
    }

//---------------------------------------------------------------
  int sommaVicine(int i, int j)
    {

/*
Data la cella (i,j), si girano le celle circostanti a partire da quella
in alto a sinistra
  012
  7 3
  654
*/

      int cs,cd,ri,rs,k, result = 0;
      cs = (i == 0)? n_colonne-1: i-1;
      cd = (i == n_colonne-1)? 0: i+1;
      ri = (j == 0)? n_righe-1  : j-1;
      rs = (j == n_righe-1)?   0: j+1;
      vicina[0][0] = ri;   vicina[0][1] = cs;  //riga inf. col. sin.
      vicina[1][0] = ri;   vicina[1][1] = i;   //riga inf. col. stessa
      vicina[2][0] = ri;   vicina[2][1] = cd;  //riga inf. col. destra
      vicina[3][0] = j;    vicina[3][1] = cd;  //stessa riga, col. destra
      vicina[4][0] = rs;   vicina[4][1] = cd;  //riga sup. col. destra
      vicina[5][0] = rs;   vicina[5][1] = i;
      vicina[6][0] = rs;   vicina[6][1] = cs;
      vicina[7][0] = j;    vicina[7][1] = cs;
      for (k=0; k<8; k++)
        if (schema.s[vicina[k][1]][vicina[k][0]].viva) result++;
      return(result);
    }

//----------------------------------------------------------------------
  public static void main(String args[])
    {
      Vita vita = new Vita();
      vita.pack();
      vita.setVisible(true);
    }

//=====================================================================
class Casella
  {
    boolean viva,futuro;
    Rectangle r;

//----------------------------------------------------------------------
    Casella(int i, int j, int l)
      {
        viva = false;
        futuro = false;
        r = new Rectangle(i*l,j*l,l,l);
      }
  }
//=====================================================================
class Schema

{
  int n_c, n_r, l;
  Casella[][] s;

//----------------------------------------------------------------------
  Schema(int nc, int nr, int lt)
    {
      n_c = nc;
      n_r = nr;
      l = lt;
      s = new Casella[nc][nr];
      for (int i=0;i<nc;i++)
        for (int j=0;j<nr;j++)
          s[i][j]=new Casella(i,j,l);
    }
}

//=====================================================================
class Pannello
extends JPanel
{
  int n_c, n_r,l;
  Pannello(int nc, int nr, int lato)
    {
      setPreferredSize(new Dimension(360,360));
      n_c=nc;
      n_r=nr;
      l = lato;
    }    

//----------------------------------------------------------------------
  public void paint(Graphics g)
    {
      Casella c;
      Rectangle r;
                                
      setBackground(Color.green);
      super.paintComponent(g);

      if (schema ==null) return;

      for (int i=0;i<n_c;i++)
        for (int j=0;j<n_r;j++)
          {
            c = schema.s[i][j];
            g.setColor(Color.black);
            r = c.r;
            g.drawRoundRect(r.x,r.y,r.width,r.height,l/2,l/2);
            if (c.viva) g.setColor(Color.red);
            else g.setColor(Color.white);
            g.fillRoundRect(r.x+1,r.y+1,r.width-2,r.height-2,l/2,l/2);
          }
    }

//----------------------------------------------------------------------
  void setParametri(int nc, int nr, int lato)
    {
      n_c = nc;
      n_r = nr;
      l = lato;
    }
}

//=====================================================================
class Sfondo
extends JPanel

{
  Sfondo()
    {
      setPreferredSize(new Dimension(420,420));
      setBackground(Color.green);
    }
}
//=====================================================================
class Messaggio
extends JPanel

{
  Border blackline;
  TitledBorder border;
 
  Messaggio(String intestazione)
    {
      Font font = new java.awt.Font("Verdana",Font.PLAIN,10);
      blackline = BorderFactory.createLineBorder(Color.black);
      border = BorderFactory.createTitledBorder(blackline,intestazione);
      border.setTitleJustification(TitledBorder.CENTER);
      border.setTitlePosition(TitledBorder.DEFAULT_POSITION);
      border.setTitleFont(font);
      setBorder(border);
      setBackground(Color.yellow);
      setLayout(new FlowLayout(FlowLayout.CENTER,0,0));
      setPreferredSize(new Dimension(40,36));
    }
}
}

Il sorgente può essere scaricato da Vita.zip e scompattato in una cartella del proprio sistema.