nerdware/packages/shared/src/quiz.ts
AyoubBenziza e3315d3b35 feat: mixed games by default, free-text quiz, mode-aware transitions
Mixed mode (default):
- gameType is now mixed | quiz | blindtest, default "mixed"; the lobby
  interleaves quiz + blindtest rounds so modes alternate
- lobby reworked: 3-way game-type switch, quiz count and/or blindtest config
  + track submission shown per selected type

Free-text quiz questions:
- quiz supports the `free` format (e.g. maths) alongside mcq/truefalse:
  payload without choices, {text} vote, tolerant matching on acceptedAnswers
- shared QuizQuestionPayload.choices optional; reveal truth carries answer
- match util moved to game/match.ts (shared by quiz + blindtest)
- repo/seed include free + acceptedAnswers; in-code bank gains free questions
- client quiz view renders a text input for free questions

Mode-aware transitions:
- RoundTransition themed per mode (colors/label/emoji); announces the mode
  on change (quiz <-> blindtest), lighter teaser within the same mode
- custom media buckets per mode: assets/transitions/{quiz,blindtest}/ + shared
- store tracks roundModeChanged

Verified e2e: mixed game alternates quiz/blindtest to completion; free-text
scoring covered by tests (22 pass).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 13:41:29 +02:00

33 lines
1.1 KiB
TypeScript

// Types concrets de l'épreuve Quiz, partagés client/serveur.
// Le payload générique du moteur est `unknown` ; le client le narrow via type === "quiz".
// V1 : formats mcq + truefalse (free / image_reveal arrivent avec la DB).
import type { QuizFormat } from "./domain"
/** Payload diffusé au lancement d'une manche quiz (round:start), SANS la réponse. */
export interface QuizQuestionPayload {
format: QuizFormat
prompt: string
/** Choix proposés (mcq/truefalse). Absent pour `free` (saisie libre). */
choices?: string[]
category?: string
/** 1 (facile) .. 3 (difficile). */
difficulty?: number
}
/** Vérité révélée à tous (round:reveal → truth). */
export interface QuizRevealTruth {
/** Index de la bonne réponse (mcq/truefalse). */
correctIndex?: number
/** Réponse canonique affichée (free). */
answer?: string
}
/** Résultat d'un joueur sur la manche. */
export interface QuizPlayerResult {
choiceIndex: number | null
correct: boolean
}
/** round:reveal → perPlayerResult : playerId → résultat. */
export type QuizPerPlayerResult = Record<string, QuizPlayerResult>