Aller au contenu
← 1001 Widgets
N° 020 · ●●○

Jeu de mémoire

Seize cartes retournées, huit paires à retrouver, et un compteur de coups. Le bon exercice pour gérer un état de jeu qui dépend de deux clics.

HTMLCSSJS
Ce qu'on apprend
  • Mélanger un tableau avec Fisher-Yates
  • Comparer deux choix puis attendre
  • Bloquer les clics pendant une animation
Pourquoi ce langage

Pourquoi un jeu de mémoire ? Parce qu'il cache une vraie difficulté : le jeu ne réagit pas à un clic, mais à une paire de clics. Il faut garder le premier choix en mémoire, comparer au second, puis laisser le temps de voir avant de retourner les cartes.

Pendant ce temps, un troisième clic ne doit rien casser. C'est le premier contact avec un état « occupé ».

Pourquoi en JavaScript simple ? Un tableau mélangé, un tableau de deux cartes choisies, un booléen. Le retournement est un simple rotateY en CSS.

Comment ça marche
  1. MÉLANGER

    Fisher-Yates : chaque ordre a exactement la même chance. sort(() => Math.random() - 0.5) ne l'est pas.

    function melanger(t) {
      for (let i = t.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [t[i], t[j]] = [t[j], t[i]];
      }
      return t;
    }
  2. L'ÉTAT

    const SYMBOLES = ['山', '川', '火', '水', '木', '月', '日', '花'];
    let cartes = melanger([...SYMBOLES, ...SYMBOLES]);
    let choix = [];
    let bloque = false;
    let coups = 0;
  3. LE HTML

    Une carte est un bouton : clavier et lecteur d'écran fonctionnent d'office.

    jeu.innerHTML = cartes.map((s, i) =>
      `<button class="carte" data-i="${i}"><b>?</b><span>${s}</span></button>`).join('');
  4. RETOURNER

    jeu.addEventListener('click', e => {
      const c = e.target.closest('.carte');
      if (!c || bloque || c.classList.contains('vue')) return;
      c.classList.add('vue');
      choix.push(c);
      if (choix.length < 2) return;
      coups++;
      const [a, b] = choix;
      choix = [];
      if (cartes[a.dataset.i] === cartes[b.dataset.i]) return; // paire trouvée : elles restent visibles
  5. ATTENDRE AVANT DE CACHER

      bloque = true;
      setTimeout(() => {
        a.classList.remove('vue');
        b.classList.remove('vue');
        bloque = false;
      }, 800);
    });
Pièges

PIÈGE 1 : Le mélange biaisé

Piège

Mauvais : cartes.sort(() => Math.random() - 0.5). Certaines positions reviennent bien plus souvent que d'autres.

Fix

Correct : l'algorithme de Fisher-Yates, qui échange chaque carte avec une position au hasard avant elle.

PIÈGE 2 : Le troisième clic

Piège

Mauvais : laisser cliquer pendant les 800 ms d'attente. On retourne trois cartes et le jeu se perd.

Fix

Correct : un booléen bloque, vérifié au début du gestionnaire de clic.

PIÈGE 3 : Cliquer deux fois la même carte

Piège

Mauvais : accepter un second clic sur une carte déjà retournée. Elle forme une paire avec elle-même.

Fix

Correct : ignorer les cartes qui ont déjà la classe vue ou trouvée.

PERSONNALISER
  • Une grille 6 × 6 pour une version difficile : il suffit de 18 symboles.
  • Un chronomètre qui démarre au premier clic et s'arrête à la dernière paire.
  • Des images à la place des kanji, pour un jeu à thème.
OPTIMISATION
  • Préchargez les images avant d'afficher la grille, sinon la première carte retournée apparaît vide.
  • Annoncez les paires trouvées avec une zone aria-live pour les lecteurs d'écran.
VERS REACT

Les cartes deviennent un tableau d'objets { symbole, vue, trouvee } dans un useState.

Le setTimeout vit dans un useEffect qui réagit aux deux cartes choisies, et se nettoie au démontage.

Le mélange se fait une seule fois, dans la fonction d'initialisation de useState.

Commentaires
0/1000

Chargement des commentaires…