domenica 8 aprile 2012

[GUIDA] Creare il gioco 8 PUZZLE in Javascript

Come da titolo, una guida per ricreare il famoso giochino che sicuramente vi è capitato almeno una volta di trovare come sorpresa nell' uovo di Pasqua. Ne realizzeremo insieme uno in Javascript in modo che possiate pubblicarlo direttamente sul vostro sito web o giocarlo sul vostro browser preferito.









Per chi non lo conoscesse, si sta parlando del famoso Sliding puzzle. Si tratta di quel gioco composto da una cornice quadrata in cui sono presenti vari tasselli, numerati in ordine sparso, con uno mancante. Lo scopo è farli scorrere in modo da mettere i numeri in sequenza .
Nella guida darò per scontato che conosciate un po' di javascript e un po' di html. Se così non fosse vi rimando al sito www.html.it, dove troverete delle ottime guide per imparare questi linguaggi.

A fine articolo trovate il link con il gioco già pronto, dovete solo scaricarlo ed eseguirlo nel vostro browser.

Dato che incorporeremo il codice in una pagina web, a meno che non ne abbiate una già pronta, consiglierei di farne una apposita.
Apriamo quindi il nostro editor html (anche un qualunque editor di testo va bene, basta salvare il file con estensione .html e non .txt) e creiamo la pagina in questo modo:


<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns = "http://www.w3.org/1999/xhtml"> 
<head>>
<title>Box Puzzle</title>
<script type="text/javascript">
<!-- QUI IL NOSTRO SCRIPT DEL PUZZLE 8 -->
</script>
</head>
 
<body>

</body>
</html>


Come vedete, il codice per lo script si troverà nell'<head>, il gioco vero e proprio invece lo disegneremo nel <body>.

Ora disegnamo la nostra cornice con tutti i tasselli numerati. Questa non è altro che una tabella, contenuta in un form per poterla elaborare, dove ogni cella rappresenta un tassello. Come già detto la inseriremo nel body:

<body>
<form name="box">
</center>
<table align="center">
<tr>
<td ><input name="casella1" value="8" onClick="test(this.value)" readonly style="text-align: center; width:50px; height:50px; background-color: green; border-style: solid; border-color: #800000"></td>
<td ><input name="casella2" value="5" onClick="test(this.value)" readonly style="text-align: center; width:50px; height:50px; background-color: green; border-style: solid; border-color: #800000"></td>
<td ><input name="casella3" value="2" onClick="test(this.value)" readonly style="text-align: center; width:50px; height:50px; background-color: green; border-style: solid; border-color: #800000"></td>
</tr>
<tr>
<td ><input name="casella4" value="6" onClick="test(this.value)" readonly style="text-align: center; width:50px; height:50px; background-color: green; border-style: solid; border-color: #800000"></td>
<td ><input name="casella5" value="3" onClick="test(this.value)" readonly style="text-align: center; width:50px; height:50px; background-color: green; border-style: solid; border-color: #800000"></td>
<td ><input name="casella6" value="4" onClick="test(this.value)" readonly style="text-align: center; width:50px; height:50px; background-color: green; border-style: solid; border-color: #800000"></td>
</tr>
<tr>
<td ><input name="casella7" value="1" onClick="test(this.value)" readonly style="text-align: center; width:50px; height:50px; background-color: green; border-style: solid; border-color: #800000"></td>
<td ><input name="casella8" value="7" onClick="test(this.value)" readonly style="text-align: center; width:50px; height:50px; background-color: green; border-style: solid; border-color: #800000"></td>
<td ><input name="casella9" value="" onClick="test(this.value)" readonly style="text-align: center; width:50px; height:50px; background-color: green; border-style: solid; border-color: #800000"></td>
</tr>
</table>
</form>
</body>


Abbiamo creato un form che contiene la nostra tabella di dimensioni 3x3. Ogni cella contiene un <input> con un suo stile (da impostare a piacimento) che da la forma alla cella. Inoltre, se avete notato, è presente il parametro onClick="test(this.value)", cioè cliccando sulla casella verrà eseguita la funzione test() su this.value che è il numero contenuto nella cella. La funzione la definiremo nell'<head> a breve. All'apertura della pagina, ogni cella verrà creata con all'interno un valore prestabilito value.

Torniamo allo script vero e proprio e cominciamo a macinare un po' di codice.

<script type="text/javascript">
<!--
var pos= new Array("8","5","2","6","3","4","1","7","");
var vic= new Array("1","2","3","4","5","6","7","8","");
var blank,contmoss=0;
-->

Abbiamo creato due vettori: pos contiene tutti i valori delle celle della tabella in ordine. L'indice del vettore rappresenta il numero della cella (il tassello), il suo contenuto rappresenta il valore. I valori sono già stati assegnati perchè il campo value di ogni cella è impostato inizialmente, come detto sopra. Infatti se notate la "casella1"  ha value="8", così come il primo elemento di pos.
L'array vic invece contiene tutti i valori in ordine, cioè come dovrebbero trovarsi in caso di vittoria.
La viariabile blank contiene la posizione della cella bianca nel vettore, contmoss invece è un contatore delle mosse effettuate e verrà incrementato ad ogni spostamento di una casella.

Prima abbiamo visto che ogni cella della tabella,se cliccata, richiama la funzione test().
Definiamo quindi questa funzione:

function test(num)
{
    var flag=0,test=0;
    var p=trova_pos(num);
    if(p==-1) alert("Errore! Si prega di ricaricare la pagina.");
    blank=trova_pos("");
    if(blank==-1) alert("Errore! Si prega di ricaricare la pagina.");

    if(p!=blank)
   {
num è il numero contenuto nella cella della tabella. Ora, dato che abbiamo definito un vettore a cui ogni cella corrisponde una casella, cerco la posizione nel vettore della casella indicata: trova_pos(num). Stessa cosa per la casella vuota: trova_pos("").
Se non è stata cliccata la casella vuota, cioè (p!=blank),quello che si vuole fare una volta che si clicca su una cella è di spostarla al posto di quella vuota.
Questo consiste in uno scambio tra le due, che è possibile ovviamente solo se sono adiacenti. Ma quando due celle sono adiacenti. Usando un vettore pos per rappresentare la tabella, dobbiamo immaginarcela come se fosse tutta su un'unica riga, cioè invece di avere 3 righe e 3 colonne avesse una sola riga e 9 colonne (cioè il vettore). In questo caso una cella è adiacente ad un'altra se si trova subito a fianco ad essa (cioè ha una distanza pari a 1, oppure se si trova sopra o sotto di essa. Dato che il sopra e il sotto, avendo una sola riga, non esistono, se sono adiacenti hanno distanza pari a 3.
Calcoliamo questa distanza semplicemente calcolando la differenza tra la posizione della cella bianca e quella cliccata. Se è uguale a 3 o a 1, sono adiacenti
     if(p < blank) test=blank-p;
     else test=p-blank;
     if(test==3)
     {
sono in una cella adiacente a quella vuota, posso effettuare lo scambio delle due e contare una mossa effettuata. La variabile flag servirà solo ad indicarci più avanti che effettivamente abbiamo fatto una mossa.
          scambia(blank,p);
          flag=1;
          contmoss++;
     }
     else if(test==1)
     {
Potrete notare che le celle della tabella ai margini (le 3 dell'ultima colonna) hanno una distanza che pari a 1 nel vettore con quelle della prima colonna. Ad esempio se la casella bianca fosse nella cella della prima colonna, seconda riga e venisse cliccata quella della terza colonna, prima riga, la distanza calcolata sarebbe 1. Lo scambio verrebbe quindi effettuato, ma questo non è lecito perchè pur avendo distanza 1 le due non sono adiacenti! Devo controllare questo fatto per ogni cella della terza colonna e far si che invece di scambiare le due celle, semplicemente si ritorni.
          if((p==2)&&(blank==3)||(p==3)&&(blank==2)||(p==5)&&(blank==6)||(p==6)&&(blank==5))
         {
               return;
         }
         else
         {
             scambia(blank,p);
             flag=1;
             contmoss++;
         }
    }
Arrivati fin qui posso aver effettuato una mossa con successo oppure no. Questo ci viene detto dalla variabile flag. Se ho mosso, devo controllare se ho vinto.
   if(flag==1)
   {
       if(controlla_vittoria()) alert("Complimenti!!\nHai vinto effettuando "+contmoss+" mosse.\nFai     'Nuovo gioco' e migliora il tuo record!");
   }
  }
  else return;
}
Ecco le tre funzioni usate in test:

function controlla_vittoria()
{
     for(var i=0; i<pos.length;i++)
    {
         if(pos[i]!=vic[i]) return false;
    }
    return true;
}
Scorro tutto il vettore corrispondente alla tabella e confronto ogni valore con quello per la vittoria. Se corrispondono tutti allora ritorno true, cioè vittoria.
function trova_pos(n)
{
       for(var i=0; i<pos.length;i++)
      {
           if(n==pos[i]) return i;
      }
      return -1;
}
n è il valore da cercare nel vettore pos. Se viene trovato, si ritorna la sua posizione.

function scambia(b,p) {
     var temp;
     temp=pos[b];
     pos[b]=pos[p];
     pos[p]=temp;
     refresh();
}
b e p sono i due valori da scambiare. Viene effettuato un semplice scambio tra i due nel vettore.
Manca un'ultima cosa: il pulsante per iniziare una nuova partita!
<input type="button" name="newg" value="Nuovo gioco" onClick="shuffle()">questo andrà inserito nel body, prima del form.
Come vedete una volta cliccato richiama la funzione shuffle(), che andremo a definire tra le altre nell'<head>:

function shuffle()
{
      for (swaps=0; swaps<pos.length; swaps++)
     {
          i = Math.floor(Math.random()*pos.length);
          j = Math.floor(Math.random()*pos.length);
          holder = pos[i];
          pos[i] = pos[j];
          pos[j]=holder;
      }
      refresh();
      contmoss=0;
}
La funzione mischia a caso (usato Math.random() ) gli elementi del vettore. Poi con refresh() viene aggiornata la tabelle, rispecchiando l'array.

function refresh()
{
     document.box.casella1.value=pos[0];
     document.box.casella2.value=pos[1];
     document.box.casella3.value=pos[2];
     document.box.casella4.value=pos[3];
     document.box.casella5.value=pos[4];
     document.box.casella6.value=pos[5];
     document.box.casella7.value=pos[6];
     document.box.casella8.value=pos[7];
     document.box.casella9.value=pos[8];
}
Ogni cella della tabella ora ha i valori aggiornati corrispondenti a quelli del vettore pos.
Un'ultima chicca: andiamo a disabilitare la possibilita di selezionare il testo di una cella della tabella. E' una cosa puramente estetica quindi evitabile a piacimento.
Nel <body> mettiamo:
<SCRIPT type="text/javascript">
if (typeof document.onselectstart!="undefined") {
     document.onselectstart=new Function ("return false");
}
else {
     document.onmousedown=new Function ("return false");
     document.onmouseup=new Function ("return true");
}
</SCRIPT>
E con questo è tutto!
Spero che la guida vi sia piaciuta. Per qualunque dubbio lasciate un commento, vi risponderò il prima possibile.

Ecco il link al gioco funzionante (e quindi anche al codice completo):
http://dl.dropbox.com/u/62437037/8puzzle.html

Nessun commento:

Posta un commento