Skip to content
← 1001 Widgets
N° 051 · ●●●

Snake with leaderboard

Snake, with a top-five leaderboard kept in the browser and a nickname to enter when you make it. The right exercise to sort, trim and save a list of records.

JS
What you'll learn
  • Decide whether a score makes the leaderboard
  • Sort and cap a list of objects
  • Save and read back a leaderboard without trusting the data
Why this language

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.

How it works
  1. 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();
  2. 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);
  3. 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é */ }
    }
  4. À LA FIN DE LA PARTIE

    function fin(score) {
      if (entre(score)) formulairePseudo.hidden = false;
      else message.textContent = `Perdu : ${score} fruits`;
    }
  5. 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;
      }));
    }
Pitfalls

PIÈGE 1 : Trier dans le mauvais sens

Pitfall

Mauvais : classement.sort(). Les nombres sont triés comme du texte : 9 passe devant 14.

Fix

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

Pitfall

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.

Fix

Correct : vérifier le type de chaque champ à la lecture, et afficher avec textContent.

PIÈGE 3 : Croire que le score est vrai

Pitfall

Mauvais : envoyer le score du navigateur à un classement mondial tel quel. N'importe qui peut taper 9999 dans la console.

Fix

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.

PERSONNALISER
  • 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.
OPTIMISATION
  • 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.
VERS REACT

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.

Comments
0/1000

Loading comments…

Audience measurement

With your consent, this site measures its traffic with Google Analytics and the Meta pixel, via Google Tag Manager. Nothing is loaded until you choose, and you can change your mind at any time with the "Cookies" link at the bottom of the page.