Sedicesima lezione - Campo Mino: algoritmi, diagrammi di flusso, l'operore Mod, la funzione IIf, il parametro Index

Ricapitolando, ora conosciamo le posizioni delle mine, e ogni pulsante sa, grazie alla proprietà tag, se nasconde una mina oppure no; ora dobbiamo scrivere le istruzioni per il gioco vero e proprio: cosa succede quando il giocore preme un pulsante ?
Quello che succede è molto semplice: se sotto il pulsante premuto si trova una mina, il gioco termina; altrimenti viene visualizzo il numero di mine circostanti quel pulsante, come mostra la figura sotto:



bisognerà quindi effettuare un controllo del genere:
If Mina(i).Tag = -1 Then
	'il gioco è finito!
Else
	'il gioco continua
End If

Prima di tutto, però, bisogna calcolare quante mine circondano ogni pulsante: questa operazione è opportuno farla all'inizio della partita, in modo che il calcolo sia effettuo una volta sola; quindi dobbiamo aggiornare la routine mnuNew_Click.
A questo punto dobbiamo escogitare un algoritmo semplice ed efficiente per fare questi calcoli: per chi non lo sapesse, un algoritmo è una sequenza di operazioni che permettono di ottenere un generico obiettivo; la sequenza con cui queste operazioni devono essere eseguite è determina dal verificarsi di certe condizioni, e il controllo di queste condizioni è parte integrante dell'algoritmo.
Solitamente si usa un grafico (il cosiddetto diagramma di flusso) per descrivere un algoritmo (ovvero il flusso delle operazioni), ma qui preferisco usare un elenco numero.
Un algoritmo per calcolare il numero di mine circostanti un pulsante potrebbe essere questo:

1) prendo in considerazione un pulsante (chiamiamolo "x");

2) prendo in considerazione tutti i pulsanti che lo circondano, uno alla volta (chiamiamo questo pulsante "y");

3) se y nasconde una mina, incremento un contore c, altrimenti non faccio nulla;

4) esamino il pulsante successivo tra quelli che circondano x, e torno al punto 2); se ho esamino tutti i pulsanti che circondano x, assegno x.Tag = c e reimposto c = 0 (altrimenti c si ricorderebbe dei valori relivi ad altri pulsanti);

5) esamino il pulsante successivo a x e torno al punto 1; se ho esamino tutti i pulsanti, ho termino l'algoritmo.

In parole povere, questo algoritmo descrive due cicli annidi tra loro: il primo esamina tutti i nostri 16 pulsanti, uno alla volta; il secondo, quello più interno, esamina tutti i pulsanti che circondano quello preso in esame dal primo ciclo; all'interno del secondo ciclo viene aggiorno il contore delle mine.
C'è però un altro algoritmo, che mi sembra più semplice ed efficiente: visto che sappiamo che le mine sono in tutto 3, non c'è alcun bisogno di fare due cicli per andare a cercare i pulsanti che nascondono una mina; noi sappiamo dove sono le mine, e quindi ci basterà aggiornare le proprietà Tag dei pulsanti che circondano quelli che nascondono una mina.
L'algoritmo è questo:



1) inizializzo le proprietà tag di tutti i pulsanti a 0;

2) prendo in esame il pulsante con indice PosMine(i) (chiamiamolo "x"; inizialmente i = 0);

3) pongo x.tag = -1 e aggiorno le proprietà tag di tutti i pulsanti che circondano x;

4) se i = 2 termino l'algoritmo, altrimenti pongo i = i + 1 e torno al punto 2.

Tradotto in codice, questo algoritmo diventerebbe così (ovviamente va aggiunto alla routine mnuNew_Click):

Dim i As Integer 'contore
Dim x As Integer, y As Integer 
'coordine del pulsante con la mina
Dim x1 As Integer, y1 As Integer
'coordine dei pulsanti circostanti quello con la mina
For i = 0 To 15
	Mina(i).Tag = 0
Next i
For i = 0 To 2
Mina(PosMine(i)).Tag = -1
x = Int(PosMine(i) / 4) 'riga in cui si trova la mina
y = PosMine(i) Mod 4 'colonna in cui si trova la mina
For x1 = IIf(x = 0, 0, x - 1) To IIf(x = 3, 3, x + 1)
	For y1 = IIf(y = 0, 0, y - 1) To IIf(y = 3, 3, y + 1)
		If Mina(4 * x1 + y1).Tag > -1 Then
		Mina(4 * x1 + y1).Tag = Mina(4 * x1 + y1).Tag + 1
	End If
	Next y1
Next x1
Next i
Timer1.Enabled = True

Il primo ciclo è quello di inizializzazione; il secondo appare più complico di quanto sia in realtà: innanzitutto viene aggiorno il Tag del pulsante con la mina, poi vengono calcole le coordine di questo pulsante; questa operazione è opportuna perché i pulsanti sono disposti graficamente come una mrice bidimensionale 4 x 4, mentre nel codice sono "allinei" in un vettore unidimensionale.
Prendiamo ad esempio il decimo pulsante, quello con indice 9: do che ogni riga è di qutro elementi, per sapere su quale riga e colonna si trova bisogna sottrarre dall'indice il multiplo di 4 immediamente precedente all'indice stesso (in questo caso 8): la differenza (9 - 8 = 1) indica la colonna, mentre questo multiplo diviso per 4 indica la riga (8 / 4 = 2).
La colonna viene ottenuta tramite l'operore Mod, che restituisce il resto intero di una divisione: ad esempio, 9 Mod 4 = 1 perché 9 / 4 è uguale a 2 col resto di 1; ovviamente 8 Mod 4 = 0, così come 10 Mod 5, 6 Mod 2, 3 Mod 3 ecc.
Per analogia con l'indice dei pulsanti, anche le righe e le colonne le contiamo a partire da 0, quindi il pulsante con indice 9 si troverà sulla riga 2 colonna 1, ovvero il decimo pulsante si trova sulla terza riga, seconda colonna.
Ottenute le coordine, è più semplice controllare i pulsanti che circondano la mina.
Avrete noto una nuova funzione nei cicli più interni: la funzione IIf; questa funzione è simile ad una if "concentra": i tre argomenti indicano rispettivamente la condizione da verificare, il risulto da restituire se la condizione è vera, il risulto da restituire se la condizione è falsa; in altri termini, IIf(x=0, 0, x-1) equivale a:

If x = 0 Then
	risulto = 0
Else
	risulto = x - 1
End If
Questo controllo è necessario perché, se è x = 0, cioè se la mina si trova sulla prima riga, i pulsanti circostanti partono anch'essi dalla prima riga e non dalla riga precedente come avverrebbe negli altri casi, semplicemente perché non esiste una riga precedente alla prima; lo stesso dicasi per l'ultima riga
IIf(x = 3, 3, x + 1))  

e per la prima e ultima colonna.
Nel ciclo più interno ho introdotto un altro controllo per verificare che il pulsante controllo NON sia quello che nasconde la mina: infti i due cicli analizzano tutti i pulsanti che circondano la mina, compreso quello che nasconde la mina stessa, e quest'ultimo non deve essere modifico.
Per non parlare della possibilità di avere due mine adiacenti. Infine, la proprietà Tag viene aggiorna incrementando di 1 il valore della stessa proprietà.
Questo per consentire un'indicazione corretta nel caso le mine circostanti un determino pulsante siano 2, oppure 3.
L'ultima istruzione serve per abilitare il Timer, in modo da poter misurare i secondi di gioco. Ora possiamo finalmente scrivere il codice da eseguire quando il giocore preme un pulsante: si trta semplicemente di controllare il valore della proprietà Tag; nella routine Mina_Click noterete la presenza del parametro Index: questo è ovviamente l'indice del pulsante premuto dall'utente, ed è sto aggiunto automicamente da Visual Basic quando abbiamo deciso di creare la mrice di pulsanti. In questa routine dunque scriviamo:

Dim x As Integer, y As Integer 
'coordine del pulsante premuto
Dim x1 As Integer, y1 As Integer 
'coordine dei pulsanti circostanti quello premuto
If Mina(Index).Tag > 0 Then
Mina(Index).Caption = Mina(Index).Tag
ElseIf Mina(Index).Tag = -1 Then
For x = 0 To 2
	Mina(PosMine(x)).Caption = "M"
Next x
Timer1.Enabled = False
Else 'mina(index).tag=0
x = Int(Index / 4) 'riga in cui si trova la mina
y = Index Mod 4 'colonna in cui si trova la mina
For x1 = IIf(x = 0, 0, x - 1) To IIf(x = 3, 3, x + 1)
	For y1 = IIf(y = 0, 0, y - 1) To IIf(y = 3, 3, y + 1)
	Mina(4 * x1 + y1).Caption = Mina(4 * x1 + y1).Tag
	Next y1
Next x1
End If  

Il significo di questo codice dovreste essere in grado di capirlo da soli, tenendo conto di come funziona il vero "Campo Mino". Comunque la prossima volta lo spiegherò in dettaglio







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