feat(question): uniform, balanced, repeat-free question draw (#55) #64

Merged
Reliodas merged 2 commits from feat/55-question-draw into develop 2026-06-25 19:02:50 +00:00
Owner

Closes #55

Rend le tirage des questions uniforme, équilibré, sans répétition et testable.

Changements (question.service.ts)

  • Fisher-Yates uniforme à la place de Array.sort(() => Math.random() - 0.5).
  • Source de hasard injectable (Rng, Math.random par défaut) → tirages déterministes en test.
  • Équilibrage par difficulté : tourniquet sur easy/medium/hard (groupe unknown pour les questions sans niveau) en 1ʳᵉ et 2ᵉ manche ; repli uniforme si métadonnées absentes. La finale tire uniformément (ses cases portent leur difficulté).
  • Anti-répétition par partie : la Room porte drawnQuestionIds ; chaque tirage exclut les ids déjà tirés et les enregistre. Réinitialisé au démarrage de partie (round1.start). Une question vue ne réapparaît plus dans une autre manche.
  • Bornes : un tirage est borné par la taille du pool, sans doublon.

Tests (question.service.test.ts)

  • fisherYates : permutation, déterminisme (PRNG seedé), non-mutation.
  • drawFrom : bornes (0, > pool), déterminisme, non-répétition via exclude, équilibrage difficulté (1/1/1 puis 2/2/2 sur banque synthétique), repli uniforme.

Conformité aux critères

Fisher-Yates ✓ · hasard injectable ✓ · équilibrage difficulté configurable quand métadonnées ✓ · pas de répétition dans une partie ✓ · tests déterministes (distribution, non-répétition, bornes) ✓ · Implémentation.md décrit garanties et limites ✓

Vérification (checkout propre, sans assets)

  • typecheck OK ; node:test 220 pass (1 skipped), Jest 13/13, Vitest 83/83
  • coverage serveur 93.82 % stmts (seuils respectés), client exit 0
  • build OK, check:assets OK, validate:questions OK (142), smoke OK, e2e Playwright 3/3
Closes #55 Rend le tirage des questions uniforme, équilibré, sans répétition et testable. ## Changements (`question.service.ts`) - **Fisher-Yates** uniforme à la place de `Array.sort(() => Math.random() - 0.5)`. - **Source de hasard injectable** (`Rng`, `Math.random` par défaut) → tirages déterministes en test. - **Équilibrage par difficulté** : tourniquet sur `easy`/`medium`/`hard` (groupe `unknown` pour les questions sans niveau) en 1ʳᵉ et 2ᵉ manche ; repli uniforme si métadonnées absentes. La finale tire uniformément (ses cases portent leur difficulté). - **Anti-répétition par partie** : la `Room` porte `drawnQuestionIds` ; chaque tirage exclut les ids déjà tirés et les enregistre. Réinitialisé au démarrage de partie (`round1.start`). Une question vue ne réapparaît plus dans une autre manche. - **Bornes** : un tirage est borné par la taille du pool, sans doublon. ## Tests (`question.service.test.ts`) - `fisherYates` : permutation, déterminisme (PRNG seedé), non-mutation. - `drawFrom` : bornes (0, > pool), déterminisme, non-répétition via `exclude`, équilibrage difficulté (1/1/1 puis 2/2/2 sur banque synthétique), repli uniforme. ## Conformité aux critères Fisher-Yates ✓ · hasard injectable ✓ · équilibrage difficulté configurable quand métadonnées ✓ · pas de répétition dans une partie ✓ · tests déterministes (distribution, non-répétition, bornes) ✓ · `Implémentation.md` décrit garanties et limites ✓ ## Vérification (checkout propre, sans assets) - typecheck OK ; `node:test` 220 pass (1 skipped), Jest 13/13, Vitest 83/83 - coverage serveur 93.82 % stmts (seuils respectés), client exit 0 - build OK, check:assets OK, validate:questions OK (142), smoke OK, e2e Playwright 3/3
Replace the biased sort-based shuffle with Fisher-Yates and an injectable random
source. Draws spread across difficulty buckets when the metadata exists (first
and second rounds) and fall back to uniform otherwise. A per-room
drawnQuestionIds set excludes already-used questions so a game never repeats one
across its three rounds. Bounds are respected when the request exceeds the pool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs(impl): document the question draw guarantees (#55)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
8065c80d3a
Describe Fisher-Yates uniformity, the injectable random source, the
repeat-free per-game draw, the difficulty balancing and its limits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reliodas merged commit a0b4d3397f into develop 2026-06-25 19:02:50 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: jeux/le-grand-concours#64
No description provided.