Battaglia Navale. Un gioco che insegna il Vba. -
dal 04/09/04 pagina vista:
volte
Questa volta
torniamo ad occuparci di Random , ovvero
come generare casualmente degli intervalli
di celle che conterranno dei valori, all'interno di aree predefinite.
La Casualità, o
Randomizzazione, si può ottenere solo su valori numerici, e non su testo.
(vedi anche su questo sito, sezione vba, articolo "Estrarre dati
casuali"). Riassumiamo velocemente comunque i concetti: un numero casuale si
ottiene in vba con una semplice istruzione:
-
Int(x * Rnd) + 1 -
dove "x" sarà un numero che rappresenta il numero da randomizzare, e che
rapresenta il limite massimo raggiungibile dal calcolo - 1, infatti
restituirà un valore compreso tra zero (0) e il valore di "x"
escluso.
Poichè in vb i numeri e gli indici, iniziano con zero (non con 1). Se per esempio "x"
fosse il numero 20, verrebbero considerati 20 numeri da casualizzare. Se il
valore randomizzato su questi 20 numeri fosse 5, otterremo in realtà in
restituzione il numero 4; iniziando infatti a contare da zero, 4 è il quinto
numero. Per questo nella formula aggiungiamo + 1:
per restituirci un numero che corrisponda ad un valore così come siamo
abituati a considerarlo.
I numeri ci
servono anche per identificare le righe e le colonne quando usiamo la
notazione Cells(numero riga, numero colonna).
Randomizzando quindi questi numeri di identificazione cella, possiamo mirare
a celle o intervalli i cui riferimenti risultano casuali. Leggendo quindi
quante righe e quante colonne compongono una determinata area del foglio di
lavoro, potremo usare questi numeri come valori da randomizzare.
Provando alcune
routines, ne è venuto fuori un esercizio interessante da segnalare e che può
essere spiegato usando un vecchio gioco: la Battaglia Navale. Tutti
conoscono questo gioco: due tabelle formate ognuna da 100 celle: 10 righe su
10 colonne: in queste tabelle due avversari posizionano delle "navi" di
dimensioni variabili, poi un giocatore alla volta, puntano ad una cella
dell'avversario cercando di colpire una "nave". Quando tutte le navi
dell'avversario sono state colpite e quindi affondate, il gioco finisce con
un vincitore. Nella realtà del gioco, i due avversari nascondono le
rispettive tabelle, su un foglio di lavoro usiamo l'accorgimento, per non
vedere dove posiziona le "navi" l'avversario, di usare su campo bianco, i
caratteri (che rappresentano le navi) di colore bianco.
Lo svolgimento del
gioco è semplice : giocheremo noi contro il computer: selezioniamo una
cella nell'area del nemico: se questa cella contiene un valore, la cella
verrà colorata in rosso, altrimenti in grigio; ad ogni nostro "colpo" il
computer reagirà colpendo una cella nella nostra tabella, ed anche qui
coloreremo le celle. Vediamo un immagine:
Ho fissato, ma si
possono variare, le dimensioni delle 5 navi che ogni giocatore avrà:
-
1 unità da due
caselle
-
2 unità da 3
caselle
-
1 unità da 4
caselle
-
1 unità da 5
caselle
E' inoltre
possibile possibile decidere il posizionamento delle unità : in verticale o
in orizzontale; io ne ho previste 4 in verticale e una in orizzontale per il
giocatore, mentre per l'avversario (il computer) ne ho previste 3 in
verticale, una in orizzontale, ed una la cui posizione viene randomizzata: o
verticale o orizzontale; non sapremo come questa verrà posizionata fino a
chè, colpendola, non la individueremo.
In questo gioco, è
il giocatore avvantaggiato: non ho previsto per il computer nessuna
particolare precisione: le nostre celle verranno colpite in maniera casuale,
mentre noi potremo decidere, una volta colpita una nave, se proseguire nelle
celle vicine per afforndare l'unità.
Invito i lettori a
sviluppare le istruzioni per migliorare la precisione del computer. I loro
lavori saranno messi nella sezione " i vostri lavori".
Vediamo ora i
passaggi più importanti usati nelle routines.
-
Sub GeneraNemico() - questa routine
serve a pulire l'area avversaria, generando casualmente le
coordinate relative alla posizione delle celle
che conterranno le unità, e all'interno dell'intervallo così reperito,
inseriamo il carattere asterisco (*). Potevamo inserire un numero, ma per
noi sarebbe stato anche troppo facile determinare dal numero colpito, la
lunghezza della "nave". Riporto sotto solo l'inizio delle prime istruzioni,
poi per ogni nave le istruzioni sono simili, cambieranno solo i valori che
determinano la lunghezza della nave e il loro posizionamento. Scaricando il
file potrete leggere tutte le routines.
Sub generaNemico()
Dim CL As Object 'dimensioniamo CL
come oggetto: sarà una cella
'sotto: impostiamo una
tantum con Nemico, l'area avversaria
Set Nemico = ActiveSheet.Range("O5:X15")
Nemico.ClearContents 'puliamo
l'area Nemico
Nemico.Cells.Interior.ColorIndex = xlNone
'impostiamo il colore delle celle al
naturale
'sotto: con "R" contiamo quante sono le
righe nell'area Nemico, e con "C" quante colonne. 'Essendo numeri
fissi, potevamo semplicemente scrivere R = 10 e C = 10, ma così
vediamo come 'poter determinare questi valori in routine che contino
righe e colonne in aree modificabili
R = Nemico.Rows.Count
C = Nemico.Columns.Count
10: 'indice di riga istruzioni
Randomize 'con Randomize aiutiamo a
generare dei numeri casuali, non ripetitivi
'sotto: con "rig"
randomiziamo il valore di "R", aggiungendo 4 perchè la tabella inizia
dalla riga 'numero 5, lo stesso facciamo con le colonne "C", aggiungendone
14 perchè la tabella inizia dalla 'colonna O, che è la quindicesima
rig = Int((R * Rnd) + 1) + 4
col = Int((C * Rnd) + 1) + 14
'sotto: ora impostiamo con
"nave1" il primo intervallo di celle, che inizierà dalla cella(rig,
col) e 'comprenderà la cella(rig + tre celle, col): in questo modo
determiniamo un intervallo di celle 'formato da 4 celle, quindi
realiziamo la prima delle 5 navi. Con lo stesso sistema, variando il
'numero che identifica l'ultima cella, determineremo la composizione
delle altre "navi".
Set nave1 = Range(Cells(rig, col), Cells(rig + 3, col))
'sotto: per evitare che vengano generati
intervalli che fuoriescono dall'area (tabella Nemico), si 'controlla
che l'ultima riga dell'intervallo "nave1" non superi la riga 15, se
ciò avvenisse, 'rimandiamo con GoTo 10, alla generazione di un altro
intervallo.
If nave1.Offset(3, 0).Row > 15 Then GoTo 10
'sotto: altrettanto facciamo, per evitare di
sovrascrivere le "navi", controlando con un ciclo For 'Each, che ogni
cella di "nave1" non contenga già valori, altrimenti si ritorna a 10
For Each CL In nave1
If CL.Value <> "" Then GoTo 10
Next 'terminati
questi due controlli, se "nave1" è gestibile, si riempono le celle con
l'asterisco. Questo 'formerà la nostra "nave"
nave1.Cells.Value = "*" |
Ovviamente a
seguire ci saranno le istruzioni, simili a questa sopra, per generare una
dopo l'altra, le 5 navi. Lo stesso sistema lo useremo per la generazione
delle "navi" nella nostra area; solo che in questo caso, ho usato dei numeri
al posto degli asterischi: ogni nave un numero in successione, partendo da
1.
Unica variante che
segnalo in questo articolo, è la routine che serve a determinare se una
"nave" verrà posizionata in verticale o in orizzontale: anche qui usiamo una
randomizzazione impostata sul numero 0 e 1; se uscirà o, seguiamo un
orientamento, se esce 1 ne seguiamo un'altro. Vediamo il particolare:
'........omissis
30:
'indice di riga istruzioni
'sotto: si crea una
randomizzazione basata su 2 soli numeri, quindi 0 e 1 e questo valore
lo 'assegniamo alla variabile "come"
Randomize
come = Int(2 * Rnd)
'sotto: se "come" è uguale
a 0, seguiamo questa routine, altrimenti passiamo a Else. In questa
'condizione facciamo randomizzare un'intervallo verticale, per una
"nave" a 5 celle, le spiegazioni 'sono ugali a quelle viste sopra:
If come = 0 Then
Randomize
rig = Int((R * Rnd) + 1) + 4
col = Int((C * Rnd) + 1) + 14
Set nave3 = Range(Cells(rig, col), Cells(rig + 4, col))
If nave3.Offset(4, 0).Row > 15 Then GoTo 30
For Each CL In nave3
If CL.Value <> "" Then GoTo 30
Next
nave3.Cells.Value = "*"
Else
35: 'indice di riga
istruzioni
Randomize
rig = Int((R * Rnd) + 1) + 4
col = Int((C * Rnd) + 1) + 14
'sotto: in queste
istruzioni anzichè generare l'intervallo aggiungendo il numero di
quante celle in più 'considerare rispetto alle righe, aggiungiamo il
numero alle colonne; questo è sufficiente a generare 'un'intervallo
orizzontale. bisogna poi modificare il successivo controllo per non
uscire fuori dalla 'tabella, indicando quale è la colonna oltre la
quale non si può andare, cioè la X (la 24esima)
Set nave3 = Range(Cells(rig, col), Cells(rig, col + 4))
If nave3.Offset(0, 4).Column > 24 Then GoTo 35
For Each CL In nave3
If CL.Value <> "" Then GoTo 35
Next
nave3.Cells.Value = "*"
End If
'...........omissis |
Va da se che
potrete creare la randomizzazione dell'orientamento per ogni "nave", io l'ho
fatta solo su una, per fornire un possibile esempio.
Ora vediamo cosa
avviene quando selezioniamo (spariamo un colpo) una cella del "Nemico".
Usiamo l'evento Worksheet_SelectionChange, unito all'uso del metodo
Intersect per limitare l'esecuzione delle istruzioni solo all'area indicata.
Private Sub
Worksheet_SelectionChange(ByVal Target As Range)
If Intersect(Target, Range("O5:X15")) Is Nothing Then
Exit Sub
End If
If ActiveCell <> "" Then 'se la cella
selezionata contiene dati, coloriamo la cella di rosso
ActiveCell.Interior.ColorIndex = 3
MsgBox "HAI COLPITO UNA UNITA'" 'e
si avvisa con questo messaggio
Else 'altrimenti
ActiveCell.Interior.ColorIndex = 15
'la coloriamo di grigio
End If
Colpito 'indi si chiama la macro
"Colpito", che agirà sulla nostra area: è questa la risposta del
'computer
End Sub |
Ora vediamo la
macro "Colpito", che serve a restituire il colpo, e che colpirà una cella
randomizzata, ma nella nostra area, le istruzioni a questo punto sono
comprensibili e non le commento :
Sub Colpito()
Set Amico = ActiveSheet.Range("B5:K15")
Ri = Amico.Rows.Count
Co = Amico.Columns.Count
10:
Randomize
riga = Int((Ri * Rnd) + 1) + 4
colo = Int((Co * Rnd) + 1) + 1
If Cells(riga, colo).Interior.ColorIndex = 3 Or _
Cells(riga, colo).Interior.ColorIndex = 15 Then GoTo 10
If Cells(riga, colo) <> "" Then
Cells(riga, colo).Interior.ColorIndex = 3
MsgBox "SONO STATO COLPITO"
Else
Cells(riga, colo).Interior.ColorIndex = 15
End If
End Sub |
Questa ultima
routine è semplice, e non ci penalizza troppo, lasciandoci vincere con
facilità. Ripeto l'invito a quanti vorranno cimentarsi nel realizzare
variazioni che consentano al computer, una volta identificata una cella col
fondo rosso, e con un determinato valore, di continuare a colpire nelle
vicinanze fino all'affondamento della "nave" rappresentata da tutte le celle
con lo stesso numero. Ho già suggerito anche troppo....
Esiste poi l'altra
routine Sub GeneraAmico() che troverete nel file, e che si basa sulle
istruzioni viste in GeneraNemico.
File scaricabile e
consultabile :
BattleShip.zip 15 Kb.
Buon lavoro.
prelevato sul sito
www.ennius.altervista.org
|