Ci eravamo lasciati con la prima parte riguardante l'argomento vettori (array), nella lezione numero 14 ma, soprattutto, vi avevo promesso che avrei continuato il discorso facendovi un esempio di come utilizzarli in una applicazione (a console) ed in particolar modo scrivendo un programmino che potevamo usare per giocare al SuperEnalotto. Bene. E' arrivato il momento di mettere mano al codice. Prima di iniziare è necessaria una piccola premessa:

"Per vincere al SuperEnalotto, è necessario avere solo ed esclusivamente fortuna! Non esistono formule magiche. Il programmino che faremo è solo a scopo didattico e quindi se non si ha fortuna..."

Poco tempo fa mi ero dilettato a progettare l'applicazione anche per il mio PDA (palmare con Windows Mobile 5) sempre scritta in C# e funzionava molto bene perché avevo previsto anche il gioco del Lotto. Era mia intenzione di fornire a tutti coloro che erano interessati questa piccola applicazione per PDA, ma in una fredda giornata di Gennaio il mio palmare mi ha detto addio e, in compenso, ho perso tutto il codice sorgente memorizzato sul mio PC. Non dispero di poter resuscitare il palmare e se dovessi riuscirci, vi informerò in qualche modo.

Le esequie per il mio PDA sono terminate ed è ora di fare le cose un po' più serie.

Come saprete il gioco del SuperEnalotto basa i suoi numeri sulle estrazioni delle prime sei ruote del lotto. Questo però ci interessa ben poco in quanto il programma dovrà "semplicemente" generare una sequenza casuale di quei sei numeri. Il problema è che questa sequenza non deve avere numeri ripetuti ovviamente, e si dovranno "estrarre" sei numeri a caso sui novanta canonici del Lotto, cioè da 1 a 90.

Ma cosa c'entrano i vettori in tutto questo?

E' necessario che un numero estratto non lo sia già stato, e per ogni estrazione (casuale) il controllo deve essere ripetuto fino a quando non vengono generati sei numeri differenti. L'uso di un vettore di 90 elementi, inizializzato tutto a zero, mi è stato di grande aiuto perché ad ogni estrazione vado a collocare l'estratto nel vettore nel punto esatto indicizzato dal numero stesso-1. Se e solo se la locazione puntata dall'estratto-1 risulta contenere il valore zero, allora lo inserisco nel vettore, altrimenti ne genero uno nuovo ed il controllo viene ripetuto. Tutto questo fino a quando non inserisco tutti e sei numeri estratti.

Vi ricordo a tal proposito che un vettore indicizza i suoi elementi con base zero; cioè il primo elemento ha indice 0, il secondo ha indice 1, e così via. Quindi i 90 numeri all'interno del vettore vanno indicizzati da 0 a 89.

ESEMPIO:

Numero estratto casualmente = 35

indice

0

1

...

33

34

35

...

86

87

88

89

Vettore

0

0

...

0

0

0

...

0

0

0

0


controllo se il contenuto del vettore puntato dall'indice dell'estratto-1 (34), contiene il valore 0. In questo primo esempio effettivamente il contenuto è 0, quindi inserisco l'estratto.

indice

0

1

...

33

34

35

...

86

87

88

89

Vettore

0

0

...

0

35

0

...

0

0

0

0


Se il successivo numero estratto dovesse essere nuovamente 35, il controllo darebbe esito positivo (è già stato estratto) e si procede con un nuovo numero casuale.

Avrei potuto utilizzare sei variabili e controllare se ognuna di esse conteneva l'estratto, ma credo che il metodo precedente sia più veloce e con meno controlli. Qualcuno potrebbe pensare che l'uso di un vettore per tenere traccia dei 6 estratti sia esagerato. A pensarci bene, no! Il motivo è che posso tenere traccia degli estratti per ben 15 giocate. Infatti 6 x 15 = 90. Quindi se volessi giocare per 15 volte in un solo colpo, non avrò sicuramente estratti ripetuti ed ogni volta che lo farò, ho molta probabilità di non avere nessuna sestina identica alle 15 precedenti. Quest'ultima ipotesi di gioco deve essere affrontata quando verranno studiate le matrici.

Questo semplice "algoritmo" l'ho inventato io, ma, se qualcuno di voi ha una idea migliore e più veloce, non esiti a farmelo sapere ne sarò felice.

Finalmente, dopo le chiacchiere, si parla di C#.

Per prima cosa è necessario generare un numero casuale appartenente all'insieme dei numeri del lotto, da 1 a 90. Il linguaggio mette a disposizione una classe ad hoc per fare ciò, la classe

Random

con la quale creeremo un oggetto che chiameremo rnd:

Random rnd= new Random();

attraverso il quale abbiamo la possibilità di generare un numero casuale. L'oggetto così creato può fare utilizzo di tre metodi principali, questi:

Nome metodo

Descrizione

Next()

Restituisce un numero casuale non negativo.

Next(Int32)

Restituisce un numero casuale non negativo inferiore al massimo specificato.

Next(Int32, Int32)

Restituisce un numero casuale all'interno di un intervallo specificato.

Per il nostro programmino, faremo uso del terzo metodo il quale dovrà generare un numero casuale appartenente all'intervallo 1-90. Quindi richiameremo questo metodo in questo modo:

rnd.Next(0,90);

che ritorna un numero intero a 32 bit appartenente all'intervallo.

L'intervallo così dichiarato non contempla però l'estremo superiore, cioè, verrà generato un numero intero da 0 a 89 che utilizzeremo per indicizzare proprio il nostro vettore per il controllo di cui sopra. Nel momento in cui si inserirà il numero nel vettore, il medesimo verrà incrementato di una unità (per avere poi gli esatti estratti da 1 a 90). L'alternativa sarebbe stata anche quella di far generare l'estratto nell'intervallo 1 a 90 per poi utilizzarlo decrementato di uno al momento dell'indicizzazione del vettore.

Il vettore che utilizzeremo, dovrà contenere gli estratti e considerando che i numeri del lotto non superano la cifra 90, credo che non c'è alcun bisogno di implementare un vettore che contenga degli interi a 32 bit (ognuno dei quali occupa ben 4 byte) ma si potrebbe dichiarare un vettore di tipo byte nel seguente modo:

byte[] estratti = new byte[90];

che potrà contenere interi a 8 bit (1 byte, cioè cifre da 0 a 256) con un notevole risparmio di memoria (lascio a voi il calcolo come esercizio).

Una volta dichiarato il vettore, è bene azzerarlo e lo faremo con il seguente metodo:

       static void Azzera_vettore()
       {
            //ogni elemento del vettore
            //viene inizializzato a 0
             for(byte i = 0; i < 90; i++)
                 estratti[i]= 0;
       }

Il passo successivo è un metodo che finalmente genera i 6 numeri:

      static void Genera()
      {
          // variabile contatore
          byte i = 0;
          // variabile che contiene l'estratto
          byte numero = 0;

// viene generato un numero intero
          // che deve essere sottoposto a
          // casting* perché il vettore contiene
          // valori di tipo byte
          numero = (byte)rnd.Next(0,90);

          // ciclo che controlla l'esistenza
          // dell'estratto per sei volte (da 0 a 5)
          while (i < 6)
          {
              if (estratti[numero] == 0)
              {
                 // se la locazione del vettore
                 // puntata dall'estratto contiene
                 // il valore 0, allora lo inserisce
                 // nel vettore incrementato di 1 e 
                 // anch'esso deve essere sottoposto a
                 // casting* perché l'incremento risulta
                 // intero
                 estratti[numero] = (byte)(numero+1);
                 // ed incrementa il contatore 
                 i++;
              }
              // genera un nuovo numero
              numero = (byte)rnd.Next(0, 90);
          }
      }

*Cosa significa casting?

Il casting è una operazione attraverso la quale abbiamo l'opportunità di convertire il tipo di una determinata variabile in un altro. Nel metodo Genera(), si è fatto uso del casting per convertire una cifra di tipo int ritornata dal metodo rnd.Next(0,90), in un tipo byte con cui abbiamo dichiarato la variabile numero:

Una scrittura del genere effettua la conversione da un tipo int (a 32 bit) ad un tipo byte (a 8 bit).

Un esempio:

...
...
int numero1=38;
byte numero2=0;
...
numero2=(byte)numero1;
...

Graficamente abbiamo in memoria una situazione del genere:

rappresentazione binaria del numero intero 38 della variabile numero1 di tipo int (32 bit):

rappresentazione binaria della variabile numero2 di tipo byte (8 bit):

Dopo l'operazione di casting, la situazione della variabile numero2 sarà:

cioè sono stati "copiati" solo i primi 8 bit della variabile intera numero1 nella seconda variabile numero2 eliminando i restanti 24 bit. Un altro esempio:

...
...
int numero1=256;
byte numero2=0;
...
...
numero2=(byte)numero1;
...

Graficamente abbiamo in memoria una situazione del genere:

rappresentazione binaria del numero intero 258 della variabile numero1 di tipo int (32 bit):

rappresentazione binaria della variabile numero2 di tipo byte (8 bit):

Dopo l'operazione di casting, la situazione della variabile numero2 sarà:

cioè conterrà il valore 2. Questa situazione non è ovviamente accettabile perché vengono persi dei dati. Non è stato memorizzato il valore originario, infatti i primi 8 bit della variabile numero1 rappresentano appunto il valore 2, non 258.

Morale della favola:
"Non si abusi del casting perché può avere effetti collaterali sui risultati che non saranno veritieri alterando così le informazioni elaborate. Utilizzarlo laddove i valori persi non siano significativi, come per esempio un arrotondamento di valori con parte decimale in calcoli che non richiedono una precisione estrema."

Nel nostro programmino del SuperEnalotto, non abbiamo problemi di sorta in quanto i numeri generati rientrano perfettamente nei valori possibili che una variabile di tipo byte (8 bit) può contenere (da 1 a 90).

Spero di essere stato chiaro. Proseguiamo.

Come ultimo metodo, ci sarà quello che visualizzerà tutti gli estratti:

       static void Stampa()
         {
            Console.WriteLine("Programma SuperEnalotto:\n\nGli estratti sono:\n");
               for(byte i = 0; i < 90; i++)
                   if (estratti[i] != 0)
                       Console.Write("{0} ", estratti[i]);
               Console.WriteLine();
         }

che visualizza gli estratti in ordine crescente. Si commenta da solo (spero!).

Infine il metodo Main() che altro non fa che richiamare i metodi di cui sopra;

         public static void Main()
         {
              Azzera_vettore();
              Genera();
              Stampa();
              // resta in attesa fino a quando non
              // viene premuto il tasto Invio
              Console.ReadLine();
         }

Il codice ora è completo, e questo è il listato:

using System;

   public class Program
    {
       // dichiarazione degli oggetti
       // a livello globale
       static Random rnd= new Random();
       static byte[] estratti = new byte[90];

        static void Azzera_vettore()
        {
             // ogni elemento del vettore
             // viene inizializzato a 0
             for (byte i = 0; i < 90; i++)
                  estratti[i] = 0;
        }

static void Genera()

      {
          // variabile contatore
          byte i = 0;
          // variabile che contiene l'estratto
          byte numero = 0;

          // viene generato un numero intero
          // che deve essere sottoposto a
          // casting perché il vettore contiene
          // valori di tipo byte
          numero = (byte)rnd.Next(0,90);

          // ciclo che controlla l'esistenza
          // dell'estratto per sei volte (da 0 a 5)
          while (i < 6)
          {
              if (estratti[numero] == 0)              {
                 // se la locazione del vettore
                 // puntata dall'estratto contiene
                 // il valore 0, allora lo inserisce
                 // nel vettore incrementato di 1 e 
                 // anch'esso deve essere sottoposto a
                 // casting perché l'incremento risulta
                 // intero
                 estratti[numero] = (byte)(numero+1);
                 // ed incrementa il contatore 
                 i++;
              }
              // genera un nuovo numero
              numero = (byte)rnd.Next(0, 90);
          }
      }

static void Stampa()
  {
      Console.WriteLine("Programma SuperEnalotto:\n\nGli estratti sono:\n");
         for(byte i = 0; i < 90; i++)
             if (estratti[i] != 0)
                Console.Write("{0} ", estratti[i]);
      Console.WriteLine();
   }

public static void Main()

         {
              Azzera_vettore();
              Genera();
              Stampa();
              // resta in attesa fino a quando non
              // viene premuto il tasto Invio
              Console.ReadLine();
         }

Copiatelo, salvatelo con il nome di Super8.cs e compilatelo. Questo è il mio esempio:

Esempio

L'interfaccia è ovviamente spartana trattandosi di una applicazione a console, ma potete tranquillamente modificare il codice affinchè possa essere più possibile gradevole, e buona fortuna!

Nella prossima lezione, in cui studieremo le matrici, faremo in modo di ampliare questo programmino in modo da ottenere tutte e 15 le possibili sestine in ordine di estrazione.


Pagina precedente - Pagina successiva