Présentation
Cascade est un jeu de réflexion dont le but est de détruire des séries de blocs alignés de la même couleur. Lorsqu’un bloc est détruit l’ensemble des blocs situés au dessus descend pour combler le vide. Lorsqu’une colonne entière est détruite l’ensemble des colonnes suivantes se déplace pour combler le vide. La partie est terminée lorsqu’il ne reste plus de blocs à détruire ou si plus aucun bloc n’a de bloc voisin permettant de créer une série destructible.
C’est assez maigre comme définition mais cela suffit pour nous attaquer à l’exercice, ce type de jeu étant une variante récente du très célèbre Tetris, aucune définition n’existe sur Wikipedia, il est cependant intéressant à étudier car il pose des problèmes de récursivité et d’optimisation.
Le code Javascript
// variables var canvas, ctx, W, H, posX, posY, T, C, L, i, images, stock, voisins, valeurs, images; // préparation du jeu window.onload = function() { canvas = document.getElementById('canvas'); ctx = canvas.getContext('2d'); W = 480; H = 480; canvas.width = W; canvas.height = H; posX = canvas.offsetLeft; posY = canvas.offsetTop; T = 32; C = W/T; loadImages(5); } // chargement des images function loadImages(nbImg){ images = []; for(i=1; i<nbImg+1; i++){ var b = new Image(); b.src = "assets/tuile"+i+".png"; b.onload = function() { images.push(this); if(--nbImg==0) init(); }; } } // initialisation du jeu function init() { stock = []; voisins = []; valeurs = []; for (i=0;i<C*C;i++){ voisins.push([]); valeurs.push(parseInt(Math.random()*4+1)); stock.push({x : parseInt(i%C)*T, y : parseInt(i/C)*T, id : i, width : T, height : T, frame : valeurs[i]}); } render(); canvas.addEventListener("mousedown", jouer, false); canvas.addEventListener("mouseup", checkTile, false); } // cliquer sur une case function jouer(e){ decouvre(parseInt((e.clientX-posX)/T)+parseInt((e.clientY-posY)/T)*C); } // découvrir les cases de même couleur function decouvre(n){ trouveVoisin(n,stock[n].x/T,stock[n].y/T,C-1,valeurs[n]); if(voisins[n].length){ var v = voisins[n]; for (var h=0; h<v.length; h++){ if (v[h].frame!=5) { v[h].frame=5; decouvre(v[h].id); valeurs[v[h].id] = 5; } } voisins[n] = []; valeurs[n] = 5; stock[n].frame = 5; render(); } } // trouver les cases vides voisines function trouveVoisin(i,X,Y,L,F){ if(X>0 && valeurs[i-1]==F) voisins[i].push(stock[i-1]); if(X<L && valeurs[i+1]==F) voisins[i].push(stock[i+1]); if(Y>0 && valeurs[i-C]==F) voisins[i].push(stock[i-C]); if(Y<L && valeurs[i+C]==F) voisins[i].push(stock[i+C]); } // vérifier les tuiles function checkTile(e) { var x = C; while(x--) checkColonne(x); checkLigne(); for (var j=0; j<stock.length;j++) stock[j].frame = valeurs[stock[j].id]; render(); verifieJeu(); } // vérifie une colonne function checkColonne(c){ var y = 0; while(y<C) { i = c+y*C; if (valeurs[i+C] && valeurs[i] != 5 && valeurs[i+C] == 5) { valeurs[i+C]= valeurs[i]; valeurs[i] = 5; checkColonne(c); } y++; } } // vérifie une ligne function checkLigne(){ var x = C; while(x--) checkDecalage(x); } function checkDecalage(c){ var t = valeurs.length-C+c if(valeurs[t] !=5 && valeurs[t-1] == 5 && t-1>valeurs.length-C-1){ var y = 0; while(y<C) { i = c+y*C; valeurs[i-1] = valeurs[i]; valeurs[i] = 5; y++; } checkLigne() } } // vérifie si il reste des combinaisons jouables function verifieJeu(){ var test = true; for (var n=0; n<stock.length; n++){ if(valeurs[n]!=5){ trouveVoisin(n,stock[n].x/T,stock[n].y/T,C-1,valeurs[n]); if(voisins[n].length) test=false; voisins[n] = []; } } if (test) finPartie(); } // fin de partie function finPartie(){ alert("Fin de partie, cliquez pour rejouer."); init(); } // Dessine le jeu function render() { for(var i=0; i<stock.length; i++){ ctx.drawImage(images[stock[i].frame-1], stock[i].x, stock[i].y); } }