Cominciamo dall'etichetta lblTempo, che dovrà misurare il trascorrere del tempo di gioco: la cosa più ovvia è aggiornarla ogni secondo, quindi dobbiamo impostare correttamente le proprietà del Timer; questo controllo misura evidentemente il trascorrere del tempo generando un evento Timer ogni volta che passano tot millisecondi.
Il numero di millisecondi trascorsi i quali viene genero l'evento è indico dalla proprietà Interval: do che noi vogliamo contare i secondi, sarà bene impostare questa proprietà a 1000 ms, cioè 1 secondo; in parole povere, quando il timer viene tivo comincerà a contare i millisecondi: quando arriva a 1000 genera l'evento Timer, reimposta a 0 il contore e ricomincia a contare fino ad arrivare nuovamente a 1000, generando un altro evento Timer; e così via.
Il controllo Timer non deve essere tivo subito, ma deve cominciare a contare solo quando l'utente darà il via alla partita: quindi nella finestra delle proprietà imposte Enabled a false.
Cliccando due volte sul Timer, comparirà l'editor del codice sulla routine dell'evento Timer: qui dobbiamo inserire le istruzioni per aggiornare l'etichetta; banalmente, sarà sufficiente dichiarare una variabile stica che funzioni da contore e scriverne il valore nell'etichetta:
Stic i As Integer
i = i + 1
lblTempo.Caption = CStr(i)
La variabile i va dichiara come Stic perché deve tener conto dei secondi passi anche "al di fuori" della routine "timer1_timer": se non si usa la parola chiave Stic, la variabile verrà crea ogni volta che l'evento timer viene genero, e di conseguenza assumerà sempre il valore zero.
Il timer, come ho detto, dovrà essere tivo quando comincia una nuova partita, quindi nella routine mnuNew_click bisognerà scrivere:
Timer1.Enabled = True
e corrispondentemente esso dovrà essere distivo quando la partita finisce: ce ne occuperemo tra poco.
Ora viene la parte più difficile, scrivere il codice associo ai pulsanti, che costituiscono il gioco vero e proprio: innanzitutto bisogna decidere dove piazzare le mine; visto che le caselle sono 16, potremmo piazzare 3 mine.
Nuralmente non possiamo decidere noi direttamente dove metterle, ma dovremo affidarci al caso, usando quindi i numeri casuali: per generare i numeri casuali esiste una funzione, Rnd, che restituisce un numero (più o meno) casuale compreso tra 0 (incluso) e 1 (escluso), ovvero tra 0 e 0,99999.
Noi però dobbiamo decidere sotto quale pulsante mettere la mina, quindi dobbiamo avere un numero che ci dica qual è l'indice del pulsante in questione; e dal momento che i pulsanti sono 16, il loro indice andrà da 0 a 15, e pertanto abbiamo bisogno di un numero casuale compreso tra 0 e 15: ottenerlo è banalissimo, basta infti moltiplicare il risulto della funzione Rnd per 16, e prenderne la parte intera:
Int(16 * Rnd)
La funzione Int() non fa altro che troncare la parte decimale di un numero restituendo solo la parte intera: se ad esempio Rnd restituisce 0.1, 16 * 0.1 = 1.6 e quindi int(1.6) = 1; visto che Rnd restituisce sempre un numero minore di 1, il prodotto 16 * Rnd sarà sempre minore di 16, ovvero compreso tra 0 e 15: proprio ciò che ci serve.
Do che abbiamo bisogno di 3 mine, ci conviene fare un vettore di tre elementi, anziché dichiarare tre variabili distinte; quindi, nella sezione delle dichiarazioni del form, scriviamo:
Dim PosMine(2) As Integer
La posizione delle mine dobbiamo deciderla all'inizio di ogni partita: quindi in mnuNew_click, scriviamo:
Dim i As Integer 'contore per il ciclo
Randomize Timer
For i = 0 To 2
PosMine(i) = Int(Rnd * 16)
Next i
L'istruzione Randomize serve ad inizializzare il generore di numeri casuali: come saprete, i numeri "casuali" generi da un qualunque computer non sono veramente casuali, poiché sono generi in modo deterministico, seguendo regole ben precise; solo che queste regole sono tali da garantire, entro certi limiti, una sorta di casualità nei numeri generi, che per questo vengono definiti più correttamente "pseudo-casuali".
Ora, la funzione rnd calcola i numeri pseudo-casuali a partire da un numero base, che viene modifico dall'istruzione Randomize tramite il suo argomento: nel nostro caso questo argomento è il valore restituito dalla funzione Timer, che restituisce il numero di secondi trascorsi dalla mezzanotte del giorno corrente (la funzione Timer non c'entra nulla con il controllo Timer, né tantomeno con l'evento Timer); do che questo valore è altamente aleorio, anche il numero base utilizzo dalla funzione rnd cambia in modo abbastanza casuale, e quindi tale funzione calcola numeri un po' più casuali di quanto accadrebbe se si utilizzasse rnd senza Randomize.
Ora che abbiamo le posizioni delle mine, potremmo controllare, ogni volta che il giocore preme un pulsante, se l'indice di quel pulsante appartiene al vettore PosMine: questo è un metodo alquanto laborioso e inefficiente, e sarebbe molto meglio se ogni pulsante sapesse già se sotto di esso si nasconde una mina oppure no. Per fare questo è sufficiente sfruttare una proprietà comune a quasi tutti i controlli, la proprietà Tag, che consente di memorizzare di aggiuntivi utili per la funzionalità del controllo: nel nostro caso, ad esempio, potremmo associare ai pulsanti che nascondono una mina la stringa "mina", assegnandola alla proprietà Tag; quando il giocore preme il pulsante, basterà controllare se la proprietà contiene la stringa "mina" per sapere se il gioco è termino o se può continuare.
Ancora meglio, visto che le possibilità sono solo due (la mina c'è o non c'è), potremmo assegnare alla proprietà Tag direttamente un valore booleano che indichi l'eventuale presenza della mina; in altre parole, potremmo scrivere, all'interno del ciclo descritto sopra:
Mina(PosMine(i)).Tag = True
C'è però da considerare un'altra cosa: sarebbe comodo che ogni pulsante sapesse non solo se nasconde o meno una delle mine, ma anche quante altre mine ci sono sotto i pulsanti che lo circondano; in tal caso, nella proprietà Tag dovremmo scrivere il numero di mine circostanti ogni pulsante e, se una mina si trova proprio sotto il pulsante considero, dovremmo assegnare un valore particolare. Ad es., se intorno al pulsante x ci sono due mine, potremmo scrivere x.Tag = 2; se intorno ad esso ce n'è una sola, scriveremmo x.Tag = 1; ma se la mina si trova proprio sotto x, non possiamo scrivere di nuovo x.Tag = 1, altrimenti si farebbe confusione: possiamo però scrivere x.Tag = -1, perché il numero di mine circostanti non può essere negivo, e quindi se la proprietà Tag contiene il valore -1, il pulsante saprà che la mina è sotto di lui, e non sotto qualche altro pulsante che lo circonda.
Un pericolo di cui una persona inesperta si accorgerebbe difficilmente è che diversi elementi del vettore PosMine potrebbero assumere valori uguali: questo dipende banalmente dal fto che noi prendiamo in considerazione non l'intero numero casuale (moltiplico per 16), ma solo la sua parte intera; se ad esempio la funzione rnd restituisse consecutivamente i valori 0.19 e 0.24, risulterebbe che Int(16 * 0.19) = Int(16 * 0.24) = 3, con la conseguenza che le nostre mine sarebbero in realtà due e non tre, perché due dei tre valori calcoli coincidono.
Per evitare questo rischio, è sufficiente controllare che il numero casuale appena estrto sia diverso da quelli già memorizzi: il modo più nurale di eseguire questo controllo sarebbe utilizzare un altro ciclo (ma di tipo diverso rispetto al For...Next), e siccome non ne ho ancora parlo, preferisco usare un metodo meno ortodosso ma di più semplice comprensione; si trta di riscrivere la routine mnuNew_click in questo modo:
Dim t As Integer 'variabile temporanea per eseguire i controlli
Randomize Timer
PosMine(0)= Int(Rnd * 16)
Mina(PosMine(0)).Tag = -1
'Estrai:
t = Int(Rnd * 16)
If t = PosMine(0) Then
GoTo Estrai
Else
PosMine(1) = t
Mina(PosMine(1)).Tag = -1
End If
'EstraiDiNuovo:
t = Int(Rnd * 16)
If t = PosMine(0) Or t = PosMine(1) Then
Goto EstraiDiNuovo
Else
PosMine(2) = t
Mina(PosMine(2)).Tag = -1
End if
Quello che succede è abbastanza intuitivo: si estrae un numero casuale e lo si memorizza in PosMine(0); se ne estrae un altro e si controlla che sia diverso da quello precedente: se è diverso, lo si memorizza nell'elemento successivo del vettore (PosMine(1)), altrimenti se ne estrae un altro; la riestrazione è ottenuta traverso l'istruzione Goto, che ordina al computer di interrompere la normale esecuzione del programma saltando a un altro punto del programma stesso, e precisamente al punto indico da un'apposita etichetta.
Nel nostro caso le etichette sono due: "Estrai" e "EstraiDiNuovo"; la sintassi vuole che il nome delle etichette non abbia alcuno spazio al suo interno e sia seguito dai due punti ":". La riestrazione continua finchè non si trova un numero diverso da quello memorizzo in PosMine(0).
Poi si passa alla terza e ultima estrazione, che segue lo stesso schema logico, controllando questa volta che il numero casuale sia diverso da entrambi i valori precedentemente memorizzi, ovvero PosMine(0) e PosMine(1), grazie all'operore logico Or.
Note sul corso:
I diritti di ognuna delle lezioni presente in queste pagine appartengono all'autore Giorgio Abraini. La riproduzione e la divulgazione delle stesse sono consentite solamente dietro citazione di fonte ed autore.
Per suggerimenti, consigli o richieste conttare giorgio102@libero.it.
Fonte : VBItalia.it