Deux joueurs, sur le même appareil. Logique de jeu en JavaScript pur — aucune librairie.
Deux personnes devant le même écran. X commence toujours. On clique une case vide, le symbole s'inscrit, et la ligne d'état au-dessus de la grille annonce à qui revient le tour. Une case déjà occupée ne répond pas au clic, et dès qu'une partie est décidée le plateau se fige.
Le pointage sous la grille compte les victoires de X, celles de O et les parties nulles. Le bouton Recommencer vide le plateau et redonne la main à X, mais ne remet pas les compteurs à zéro. C'est voulu: ça permet de jouer une série au complet sans perdre le fil du pointage.
Tout tient dans un fichier de moins de quatre-vingts lignes, sans librairie, enveloppé dans une fonction anonyme immédiatement appelée pour ne rien laisser traîner dans l'espace global.
L'état de la partie tient dans trois variables. cells est un tableau plat de neuf
entrées créé avec new Array(9).fill(null), current vaut la chaîne
"X" ou "O", et gameOver est un simple drapeau. Pas d'objet
plateau, pas de matrice à deux dimensions: un index de 0 à 8 suffit, et il correspond exactement à
l'ordre dans lequel la grille CSS place les cases à l'écran.
La détection de victoire s'appuie sur WIN_LINES, une table de huit triplets d'index
écrits à la main: trois rangées, trois colonnes, deux diagonales. checkWinner parcourt
ces triplets et retourne le symbole dès que les trois cases sont remplies et identiques. Si aucune
ligne ne sort et que cells.every confirme qu'il ne reste plus de case libre, la fonction
retourne la chaîne "draw". L'ordre des deux tests compte: la victoire est vérifiée avant
le plateau plein, sinon un coup gagnant joué sur la neuvième case serait compté comme une nulle.
Le rendu est volontairement naïf. render vide boardEl.innerHTML, recrée
les neuf éléments et rattache neuf écouteurs de clic — à chaque coup, pas seulement au démarrage.
C'est du gaspillage assumé sur une grille de neuf cases, et ça élimine toute logique de
synchronisation entre le modèle et le DOM: l'affichage reste en tout temps une fonction directe du
contenu de cells. La classe CSS de chaque case est dérivée de sa valeur, ce qui donne
les deux couleurs sans aucune ligne de code supplémentaire.
Le vrai apprentissage n'est pas la détection de victoire, qui se règle avec une table de huit
triplets. C'est la séparation entre l'état et l'affichage. Tant que cells demeure la
seule source de vérité, je peux tout redessiner sans me demander ce qui est déjà à l'écran.
J'ai aussi vu la limite de l'approche. Recréer les écouteurs à chaque rendu passe très bien pour neuf cases, mais deviendrait une fuite de performance sur une grille plus grande. La correction serait de construire les cases une seule fois et de déléguer le clic au conteneur parent en lisant l'index sur la cible de l'événement. Je garde la version naïve ici parce qu'elle se lit d'un seul coup d'œil, et parce que l'objectif de cette page est la lisibilité du code, pas l'optimisation.