Snake avec classement
Le Snake, avec un classement des cinq meilleurs scores gardé dans le navigateur et un pseudo à saisir quand on y entre. Le bon exercice pour trier, couper et enregistrer une liste de records.
- Décider si un score entre au classement
- Trier et limiter une liste d'objets
- Enregistrer et relire un classement sans faire confiance aux données
Pourquoi un Snake avec classement ? Un jeu sans record, on y joue une fois. Avec un tableau des meilleurs scores, on y revient pour battre le sien, ou celui d'un ami sur le même ordinateur.
Le jeu reprend le Snake de base. À la fin de chaque partie, on vérifie si le score mérite une place : il faut qu'il reste de la place, ou qu'il batte le dernier du classement. Si oui, on demande un pseudo, on ajoute l'entrée, on trie du plus haut au plus bas et on garde les cinq premiers.
Le classement est enregistré dans localStorage. À la relecture, on vérifie chaque entrée : un score modifié à la main ne doit pas casser l'affichage.
Pourquoi en JavaScript simple ? Un tableau, un sort et un slice. Dans la démo, le cadre isolé bloque le stockage : le classement y vit en mémoire.
LE CLASSEMENT
const CLE = 'snake-classement', MAX = 5; function lire() { try { const v = JSON.parse(localStorage.getItem(CLE)); // on ne garde que les entrées bien formées return Array.isArray(v) ? v.filter(e => typeof e?.s === 'number' && typeof e?.n === 'string').slice(0, MAX) : []; } catch { return []; } } let classement = lire();EST-CE QUE CE SCORE ENTRE ?
Oui s'il reste de la place, ou s'il bat le dernier.
const entre = score => score > 0 && (classement.length < MAX || score > classement.at(-1).s);
AJOUTER, TRIER, COUPER
function ajouter(pseudo, score) { classement.push({ n: pseudo.slice(0, 12), s: score, d: new Date().toISOString().slice(0, 10) }); classement.sort((a, b) => b.s - a.s); classement = classement.slice(0, MAX); try { localStorage.setItem(CLE, JSON.stringify(classement)); } catch { /* stockage bloqué */ } }À LA FIN DE LA PARTIE
function fin(score) { if (entre(score)) formulairePseudo.hidden = false; else message.textContent = `Perdu : ${score} fruits`; }AFFICHER SANS RISQUE
Le pseudo vient de l'utilisateur : textContent, jamais innerHTML.
function rendre() { liste.replaceChildren(...classement.map((e, i) => { const li = document.createElement('li'); li.textContent = `${i + 1}. ${e.n} : ${e.s}`; return li; })); }
PIÈGE 1 : Trier dans le mauvais sens
Mauvais : classement.sort(). Les nombres sont triés comme du texte : 9 passe devant 14.
Correct : sort((a, b) => b.s - a.s) pour un tri numérique, du plus grand au plus petit.
PIÈGE 2 : Faire confiance au stockage
Mauvais : afficher ce qui est relu tel quel. Un pseudo modifié à la main avec du HTML, ou un score en texte, et l'affichage casse.
Correct : vérifier le type de chaque champ à la lecture, et afficher avec textContent.
PIÈGE 3 : Croire que le score est vrai
Mauvais : envoyer le score du navigateur à un classement mondial tel quel. N'importe qui peut taper 9999 dans la console.
Correct : un classement local reste local. Pour un classement en ligne, le serveur doit pouvoir vérifier la partie, ou au moins limiter les envois.
- Un classement par jour ou par semaine, en filtrant sur la date.
- Le meilleur score affiché en haut pendant la partie, pour savoir ce qu'il faut battre.
- Un classement partagé en ligne avec une base comme Supabase, et des règles d'envoi côté serveur.
- Ne relisez le stockage qu'au chargement : ensuite, la copie en mémoire fait foi.
- Écoutez l'événement storage pour mettre à jour le classement si un autre onglet enregistre un score.
Le classement est un useState initialisé avec useState(lire).
Un hook useClassement(cle, max) regroupe lire, entre et ajouter pour d'autres jeux.
Le formulaire de pseudo apparaît selon un état « en attente d'enregistrement », pas avec hidden.
Chargement des commentaires…