Commit graph

12 commits

Author SHA1 Message Date
AyoubBenziza
42a36fc442 feat: blindtest needs 2 real players + bot difficulty setting
- blindtest is only available with >=3 players AND >=2 humans (a bot can't be DJ):
  lobby gate + tooltips updated, and server game:start rejects otherwise
- new botDifficulty setting (easy/normal/hard) drives how often bots answer
  correctly (quiz + blindtest who-added / title-artist); host picks it in the
  lobby when at least one bot is present

Verified e2e: blindtest start rejected with 1 human + 2 bots (NEED_THREE).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 18:03:03 +02:00
AyoubBenziza
8eac8d0123 feat: bots — virtual players to fill a game
- server-side bot players (isBot): host adds/removes them in the lobby
  (lobby:addBot / lobby:removeBot), capped at 8 players
- bots auto-vote each round: GameRound.botAnswer generates a plausible answer
  (quiz ~50% right, blindtest guesses a random player / title); engine schedules
  the votes within the answer window and triggers early end when all have voted
- bots are never DJ (pickDj excludes them) and are excluded from the blindtest
  track-submission gate (they don't submit)
- snapshot players carry `bot`; lobby shows a 🤖 marker + add/remove controls

Verified e2e: 2 bots added, they vote, game ends with all 3 players scored
and rounds end early.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 17:48:33 +02:00
AyoubBenziza
b91d09b7e7 feat: category filter in lobby settings
- settings carry `categories` (empty = all); quiz/image pool filters by category
  (DB + in-code bank), threaded through prepareQuizForRoom/loadGroup/loadQuizPool
- public GET /api/categories lists available categories
- lobby: category chips (Toutes + each), shown when quiz/images is active
- shared: RoomSettings.categories + UpdateSettingsPayload

Verified e2e: /api/categories returns the list; filtering by ["Pokémon"] serves
only Pokémon questions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 15:37:33 +02:00
AyoubBenziza
879bc5b388 feat: per-submode mixed settings, fastest award, wider desktop layout
Mixed settings fix:
- RoundConfig carries a `pool` hint (quiz | image) per quiz round; lobby shows
  a separate count for each selected sub-mode (Quiz, Images) and the blindtest
  config — not just the quiz one
- server pool composes the queue per round's pool (quiz formats vs image_reveal),
  in order; verified e2e (2 quiz + 2 images honored)

Awards:
- " Le plus rapide" — derived client-side from the speed bonus baked into
  quiz/image points (points above the 100 base on correct answers)

Desktop restyle:
- room page gets an ambient halo background and a responsive container
  (lobby max-w-3xl with a 2-column players|settings grid, game/end max-w-lg)
- game/end views widened to max-w-lg

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 15:10:09 +02:00
AyoubBenziza
f7ee2204d1 feat: round recap shows each player's answer
- shared: RoundRecap gains answers[] (RoundPlayerAnswer: text + correct)
- server: quiz/blindtest recap() build per-player answers (choice text / free
  text / blindtest guess formatted by mode), with correctness
- client: recap lists each player's answer per round, green/red by correctness,
  with their points when they scored

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 01:00:50 +02:00
AyoubBenziza
03a0dfa998 feat: end-of-game round recap (all rounds with answers + scorers)
- shared: RoundRecap (+ scorers); game:end carries recap[]
- server: GameRound.recap(ctx) per mode (quiz: prompt + correct answer +
  image; blindtest: "title — artist" + addedBy + links); engine accumulates a
  history per round (with that round's scorers from the score deltas) and sends
  it at game:end
- client: GameEndView shows a collapsible "Récapitulatif des manches" — each
  round's label, answer, media thumbnail (image/youtube) and who scored
- engine test dummy round implements recap

Verified e2e: game:end recap lists every round with answer + scorers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 00:54:40 +02:00
AyoubBenziza
8bc184fab7 feat: bigger image_reveal, optional image prompt, mixed sub-mode toggles, Pokémon seed
- image_reveal display: larger, object-contain (whole image up to 58vh), no crop
- image prompt optional in back-office (defaults to "Qui est-ce ?"), overridable
- mixed mode reworked: select "Mixte" then toggle sub-modes (Quiz/Images/
  Blindtest). Blindtest sub-mode locked under 3 players; mixed itself no longer
  needs 3. Server quiz pool formats derived from gameType + mixedModes.
  shared: MixMode + RoomSettings.mixedModes (default quiz+image).
- DB: prompt uniqueness is now a partial index (excludes image_reveal) + unique
  index on image_url; lets many images share the generic prompt, dedup by URL.
- seed: db:seed:images pulls Pokémon (PokéAPI) as image_reveal questions with
  official artwork (remote URL) + FR/EN accepted answers; idempotent by image_url.

Verified: migration applied, Pokémon seed inserts FR+EN, image mode serves only
image_reveal, all checks green (23 tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 00:20:28 +02:00
AyoubBenziza
62b536cc2b feat: "Images" as a distinct mode, refined lobby settings, upload button, app title
- image_reveal becomes its own game mode: GameType gains "image"; the quiz
  pool is filtered by allowed formats derived from gameType (image →
  image_reveal only, quiz → mcq/truefalse/free, mixed → all). loadQuizPool
  takes a formats list; pool fallback respects it.
- lobby settings reworked into a refined "Réglages" card: 4 mode tiles
  (icon + label, locked <3 players for blindtest/mixed), question count as a
  stepper, blindtest config grouped; dynamic label for image mode + hint.
- admin: real upload button (hidden input + styled Button with Upload icon)
  instead of the bare file input.
- app title set to "NerdWare …" in index.html (+ lang fr, description).

Verified e2e: image mode serves only image_reveal questions (with imageUrl).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 23:31:30 +02:00
AyoubBenziza
0619c02cdf feat(quiz): image_reveal format (progressive blur) end-to-end
Server:
- quiz-round handles image_reveal like a text-answer format (tolerant match
  on acceptedAnswers), exposing imageUrl in the round payload; degressive
  scoring comes for free from the existing speed bonus (earlier = blurrier =
  more points)
- repo includes image_reveal in the pool (requires imageUrl + acceptedAnswers)
- back-office: image upload (@fastify/multipart) saved to UPLOADS_DIR and
  served statically on /uploads (@fastify/static); create supports image_reveal
- env: UPLOADS_DIR; gitignore uploads/

Client:
- admin form: "Image à deviner" — upload + preview + accepted answers
- in-game: ImageReveal shows the image with a time-cadenced decreasing blur
  (synced via server timestamps) + text input; reveal shows the clear image
  and the answer

shared: QuizQuestionPayload.imageUrl

Verified against real server+DB: upload (url), static serve (200), create
(201), validation (400).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 21:41:34 +02:00
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
AyoubBenziza
9fe30feec5 feat(server): Drizzle + PostgreSQL schema + Open Trivia DB seed
- Drizzle (postgres-js) setup: schema (quiz_category, quiz_question,
  quiz_played, game_history), client (nullable — no DATABASE_URL → null),
  drizzle.config, initial migration, migrate script
- Open Trivia DB seed: curated geek categories, mcq + truefalse, base64
  decode (incl. type/difficulty), 1 req/5s rate limit, idempotent
  (prompt unique); also seeds the FR in-code bank as source 'manual'
- quiz wired to the DB: per-room pool preloaded at game start (loadQuizPool
  excludes already-played), records quiz_played for cross-session
  anti-repeat; falls back to the in-code bank when no DB
- docker-compose for local Postgres; db:* scripts; DATABASE_URL in env

Roadmap V1 step 5. Verified against a real Postgres: migrate + seed (270
OpenTDB + 10 manual), DB-backed game serves OpenTDB questions and fills
quiz_played; no-DB fallback still serves the in-code bank.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 12:24:17 +02:00
AyoubBenziza
5cc9113b8c feat(quiz): quiz game mode end-to-end (server mode + playable client UI)
Server:
- quiz mode (GameRound): in-memory FR question bank (mcq + truefalse),
  per-room anti-repeat, first-vote lock, score = base + speed bonus ∝ time
  left; registered via registerRound, loaded at startup
- shared: typed quiz payloads (QuizQuestionPayload / reveal truth / result)
- tests: quiz scoring, vote lock, bounds, reveal (bun test)

Client:
- room store handles round:start / voteAck / reveal / game:end
- RoomPage dispatches by status: lobby (host start controls) → quiz view
  (question, countdown, vote, reveal + scoreboard) → game-end view
- replaces standalone lobby page

Roadmap V1 step 4. Verified end-to-end over the wire (start→vote→reveal→
score→next round→game:end, no answer leaked, anti-repeat works).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 15:43:17 +02:00