- Nouveaux personnages illustrés (12 PNG remplacés dans client/public/cards) - 4 objets achetables (Trébuchet, Charrue, Trousse du Médecin, Sceau Royal) avec effets permanents (double_gain, guaranteed_gain, copy_best_in_game), stock unique par partie, priorité d'achat tournante entre joueurs - Boutique en colonne à gauche du plateau : icônes compactes, carte détaillée agrandie au survol (taille découplée du displayScale du jeton), bouton ACHETER actif/grisé selon la jauge du joueur, retrait immédiat de la colonne une fois l'objet acheté (les autres remontent pour combler) - Objets achetés affichés "posés sur la table" : à côté de la main pour soi, sous le slot de jeu pour les adversaires - Icône du premier joueur (priorité d'achat) dans le HUD Vue - Refonte des cartes personnage (CardObject) : portrait plein cadre épuré, valeurs d'attribut discrètes aux 4 coins - Fix removePlayer : nettoyage de pendingBuys/pendingPlays et recalage de firstPlayerIndex à la déconnexion d'un joueur - 77 tests unitaires serveur (node:test) : cards, items, Room, RoomManager - README mis à jour avec les règles complètes du jeu - .gitignore : exclusion de .idea/ et .serena/ (artefacts d'outils locaux)
72 lines
2.6 KiB
JavaScript
72 lines
2.6 KiB
JavaScript
const { test, describe } = require('node:test')
|
|
const assert = require('node:assert/strict')
|
|
|
|
const { CHARACTERS, TEAM_COLORS, getDeckForTeam, dealHands } = require('../../src/game/cards')
|
|
|
|
describe('cards.js', () => {
|
|
test('expose exactement 12 personnages', () => {
|
|
assert.equal(CHARACTERS.length, 12)
|
|
})
|
|
|
|
test('chaque personnage a un id unique', () => {
|
|
const ids = CHARACTERS.map(c => c.id)
|
|
assert.equal(new Set(ids).size, ids.length)
|
|
})
|
|
|
|
test('chaque personnage a les 4 attributs définis entre 1 et 6', () => {
|
|
for (const c of CHARACTERS) {
|
|
for (const attr of ['food', 'war', 'health', 'diplo']) {
|
|
assert.ok(Number.isInteger(c[attr]), `${c.id}.${attr} doit être un entier`)
|
|
assert.ok(c[attr] >= 1 && c[attr] <= 6, `${c.id}.${attr} doit être entre 1 et 6`)
|
|
}
|
|
}
|
|
})
|
|
|
|
test('getDeckForTeam retourne les 12 personnages avec la couleur d\'équipe', () => {
|
|
const deck = getDeckForTeam(2)
|
|
assert.equal(deck.length, 12)
|
|
for (const card of deck) {
|
|
assert.equal(card.teamIndex, 2)
|
|
assert.deepEqual(card.teamColor, TEAM_COLORS[2])
|
|
}
|
|
})
|
|
|
|
test('getDeckForTeam ne mute pas les CHARACTERS d\'origine', () => {
|
|
const before = JSON.parse(JSON.stringify(CHARACTERS))
|
|
getDeckForTeam(0)
|
|
assert.deepEqual(CHARACTERS, before)
|
|
})
|
|
|
|
describe('dealHands', () => {
|
|
test('distribue une main de taille handSize et le reste au deck, pour chaque joueur', () => {
|
|
const result = dealHands(3, 4)
|
|
assert.equal(result.length, 3)
|
|
for (const { hand, deck } of result) {
|
|
assert.equal(hand.length, 4)
|
|
assert.equal(deck.length, 12 - 4)
|
|
// Pas de doublons entre hand et deck (même deck de 12 cartes)
|
|
const allIds = [...hand, ...deck].map(c => c.id)
|
|
assert.equal(new Set(allIds).size, 12)
|
|
}
|
|
})
|
|
|
|
test('chaque joueur reçoit un deck mélangé indépendamment (decks non systématiquement identiques)', () => {
|
|
// Avec un shuffle aléatoire, la probabilité que 4 joueurs aient exactement
|
|
// la même main de 4 cartes dans le même ordre est infinitésimale.
|
|
const result = dealHands(4, 4)
|
|
const handsAsStrings = result.map(r => r.hand.map(c => c.id).join(','))
|
|
assert.ok(new Set(handsAsStrings).size > 1, 'les mains devraient différer entre joueurs')
|
|
})
|
|
|
|
test('gère handSize par défaut (4) si non fourni', () => {
|
|
const result = dealHands(2)
|
|
assert.equal(result[0].hand.length, 4)
|
|
})
|
|
|
|
test('gère playerCount = 0 sans erreur', () => {
|
|
const result = dealHands(0, 4)
|
|
assert.deepEqual(result, [])
|
|
})
|
|
})
|
|
})
|