🎮 Tic-Tac-Toe

Deux joueurs, sur le même appareil. Logique de jeu en JavaScript pur — aucune librairie.

Au tour de X
X: 0 O: 0 Nulles: 0

Comment jouer

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.

Comment c'est construit

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.

Ce que j'en ai retenu

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.