Vediamo come realizzare un prototipo del gioco del campo minato, ideato da Robert Donner e Curt Johnson e pubblicato nel 1990 nella raccolta Microsoft Entertainment Pack 1 per Windows 3.1. Negli anni successivi il gioco divenne famoso anche con il nome Prato Fiorito.
Il gioco si basa sullo scoprire le mine nascoste nel campo, quando si scopre una cella che non contiene mine questa può essere vuota, oppure avere un numero che ci indica quante mine limitrofe sono presenti. Il risultato che vogliamo ottenere sarà simile a questo:
Per questo esercizio ho utilizzato il font Digital Counter 7, sviluppato da Sizenko Alexander
Anzitutto abbiamo bisogno di creare lo spazio per il nostro gioco. Creiamo quindi una pagina HTML come nel codice seguente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<html> <head> <title>Campo Minato</title> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;700&display=swap" rel="stylesheet"> <link href="css/font-awesome.min.css" rel="stylesheet"> <link href="campominato.css" rel="stylesheet"> </head> <body> <div id="griglia"></div> <div id="stato"></div> </body> <script src="jquery-3.6.1.min.js"></script> <script src="campominato.js"></script> </html> |
Ogni cella del gioco dovrà contenere 4 proprietà diverse:
- indicatore se c’è o meno una mina
- indicatore se la cella sia o meno aperta
- il conteggio delle mine limitrofe
- indicatore se è stata messa o meno una bandierina
Faccio notare come i punti 3 e 1 possono essere riassunti in un’unica variabile, all’occorenza.
Creiamo quindi anzitutto un oggetto opportuno nel modo seguente:
1 2 3 4 5 6 7 8 |
class Cella { constructor(mina) { this.mina = mina; this.aperta = false; this.cont = 0; this.flag = false; } } |
Dentro a $(document).ready(function() {});
andiamo ad inizializzare il resto del gioco come segue.
Anzitutto popoliamo il nostro campo di celle:
1 2 3 4 5 6 7 8 |
for(r = 0; r < n; r++) { campo[r] = []; for(c = 0; c < n; c++) { campo[r][c] = new Cella(false); $('#griglia').append('<div class="cella" data-row="'+r+'" data-col="'+c+'"> </div>'); } //$('#griglia').append("<br>"); } |
Andiamo poi a distribuire tutte le mine, in base al numero prescelto.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
m = 0; while(m < mine) { pr = Math.round((n-1)*Math.random()); pc = Math.round((n-1)*Math.random()); if( !campo[pr][pc].mina ) { campo[pr][pc].mina = true; for(i=-1;i<=1;i++) for(j=-1;j<=1;j++) { dr = pr + i; dc = pc + j; if( dr >= 0 && dc >= 0 && dr < n && dc < n ) { campo[dr][dc].cont++; } } m++; } } |
Costruiamo ora una funzione ricorsiva per il controllo delle mine medesime.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
function controlla(r, c) { if( campo[r][c].aperta ) return true; campo[r][c].aperta = true; cAperte++; $('.cella[data-row='+r+'][data-col='+c+']').html(campo[r][c].cont ? campo[r][c].cont : " "); $('.cella[data-row='+r+'][data-col='+c+']').addClass("aperta"); if( campo[r][c].cont ) $('.cella[data-row='+r+'][data-col='+c+']').addClass("colore-" + (campo[r][c].cont<4?campo[r][c].cont:4)); if( !campo[r][c].mina && !campo[r][c].cont ) { for(var dr=r-1;dr<=r+1;dr++) for(var dc=c-1;dc<=c+1;dc++) if(dc >= 0 && dr >= 0 && dc < n && dr < n) controlla(dr,dc); } else if( campo[r][c].mina ) { // qui mettiamo la mina $('.cella[data-row='+r+'][data-col='+c+']').html('<i class="fa fa-bomb"></i>'); $('.cella[data-row='+r+'][data-col='+c+']').addClass("mina"); return false; } return true; } |
Aggiungiamo anche una funzione per quando il gioco sarà finito e vorremo visualizzare tutte le mine:
1 2 3 4 5 6 7 8 9 |
function scopriMine() { for(var r = 0; r < n; r++) for(var c = 0; c < n; c++) if(campo[r][c].mina) { campo[r][c].aperta = true; $('.cella[data-row='+r+'][data-col='+c+']').html('<i class="fa fa-bomb"></i>'); $('.cella[data-row='+r+'][data-col='+c+']').addClass("mina"); } } |
A questo punto aggiungiamo una funzione per consentire il click sulle singole caselle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$('.cella').click(function() { if( gioco ) { var r = parseInt($(this).attr("data-row")); var c = parseInt($(this).attr("data-col")); if( !controlla(r,c) ) { // ha perso gioco = false; $('#stato').html("Hai perso!"); } else if( n*n - cAperte <= mine ) { // hai vinto gioco = false; $('#stato').html("Hai vinto!"); } if( !gioco ) scopriMine(); } else { alert("Gioco finito!"); } }); |
E una per aggiungere le bandierine:
1 2 3 4 5 6 7 8 9 10 11 |
$('.cella').contextmenu(function(e) { e.stopPropagation(); e.preventDefault(); var r = parseInt($(this).attr("data-row")); var c = parseInt($(this).attr("data-col")); if( !campo[r][c].aperta ) { if( campo[r][c].flag ) $(this).html(' '); else $(this).html('<i class="fa fa-flag"></i>'); campo[r][c].flag = !campo[r][c].flag; } }); |
Il file JavaScript nel suo complesso sarà simile al seguente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
class Cella { constructor(mina) { this.mina = mina; this.aperta = false; this.cont = 0; this.flag = false; } } var n = 10; var mine = 10; var campo = []; var cAperte = 0; var gioco = true; $(document).ready(function() { $('#griglia').css("height",$('#griglia').width()+"px"); for(r = 0; r < n; r++) { campo[r] = []; for(c = 0; c < n; c++) { campo[r][c] = new Cella(false); $('#griglia').append('<div class="cella" data-row="'+r+'" data-col="'+c+'"> </div>'); } //$('#griglia').append("<br>"); } $('.cella').css("line-height",$('.cella').width()+"px"); $('.cella').css("font-size",(32/50)*$('.cella').width()+"px"); m = 0; while(m < mine) { pr = Math.round((n-1)*Math.random()); pc = Math.round((n-1)*Math.random()); if( !campo[pr][pc].mina ) { campo[pr][pc].mina = true; for(i=-1;i<=1;i++) for(j=-1;j<=1;j++) { dr = pr + i; dc = pc + j; if( dr >= 0 && dc >= 0 && dr < n && dc < n ) { campo[dr][dc].cont++; } } m++; } } function controlla(r, c) { if( campo[r][c].aperta ) return true; campo[r][c].aperta = true; cAperte++; $('.cella[data-row='+r+'][data-col='+c+']').html(campo[r][c].cont ? campo[r][c].cont : " "); $('.cella[data-row='+r+'][data-col='+c+']').addClass("aperta"); if( campo[r][c].cont ) $('.cella[data-row='+r+'][data-col='+c+']').addClass("colore-" + (campo[r][c].cont<4?campo[r][c].cont:4)); if( !campo[r][c].mina && !campo[r][c].cont ) { for(var dr=r-1;dr<=r+1;dr++) for(var dc=c-1;dc<=c+1;dc++) if(dc >= 0 && dr >= 0 && dc < n && dr < n) controlla(dr,dc); } else if( campo[r][c].mina ) { // qui mettiamo la mina $('.cella[data-row='+r+'][data-col='+c+']').html('<i class="fa fa-bomb"></i>'); $('.cella[data-row='+r+'][data-col='+c+']').addClass("mina"); return false; } return true; } function scopriMine() { for(var r = 0; r < n; r++) for(var c = 0; c < n; c++) if(campo[r][c].mina) { campo[r][c].aperta = true; $('.cella[data-row='+r+'][data-col='+c+']').html('<i class="fa fa-bomb"></i>'); $('.cella[data-row='+r+'][data-col='+c+']').addClass("mina"); } } $('.cella').click(function() { if( gioco ) { var r = parseInt($(this).attr("data-row")); var c = parseInt($(this).attr("data-col")); if( !controlla(r,c) ) { // ha perso gioco = false; $('#stato').html("Hai perso!"); } else if( n*n - cAperte <= mine ) { // hai vinto gioco = false; $('#stato').html("Hai vinto!"); } if( !gioco ) scopriMine(); } else { alert("Gioco finito!"); } }); $('.cella').contextmenu(function(e) { e.stopPropagation(); e.preventDefault(); var r = parseInt($(this).attr("data-row")); var c = parseInt($(this).attr("data-col")); if( !campo[r][c].aperta ) { if( campo[r][c].flag ) $(this).html(' '); else $(this).html('<i class="fa fa-flag"></i>'); campo[r][c].flag = !campo[r][c].flag; } }); }); |
Affinché il tutto funzioni abbiamo bisogno anche di un po’ di CSS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
#stato { font-size: 32px; text-align: center; font-family: 'Roboto', sans-serif; width: 600px; } #griglia { width: 600px; height: 600px; } .cella { border: 4px outset #bbb; display: inline-block; width: 50px; height: 50px; background-color: #ccc; cursor: pointer; text-align: center; margin: 1px; font-family: 'Roboto', sans-serif; font-weight: 700; font-size: 32px; position: relative; vertical-align: middle; } .cella i { position: absolute; transform: translate(50%,-50%); top: 50%; right: 50%; } .cella:hover { background-color: #aaa; } .aperta { background-color: #fff; } .colore-1 {color: blue;} .colore-2 {color: green;} .colore-3 {color: red;} .colore-4 {color: purple;} .mina { background-color: rgb(255,0,0); color: #000; } |
E il gioco è fatto.
Qui è possibile trovare una versione funzionante del gioco (con qualche aggiunta extra).