Kevin L'Hereec 44d4eb7e96 fix: bugs critiques du système d'événements + stabilisation E2E
Trois bugs fonctionnels réels découverts et corrigés en stabilisant les
tests E2E des cartes Événement :

1. client/src/stores/game.js — le store Pinia n'exposait JAMAIS les champs
   liés aux bâtiments (myBuildings, pendingBuyBuildingId, availableBuildings)
   ni aux événements (myEventHand, eventDeckSize, hasPendingEventDraw,
   pendingEventPlayId) : updateState() ne les lisait pas depuis le
   'game-state' serveur. Conséquence concrète : GameScene._epidemicNeedsTarget()
   lisait this.store.myBuildings (toujours vide) pour décider si la
   variante Église d'Épidémie s'applique — le ciblage de la variante Trousse
   ne fonctionnait donc jamais correctement dès qu'un joueur possédait
   aussi le Château. Corrigé + 4 nouveaux tests unitaires Vitest.

2. client/src/game/scenes/GameScene.js — GameScene._onEventCardSelected
   envoyait encore les clés historiques gainTargetSocketId/
   loseTargetSocketId/targetSocketId au serveur, alors que Room.js avait été
   migré vers gainTargetTeamIndex/loseTargetTeamIndex/targetTeamIndex
   (socketId n'est jamais exposé au client). Conséquence : le ciblage
   d'Accord royal et de la variante Trousse d'Épidémie était intégralement
   cassé en jeu réel — les clés ne correspondant à rien côté serveur, aucune
   cible n'était jamais appliquée, silencieusement.

3. e2e/tests/helpers.js — drawEventUntil abandonnait immédiatement dès que
   la main d'événements atteignait 2 cartes sans contenir l'id recherché,
   rendant les tests E2E d'événements extrêmement flaky (avec seulement 6
   types d'événements et une main de 2, l'échec dès les 2 premières pioches
   est fréquent, pas rare). Corrigé : joue désormais une carte pour libérer
   un slot et continue à piocher jusqu'à maxDraws (20).

Stabilisation supplémentaire :
- Toutes les fonctions d'introspection Phaser dans helpers.js sécurisées
  (optional chaining) contre une scène pas encore initialisée après un
  changement de tour/navigation sous charge (plusieurs pages Playwright
  simultanées).
- Timeouts des tests events.spec.js augmentés (150-240s) pour laisser le
  temps aux boucles d'accumulation de jauge + pioche aléatoire de converger.
- playwright.config.js : retries=1 même en local, pour absorber la
  variance résiduelle des tests basés sur un tirage aléatoire, tout en
  gardant une détection fiable des vraies régressions (qui échoueraient de
  façon reproductible sur les deux tentatives).
- events.spec.js : test 'Accord royal' fournit désormais les 2 cibles
  requises par le ciblage séquentiel (gain + perte) au lieu d'une seule,
  ce qui laissait la séquence de ciblage bloquée sans jamais émettre
  'play-event'.

Résultat : 166 tests unitaires serveur + 28 tests unitaires client
toujours au vert ; suite events.spec.js (9 tests E2E) passe intégralement
(certains via retry probabiliste documenté, comportement attendu vu la
pioche aléatoire dans le deck partagé).
2026-08-24 13:24:37 +02:00

Regnum

Jeu de cartes en ligne multijoueur (2-4 joueurs). Collecte des points en jouant des cartes de royaume simultanément pendant X tours.

Stack

  • Frontend : Vue 3 + Phaser 3 + Pinia
  • Backend : Node.js + Express + Socket.io
  • Temps réel : WebSockets

Structure

regnum/
├── client/          # Vue 3 + Phaser 3
│   ├── src/
│   │   ├── views/   # HomeView, LobbyView, GameView
│   │   ├── game/    # Scènes Phaser (Boot, Game) + objets + items
│   │   ├── stores/  # Pinia store
│   │   ├── services/# Socket.io client
│   │   └── utils/   # Fonctions pures partagées (HUD, etc.)
│   └── test/unit/   # Tests unitaires Vitest (store, utils)
├── server/          # Node.js
│   ├── src/
│   │   ├── game/    # Room, RoomManager, cards, items
│   │   └── index.js # Serveur Express + Socket.io
│   └── test/game/   # Tests unitaires node:test (moteur de jeu)
└── e2e/             # Tests d'intégration Playwright (client+serveur réels)
    └── tests/

Lancement

Serveur

cd server
npm install
npm run dev

Client

cd client
npm install
npm run dev

Ouvrir http://localhost:5173

Tests

Le projet a 3 niveaux de tests, à exécuter indépendamment :

1. Tests unitaires — serveur (moteur de jeu)

Logique pure de Room.js / cards.js / items.js (règles, gains, achats, effets permanents), avec le test runner natif de Node.js :

cd server
npm test            # exécution unique
npm run test:watch  # mode watch

2. Tests unitaires — client (store, utilitaires)

Store Pinia et fonctions pures du HUD (hudUtils.js), avec Vitest :

cd client
npm run test:unit        # exécution unique
npm run test:unit:watch  # mode watch

3. Tests d'intégration E2E (client + serveur réels)

Parcours complets (créer/rejoindre une salle, jouer des tours, acheter un objet, fin de partie) pilotés dans un vrai navigateur Chromium via Playwright, avec de vrais clients Socket.io. webServer dans e2e/playwright.config.js démarre automatiquement le serveur et le client si besoin.

cd e2e
npm install                # première fois seulement
npx playwright install chromium  # première fois seulement, si besoin
npm test                   # exécution unique
npm run test:ui            # mode interactif (debug)

Règles

Principe général

  • 2 à 4 joueurs par partie, chacun avec son propre deck des mêmes 12 personnages (seule la couleur d'équipe change).
  • Chaque joueur commence avec une main de 4 cartes ; le reste du deck (8 cartes) est pioché une carte par tour.
  • La partie dure 12 tours. Elle se termine avant si tous les joueurs n'ont plus ni carte en main ni carte à piocher.
  • Chaque tour, tous les joueurs choisissent simultanément une carte (cachée), puis les cartes sont révélées et résolues en même temps.

Les 4 jauges

Chaque personnage possède 4 attributs (valeurs de 1 à 6), qui correspondent à 4 jauges cumulées par le joueur pendant toute la partie :

Jauge Icône
Nourriture 🟡
Guerre 🔴
Santé 🟢
Diplomatie 🔵

Résolution d'un tour

Pour chaque attribut, un joueur compare la valeur de sa carte jouée à celle de son voisin de gauche (ordre des joueurs dans la partie, en boucle) :

gain = max(0, ma_valeur - valeur_du_voisin_de_gauche)

Le gain (jamais négatif) est ajouté à la jauge correspondante du joueur. Le score total d'un joueur est la somme de ses 4 jauges.

Les 12 personnages

Reine, Prince, Boucher, Médecin, Apothicaire, Fou du Roi, Assassin, Paysan, Bourreau, Chevalier, Marchand, Évêque — chacun avec un profil d'attributs différent (ex : le Paysan est fort en Nourriture mais faible en Diplomatie, l'Assassin est très fort en Guerre mais faible partout ailleurs).

Objets achetables

Pendant la phase de sélection, un joueur peut dépenser des points d'une jauge pour acheter un objet, qui procure un effet permanent pour le reste de la partie tant qu'il le possède. Chaque objet coûte 3 points et n'existe qu'en un seul exemplaire pour toute la partie (premier arrivé, premier servi) :

Objet Jauge liée Effet
Trébuchet 🔴 Guerre Double le gain de Guerre à chaque tour
Charrue 🟡 Nourriture Double le gain de Nourriture à chaque tour
Trousse du Médecin 🟢 Santé Garantit +2 en Santé à chaque tour, quel que soit le résultat
Sceau Royal 🔵 Diplomatie Copie le meilleur gain de Diplomatie parmi tous les autres joueurs

En cas de conflit (plusieurs joueurs achètent le même objet le même tour), c'est le joueur le plus proche du "premier joueur" du tour (rôle qui tourne entre les joueurs à chaque tour) qui l'obtient ; les autres conservent leurs points.

Fin de partie

Le joueur avec le score total (somme des 4 jauges) le plus élevé après 12 tours remporte la partie.

S
Description
Jeu de cartes en ligne - 4 joueurs - collecte de points par royaume
Readme
117 MiB
0 Stars 1 Watchers 0 Forks
Languages
JavaScript 95.5%
Vue 4%
Dockerfile 0.4%
HTML 0.1%