Nous avons créé Cards4.net pour qu’on puisse ouvrir un jeu de cartes et commencer à jouer sans compte ni publicité. Une fois la partie lancée, on doit pouvoir annuler un coup ou y revenir plus tard sans se demander si le tableau a changé. Une partie sauvegardée doit conserver les mauvaises décisions aussi fidèlement que les bonnes.
Répondre à cette attente a déterminé notre façon d’utiliser Phoenix et Elixir côté serveur, React dans le navigateur et PostgreSQL pour la persistance. Les règles, les commandes et les sauvegardes doivent toutes s’accorder sur ce que le joueur a fait.
Garder l’état de la partie sur le serveur
Au Klondike, déplacer une carte peut découvrir et retourner la carte située en dessous. Si le joueur appuie sur Annuler, les deux changements doivent être inversés. S’il quitte ensuite la partie, la position sauvegardée doit refléter le tableau auquel il est revenu. Nous gardons l’état de la partie sur le serveur pour que le traitement des coups, l’annulation et la persistance s’appuient tous sur la même représentation de cette position.
Quand un joueur lâche une carte, le navigateur envoie le coup voulu à notre backend Phoenix, où les règles le vérifient par rapport à l’état actuel. Si le coup est légal, le serveur l’applique, enregistre la position précédente pour Annuler et renvoie le tableau mis à jour. Le navigateur peut alors afficher le résultat, y compris les cartes révélées. Comme le serveur traite ces changements ensemble, le frontend n’a pas besoin de reconstituer les positions antérieures à partir de ce qui était visible à l’écran.
En multijoueur, cette organisation nous permet aussi de coordonner une table sans compter sur le navigateur d’un joueur pour décider de ce qui s’est passé. Les Phoenix Channels transmettent les actions au serveur et livrent aux joueurs les mises à jour qui en résultent. Puisque le serveur détient l’état complet de la partie, il peut envoyer à chaque place les informations que ce joueur a le droit de voir, tout en excluant les autres mains de la réponse.
Conserver une partie après la fin de sa session
Pendant le jeu, chaque session de solitaire conserve sa position dans un GenServer, un processus Elixir qui traite les appels entrants l’un après l’autre. La session délègue la vérification des règles au module de jeu approprié, ce qui permet au Klondike, à FreeCell et à Spider de partager le code de gestion des sessions et des sauvegardes.
Ce processus ne garde la position qu’en mémoire ; nous écrivons donc aussi des instantanés dans PostgreSQL. Lorsqu’une requête arrive pour une session arrêtée, l’application peut charger son état sauvegardé dans une nouvelle session. La récupération passe par ce code ; le superviseur ne redémarre pas automatiquement ces processus de session temporaires après un plantage.
Nous planifions les écritures séparément du traitement des coups pour éviter d’attendre la base de données à chaque coup. Par conséquent, la position en mémoire peut être en avance sur la dernière sauvegarde réussie. Tester la récupération consiste à vérifier ce qui a réellement été écrit et ce qui peut être restauré, y compris l’historique d’annulation.
Garder des commandes réactives
Placer les règles sur le serveur signifie que chaque coup exige un aller-retour réseau avant d’être accepté. Cela peut retarder le résultat sur une connexion lente et empêche le jeu hors ligne. Un solitaire fonctionnant entièrement dans le navigateur éviterait cette dépendance. Nous l’avons acceptée pour que la validation des règles, l’annulation et la progression sauvegardée puissent utiliser la même implémentation côté serveur.
L’interface doit tout de même réagir pendant qu’un joueur fait glisser une carte. Nos tableaux utilisent React et TypeScript, et Vite se charge du build du frontend. React gère le tableau et l’état de l’interaction, tandis qu’une boucle requestAnimationFrame déplace directement l’aperçu du glisser. L’aperçu suit ainsi le pointeur sans mise à jour d’état React à chaque image, même si l’acceptation du dépôt exige toujours une réponse du serveur.
Sur un téléphone, le geste lui-même demande aussi de l’attention. Les cartes sont plus petites, les colonnes peuvent être longues, et un toucher peut signifier sélectionner une carte ou commencer un glisser. Nous testons ces interactions séparément des règles, car savoir qu’une pile peut légalement être déplacée ne nous dit pas grand-chose sur la facilité avec laquelle quelqu’un peut la saisir. Les gestes annulés et les dépôts illégaux doivent aussi laisser le tableau dans un état que le joueur comprend.
Reproduire ce qui n’a pas fonctionné
Quand un joueur signale que le tableau s’est comporté de façon inattendue, nous devons recréer la position qu’il jouait. Les donnes de solitaire prises en charge sont générées à partir de graines : le même jeu, la même variante et la même graine produisent donc la même disposition de départ avec la même implémentation.
Pour un problème qui survient plus tard, il nous faut aussi les coups qui y mènent. Une capture d’écran peut montrer ce qui semblait anormal, tandis que la graine et la suite de coups nous permettent de suivre la partie jusqu’à ce point. Conserver ces entrées avec un test de régression nous donne un moyen de revérifier le comportement après une correction. Cela signifie aussi que les modifications du code de mélange ou de distribution exigent des vérifications de compatibilité si les anciennes graines doivent continuer à produire les mêmes dispositions.
Nous utilisons également des rapports d’erreurs et des mesures de performance pour enquêter sur les pannes et les pages lentes. Ils servent un autre objectif que les chiffres de fréquentation de Plausible, que nous ne chargeons qu’après consentement aux statistiques. Les pages vues peuvent nous dire que quelqu’un est passé ; elles ne peuvent pas établir qu’une partie a été correctement sauvegardée ou restaurée. La politique de confidentialité décrit les données collectées par ces systèmes distincts.
Ce que nous ferions plus tôt
Sur un autre projet de jeu de cartes, nous ferions passer un jeu par un cycle complet de jeu, d’annulation, de sauvegarde et de reprise avant d’élargir le catalogue. Nous conserverions cette séquence sous forme de test avec une donne fixe, afin de pouvoir vérifier les changements ultérieurs à partir du même point de départ. Cela nous donnerait un moyen reproductible de repérer une modification qui fait fonctionner un coup à l’écran mais casse la position restaurée après un rechargement. Pour un site auquel on revient entre deux autres occupations, réussir ce retour mérite qu’on y consacre du temps tôt dans le développement.
Questions fréquentes
Quelles technologies Cards4.net utilise-t-il ?
Le backend utilise Phoenix et Elixir avec PostgreSQL. Le frontend utilise React, TypeScript et Vite. Les actions de solitaire passent par REST, tandis que les tables multijoueurs utilisent les Phoenix Channels. Les tableaux de jeu n’utilisent pas LiveView.
Puis-je jouer sans compte ?
Oui. Les parties occasionnelles sont accessibles aux invités. Si tu veux un compte, la connexion utilise un lien à usage unique envoyé par e-mail. Le site ne te demande pas de créer de mot de passe.
Les jeux fonctionnent-ils hors ligne ?
Non. Les coups sont validés sur le serveur : le jeu exige donc une connexion Internet.
Quels outils de statistiques le site utilise-t-il ?
Plausible se charge après consentement aux statistiques. L’application dispose aussi d’un suivi des erreurs et d’une surveillance des performances distincts. Consulte la politique de confidentialité pour les détails.