Diciassettesima lezione - Campo Mino: gli ultimi accorgimenti

Nell'ultima lezione vi avevo lascio con la routine che gestisce il click sui pulsanti: quando viene premuto un pulsante, bisogna innanzitutto verificare se esso nasconde una mina oppure no, e per questo bisogna controllare la sua proprietà Tag. Se essa è maggiore di 0, significa che sotto di esso non ci sono mine e bisogna mostrare al giocore quante mine circondano quel pulsante.



E' quello che fa la prima diramazione della If tramite l'aggiornamento della proprietà Caption. Se il Tag non è maggiore di 0, potrebbe essere uguale a -1: in tal caso, il pulsante nasconde una mina e lo facciamo capire all'utente visualizzando una "M" sul pulsante; il vero Campo Mino mostra l'icona della mina, ma noi per semplicità ci accontentiamo di modificare opportunamente la Caption dei pulsanti.
Trova la mina, il gioco deve terminare e quindi interrompiamo il conteggio dei secondi disabilitando il Timer e mostrando al giocore la posizione di tutte le mine: per questo motivo con un ciclo aggiorniamo la Caption di tutti i pulsanti con le mine, e non solo di quella trova dal giocore. La variabile contore utilizza nel ciclo è x, che di per sé dovrebbe essere usa per calcolare la riga in cui si trova il pulsante premuto: volendo essere diligenti, avremmo dovuto dichiarare un'altra variabile apposita, ma do che l'utilizzo di x viene comodo e non crea problemi con le altre diramazioni della If, possiamo benissimo utilizzarla risparmiando così qualche byte. La terza diramazione della If serve per l'ultimo caso, quello in cui

mina(Index).Tag = 0  

Quando ciò si verifica, Campo Mino mostra tutta la "frontiera" dei pulsanti che si trovano nelle vicinanze di una mina.



Per ottenere lo stesso risulto, noi dovremmo usare una funzione ricorsiva (cioè una funzione che chiama se stessa un numero indefinito di volte) con opportuni controlli per evitare di bloccare l'applicazione, e siccome tutto ciò potrebbe rivelarsi complesso, per ora ci accontentiamo di mostrare il numero di mine vicine ai pulsanti che circondano quello premuto.
Ora il nostro Campo Mino è quasi pronto: possiamo già fare una prima partita, avvie il progetto, selezione "Nuova" dal menù "Partita" e premete i pulsanti; ricorde di selezionare il menù, perché altrimenti i pulsanti non sono ancora inizializzi (o, se preferite, il campo non è ancora mino).
Ora che avete gioco la vostra prima partita, prove a farne un'altra: noterete che le Caption dei pulsanti rimangono invarie. Infti ingenuamente ci siamo dimentici di inizializzare anche quelle: per la prima partita non ce n'era bisogno, perché in fase di progettazione avevamo elimino le proprietà Caption, ma per le partite successive dobbiamo ripristinare le condizioni iniziali. Pertanto aggiungiamo queste semplici righe nella routine mnuNew_click (all'inizio o anche alla fine, come preferite):

For i = 0 To 15
	Mina(i).Caption = ""
Next i

Ora possiamo giocare quante partite vogliamo, ma non abbiamo ancora finito: innanzitutto dobbiamo scrivere un bel "Unload me" in mnuExit_click; e poi sarebbe bene congrularsi col giocore quando vince la partita:



E' intuitivo che una partita a campo mino viene vinta quando il giocore "scopre" tutti i pulsanti tranne quelli che nascondono le mine (nel nostro caso 13 pulsanti); avremo quindi bisogno di un contore che tenga traccia di quanti pulsanti sono sti scoperti.
Il luogo più nurale in cui fare questi conti è la routine Mina_Click:

If Len(Mina(Index).Caption) = 0 Then
	ContaMine = ContaMine + 1
End If
If ContaMine = MaxContaMine Then
	Msgbox "HAI VINTO!"
End If 

La variabile ContaMine andrebbe logicamente dichiara a livello di modulo, nella sezione delle dichiarazioni generali del Form; tuttavia si potrebbe anche dichiararla nella medesima routine Mina_Click usando la parola chiave Stic.
La variabile MaxContaMine, invece, va necessariamente dichiara a livello di modulo, perché non è utilizza solo da Mina_Click: essa infti deve essere inizializza al valore 13 (il numero massimo di pulsanti scopribili) all'avvio dell'applicazione, cioè in Form_Load; dal momento che abbiamo un solo livello di gioco (e non tre come il vero Campo Mino), potremmo fare direttamente:

If ContaMine = 13 Then ...  

ma questo non è un buon metodo, perché limita la possibilità di ulteriori aggiornamenti ed espansioni dell'applicazione: se infti un giorno volessimo aumentare il numero di pulsanti, dovremmo cambiare manualmente tutte le occorrenze del valore 13; per non parlare del caso in cui volessimo aggiungere un livello di gioco.
Invece utilizzando una variabile apposita, dovremmo preoccuparci soltanto di modificarla in fase di inizializzazione.
Ricorde inoltre che l'utilizzo di una variabile apposita rende più chiaro e leggibile il codice, anche a voi stessi. Ricorde anche di inserire l'istruzione:

ContaMine = 0  

nella routine mnuNew_Click, perché in ogni nuova partita il contore deve partire da 0.
Per quanto riguarda la prima If che abbiamo inserito, c'è da dire che il contore va aggiorno solo se il pulsante è sto premuto per la prima volta (altrimenti uno potrebbe "vincere" premendo per 13 volte lo stesso pulsante!), ovvero se la sua Caption è ancora una stringa nulla.
Il controllo non viene effettuo confrontando la Caption con "", ma esaminando la lunghezza della Caption stessa: questo perché le operazioni con i numeri (la funzione Len restituisce appunto la lunghezza della stringa, cioè un numero) sono più veloci di quelle con le stringhe.
La seconda If è così ovvia che se non l'avete capita dovreste ricominciare il corso daccapo...
L'aggiornamento del contore però non deve essere fto solo per il pulsante premuto, ma per qualunque pulsante sia sto "scoperto": in altri termini dobbiamo aggiornare il contore anche quando si preme un pulsante che non è circondo da mine, perché in questo caso il gioco visualizzerà il numero di mine vicine ai pulsanti che circondano quello premuto.
Quindi le istruzioni:

If Len(Mina(Index).Caption) = 0 Then
	ContaMine = ContaMine + 1
End If

vanno inserite nella prima e nella terza diramazione della If contenuta nella routine Mina_Click, che pertanto è diventa così:

If Mina(Index).Tag > 0 Then
	If Len(Mina(Index).Caption) = 0 Then
		ContaMine = ContaMine + 1
	End If
	Mina(Index).Caption = Mina(Index).Tag
ElseIf Mina(Index).Tag = -1 Then
	Mina(Index).Caption = "M"
	For x = 0 To 2
		Mina(PosMine(x)).Caption = "M"
	Next x
	Timer1.Enabled = False
Else
	x = Int(Index / 4) 'riga in cui si trova il pulsante premuto
	y = Index Mod 4 'colonna in cui si trova il pulsante premuto
	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 Len(Mina(4 * x1 + y1).Caption) = 0 Then
				ContaMine = ContaMine + 1
			End If
			Mina(4 * x1 + y1).Caption = Mina(4 * x1 + y1).Tag
		Next y1
	Next x1
End If
If ContaMine = MaxContaMine Then
	MsgBox "HAI VINTO!"
End If 

Non resta che aggiornare l'etichetta lblMine: a che serve? Serve a tenere il conto delle mine non ancora trove; come saprete, cliccando col tasto destro su uno dei pulsanti del vero Campo Mino, su di esso apparirà una bandiera ad indicare che in quella posizione c'è (o pensiamo che ci sia) una mina, e quel pulsante diventerà insensibile al clic sinistro, almeno finchè su di esso resterà la bandierina.



Noi al posto della bandiera visualizzeremo una "x", ma non è questo il punto: la cosa importante è capire come intercettare il clic col tasto destro, ed è una cosa che impareremo più avanti. Per concludere questa lezione, vorrei tornare al punto da cui siamo partiti: la mrice di controlli. Dovreste esservi resi ormai conto dell'utilità dei vettori e delle mrici di variabili: con una sola dichiarazione avete a disposizione numerose variabili dello stesso tipo, college tra loro da un nesso logico, da una medesima funzione, da un medesimo scopo; potete modificarle tutte insieme con un semplice ciclo sfruttando la possibilità di accedere ad esse traverso un indice. Per una mrice di controlli i vantaggi sono gli stessi: i pulsanti del nostro campo mino svolgono tutti la stessa funzione, cioè visualizzano quante mine ci sono nelle vicinanze o interrompono il gioco se nascondono una mina; grazie al fto che appartengono a una mrice, possiamo accedere ad ognuno di essi tramite un indice che, nel caso dell'evento click, ci viene fornito direttamente da Visual Basic, con la conseguenza che possiamo scrivere il codice solo una volta, perché una sola è la routine Mina_Click; se non avessimo uso una mrice di pulsanti, avremmo dovuto crearne 16 diversi e indipendenti, e ripetere le istruzioni della routine per l'evento click di ognuno dei 16 pulsanti: immagine se avessimo uso 480 pulsanti, come nel terzo livello del vero Campo Mino! Le mrici di controlli semplificano tutto questo.







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