Przejdź do treści

~/projekty / blind-clue

BlindClue

Wieloosobowa gra dedukcyjna w czasie rzeczywistym: wszyscy dostają tajne słowo poza jednym graczem, który ma tylko podpowiedź i musi się wtopić.

rola
full stack: backend realtime, frontend w Vue
czas
maj – sie 2026
typ
własny
stack
TypeScript · Vue 3 · Pinia · Fastify · Socket.io · SQLite
demo.mp4
[wideo demo: nagranie ekranu 1–3 min]

01 / problem

Gra imprezowa wymaga, by wszyscy byli w tym samym momencie

Gra działa tylko wtedy, gdy wszyscy gracze przechodzą przez te same fazy razem: opis słowa, głosowanie, ujawnienie. Każda faza musi dotrzeć do każdego gracza na bieżąco, a punktacja musi być rozstrzygana w jednym miejscu.

02 / rozwiązanie

Gra sterowana przez serwer na Socket.io

Od dwóch do czterech graczy dołącza do lobby i gra trzy rundy opisywania słowa bez jego wymieniania, po czym głosuje, kto jest impostorem. Głosy są ujawniane, a przyłapany impostor może jeszcze zdobyć punkty, zgadując słowo. Serwer jest właścicielem stanu gry i punktacji; klienci wysyłają zdarzenia, takie jak joinGame, submitDescription i voteImpostor, a reagują na GameStarted, VotingStarted, VotesRevealed i GameEnded.

  • [screenshot 1]

    [podpis]

  • [screenshot 2]

    [podpis]

03 / architektura

Jak to działa

Monorepo pnpm: backend Fastify + Socket.io + SQLite zbudowany w warstwach Clean Architecture (encje domeny, przypadki użycia, serwisy aplikacji, adaptery) oraz frontend Vue 3 + Vite + Pinia + Tailwind. Trwające gry żyją w pamięci w GameManagerze i znikają po zakończeniu; kategorie i słowa zarządzane są endpointami administracyjnymi. Backend jest dostarczany jako obraz Dockera.

04 / wyniki

Efekt

05 / wnioski

Czego się nauczyłem

  • Trzymanie trwających gier wyłącznie w pamięci upraszcza model; druga strona to fakt, że restart zakończyłby gry w toku.
  • Zwracanie Result z każdej operacji czyni ścieżki błędów jawnymi i łatwymi do testowania, także dla punktacji przy przyłapanym i nieprzyłapanym impostorze.

galeria

  • [zdjęcie]

    [podpis]

  • [zdjęcie]

    [podpis]

  • [zdjęcie]

    [podpis]