Le jeu du Demi-Cercle

Ma propre version web du jeu "Longueur d'onde". C'est un face-à-face en temps réel où tu dois aligner une jauge sur un spectre selon un indice, le tout propulsé par un backend Flask et des WebSockets pour une synchro instantanée

Python Flask WebSockets Tailwind CSS SQLite

Aperçu du projet

Démo de Le jeu du Demi-Cercle

Le Jeu du Demi-Cercle

Ce projet est né d'une envie simple : jouer. En regardant une vidéo de Wankil Studio, j'ai découvert le jeu de société "Longueur d'onde" (Wavelength), un jeu d'ambiance basé sur l'empathie et la communication. Dans leur vidéo, ils utilisaient une version numérique faite sur mesure pour eux.

J'ai immédiatement voulu essayer ce jeu avec mes amis sur Discord, mais je me suis heurté à un mur : aucune version web gratuite, simple et accessible sur PC n'existait pour le grand public (seulement sur téléphone). Le jeu original est un jeu de plateau physique avec une roue en plastique.

C'était le prétexte idéal pour un défi technique : coder ma propre version du jeu de A à Z, en transformant une mécanique physique (tourner une roue) en une expérience web fluide et temps réel.

Le Concept du Jeu

Le "Jeu du Demi-Cercle" est une expérience coopérative et compétitive asymétrique qui se joue ici en 1 contre 1 (ou en équipes partageant un écran).

La Mécanique Cœur

Tout repose sur un spectre continu représenté par un demi-cercle. Ce spectre est défini par une dualité (le Thème).
Exemple : "Inutile" (Gauche) <---> "Indispensable" (Droite).

Le Déroulement d'une Manche

  1. Le Psychic (L'indice) :
  2. Il est le seul à voir la cible (une zone colorée sur le cadran).
  3. Il doit inventer un indice (un mot ou une phrase) qui positionne conceptuellement la cible sur le spectre.
  4. Exemple : Si la cible est à 80% vers la droite (très "Indispensable") et le thème est "Objet du quotidien", il pourrait dire "Smartphone".

  5. Le Devin (L'aiguille) :

  6. Il ne voit pas la cible.
  7. Il voit le thème et l'indice.
  8. Il doit interpréter l'indice et déplacer l'aiguille rouge sur le demi-cercle pour tenter de s'aligner avec la cible invisible.

  9. La Révélation :

  10. Une fois le choix validé, la cible apparaît.
  11. Le score est calculé en fonction de la précision (distance entre l'aiguille et le centre de la cible).

Stack Technique & Choix d'Architecture

Pour ce projet, j'ai privilégié une approche "Fullstack" légère mais robuste, capable de gérer des connexions persistantes.

Backend : Python & Flask

J'ai choisi Flask plutôt que Django pour sa légèreté. Je voulais garder un contrôle total sur l'architecture sans m'encombrer d'un ORM lourd ou d'une structure trop rigide.
- Routing : Gestion des routes classiques (/, /game, /api/...).
- Session Management : Utilisation de Flask-Session pour gérer l'identité des joueurs de manière sécurisée via des cookies signés.

Temps Réel : WebSockets (Flask-SocketIO)

C'est le moteur du jeu. Pour reproduire la sensation de jouer autour d'une même table, il fallait que les actions soient instantanées.
- Synchronisation : Quand le Devin bouge sa souris, l'aiguille bouge en même temps sur l'écran du Psychic.
- Rooms : Utilisation du système de "rooms" de Socket.IO pour isoler chaque partie (game_ID). Cela permet d'avoir plusieurs parties simultanées sur le même serveur sans interférence.

Frontend : Vanilla JS & Tailwind CSS

  • Tailwind CSS : J'ai utilisé Tailwind pour le styling. Cela m'a permis de prototyper l'interface extrêmement vite grâce aux classes utilitaires, tout en gardant un design moderne et responsive sans écrire des centaines de lignes de CSS personnalisé.
  • Vanilla JS : Pas de React ou Vue ici. Je voulais manipuler le DOM directement pour gérer la logique du SVG et les événements WebSocket. Cela rend l'application très légère à charger.

Base de Données : SQLite

Pour la persistance, j'ai opté pour SQLite avec un pattern de "Context Manager" en Python (with get_db() as conn:). Cela assure que les connexions sont toujours proprement fermées, même en cas d'erreur. La base stocke l'état complet de la partie, permettant de reprendre une partie en cours même après un crash du navigateur.

Défis Techniques & Implémentation

1. La Mathématique du SVG (Scalable Vector Graphics)

Le plus gros challenge frontend a été de créer ce demi-cercle interactif. Ce n'est pas une simple image, c'est un tracé vectoriel dynamique.
- Courbes de Bézier : L'arc est dessiné avec une commande path SVG (Q pour Quadratic Bézier).
- Projection de Coordonnées : J'ai dû écrire des fonctions pour traduire la position de la souris (x, y en pixels) en une valeur de 0 à 10 sur l'arc de cercle, et inversement pour placer l'aiguille.

function getPointOnArc(position) {
  const t = position / 10;
  const x = (1-t)*(1-t)*x0 + 2*(1-t)*t*cx + t*t*x1;
  const y = (1-t)*(1-t)*y0 + 2*(1-t)*t*cy + t*t*y1;
  return { x, y };
}

2. Gestion de l'État (State Machine)

Une partie est une machine à états complexe. À tout moment, le serveur doit savoir si on attend un indice, une réponse, ou si on affiche les résultats.
- J'ai implémenté des vérifications strictes côté serveur (@login_required, vérification du tour de jeu) pour empêcher la triche ou les actions hors tour via des appels API directs.
- Si un joueur rafraîchit sa page, le serveur renvoie l'état exact de la partie (position de l'aiguille, thème actuel, rôle) grâce à la base de données.

3. UX et Feedback Visuel

Avec Tailwind, j'ai pu ajouter facilement des animations et des transitions.
- L'aiguille suit la souris avec fluidité.
- Lors de la révélation, un arc de cercle se dessine entre l'aiguille et la cible pour visualiser l'erreur.
- Les scores s'incrémentent dynamiquement.

🚀 Améliorations Futures

Ce projet est fonctionnel, mais j'ai encore des idées pour l'améliorer :
- Mode Spectateur : Permettre à d'autres amis de rejoindre la room juste pour regarder sans jouer.
- Plus de Thèmes : Enrichir la base de données de thèmes par défaut.

Publié en October 2025