🐍 Serpents et Échelles

Un jeu de serpents et échelles en JavaScript pur sur canvas HTML5. Jouez contre l'ordinateur ou à deux : lancez le dé (ou barre d'espace), grimpez les échelles, évitez les serpents, et atteignez la case 100.

🪜 Échelle : tu montes 🐍 Serpent : tu descends
À toi de jouer !

🏆 Victoire !

Comment jouer

Le plateau compte cent cases numérotées en boustrophédon: la 1 en bas à gauche, la 10 en bas à droite, la 11 juste au-dessus de la 10, et ainsi de suite jusqu'à la 100 en haut à gauche. On lance le dé avec le bouton ou la barre d'espace, et le pion avance du nombre affiché.

Neuf échelles font monter, dix serpents font descendre. La case 1 mène à la 38 et la 80 monte directement à la 100; à l'inverse, la 98 redescend à la 78 et la 49 tombe jusqu'à la 11. Deux règles de la maison sont codées: il faut arriver exactement sur la case 100, sinon le pion reste où il est, et un 6 donne un tour supplémentaire. En mode contre l'ordinateur, l'adversaire tire le même dé que vous, sans avantage caché.

Comment c'est construit

Tout tient dans un seul fichier, sans bibliothèque. Le parcours se résume à deux tables. LADDERS associe 1 à 38, 4 à 14, 9 à 31, 21 à 42, 28 à 84, 36 à 44, 51 à 67, 71 à 91 et 80 à 100; SNAKES fait l'inverse pour dix cases. Après chaque déplacement, une seule consultation de ces tables décide si le pion grimpe ou glisse.

La fonction cellToXY convertit un numéro de case en coordonnées: division par dix pour la rangée, reste pour la colonne, inversion de la colonne une rangée sur deux, puis retournement de l'axe vertical pour que la rangée zéro se retrouve en bas.

Le décor ne change jamais, alors buildBoardLayer le dessine une seule fois dans un canvas hors écran: cases, numéros, échelles à deux montants et serpents tracés comme une sinusoïde de quarante segments. Chaque image de la boucle se contente d'un drawImage de ce calque, puis des deux pions par-dessus.

Le déplacement passe par une file d'attente de cases. moveAlong empile les cases une à une, ce qui donne le saut de case en case; slideTo n'empile qu'une destination, ce qui donne la glissade continue du serpent ou de l'échelle. step rapproche le pion de sa cible à vitesse constante et déclenche le rappel quand la file est vide.

Le tour est écrit en async/await: takeTurn attend l'animation du dé, puis le déplacement, puis la vérification serpent ou échelle. Le code se lit dans le même ordre que les règles. Le dé, lui, est dessiné à partir d'une table de positions de points, avec une rotation amortie sur 750 ms.

Ce que j'en ai retenu

Le pion à la case 0 est affiché sur la case 1, parce que la position de départ emprunte les coordonnées de la première case. Personne ne le remarque en jouant, mais c'est une confusion entre l'état du jeu et sa représentation, et c'est le genre de raccourci qui coûte cher quand le programme grossit.

Deux réflexes me restent. Séparer un calque statique d'un calque animé change tout: le plateau et ses vingt tracés ne sont calculés qu'une fois. Et une boucle doit survivre à un onglet en arrière-plan: requestAnimationFrame est alors suspendu, donc un setTimeout de secours prend le relais quand document.hidden est vrai, sinon la partie se fige au milieu d'un déplacement.