Karetní hra · Sólo projekt

Kanasta
Online

Multiplayer karetní hra pro 2–4 hráče. Vlastní implementace všech pravidel kanasty — engine, server, klient. Vanilla JS, Socket.io, work in progress.

Kanasta Online je digitální verze klasické karetní hry (2 balíčky + 4 jokeři = 108 karet, 2v2 týmy, melds, canasty, scoring do 10 000 bodů). Postavila jsem ji sólo — backend, frontend, herní logiku, UI. Work in progress: jádro je hratelné, chybí bot AI a mobile responsivita.

Stack je záměrně minimalistický — Node.js + Express + Socket.io backend, vanilla JS/HTML/CSS frontend, žádný framework. Dokázat, že komplexní hru jde napsat bez build kroku a bez dependency overheadu.

Role Sólo (engine + server + UI)
Typ Karetní hra · WIP
Období Březen 2026
Technologie
Node.js Express 4.18 Socket.io 4.7 Vanilla JavaScript Vanilla CSS (Grid) HTML In-memory state

Kanasta je klasická týmová karetní hra, kterou hraje moje rodina odjakživa. Online verze se nedá najít v rozumné kvalitě — buď jsou hry placené, nebo nemají český původní rules, nebo neumí 2v2 týmovou verzi. Cílem bylo postavit verzi, kterou se dá hrát „jen tak" kdykoliv s rodinou na dálku.

Důležitá designová rozhodnutí: žádná databáze (in-memory state stačí pro 4 hráče), žádný framework (vanilla JS = nulové build environment), žádný login (kód místnosti místo účtů). Cíl je minimální friction při zahájení hry.

9 souborů, ~3 884 řádků kódu, rozděleno na:

  • Backend (1 365 LoC):
    • server.js (497 LoC) — Express + Socket.io server, room management, 11+ socket eventů (createRoom, joinRoom, drawFromStock, takeDiscardPile, meld, multiMeld, discard, goOut, …).
    • game/Game.js (969 LoC) — hlavní herní engine, všechna pravidla.
    • game/RoomManager.js (112 LoC) — místnosti, generování 6-znakového kódu.
    • game/Player.js (98 LoC) — stav hráče, ruka, červené trojky.
    • game/Deck.js (71 LoC) — 2×52 karet + 4 jokeři, shuffle.
    • game/constants.js (115 LoC) — hodnoty karet, bonusy, požadavky první sehry.
  • Frontend (2 505 LoC):
    • public/game.js (1 210 LoC) — Socket.io klient, UI orchestrace, card selection, builder pendingových sloh.
    • public/style.css (1 099 LoC) — green felt theme, CSS Grid layout (3×4), card rendering s Unicode suit symboly.
    • public/index.html (196 LoC) — lobby, čekárna, herní obrazovka, modaly.

Server posílá každému hráči privatní state (getStateForPlayer(playerId)) — soupeři nevidí moji ruku, ale vidí počet karet a melds.

Kompletní pravidla kanasty napsaná from scratch:

  • Balíček — 2×52 karet + 4 jokeři. Dělení po 13 kartách, červené trojky automaticky na stůl s náhradou (Player.js:9–20).
  • Melds — minimum 3 karty, vždy víc přirozených než wild (limit 1:1). Trojky nelze vykládat (Game.js:707–738).
  • Wild cards — jokeři (50 b.) a dvojky (20 b.) — isWild() v constants.js:86–88.
  • Canasta — 7+ karet, clean (jen naturals) = 600 b., dirty (s wild) = 300 b.
  • Frozen pile — wild na vrchu zamrazuje balík; rozmrazení = 2 naturals z ruky + top card (Game.js:787–789, 359–367).
  • Starting card — první odhozená karta zůstává v balíku napořád (nelze ji vzít, Game.js:89–105).
  • Black threes — nesmí být na vrchu balíku (validace při odhozu).
  • Round number rule — vykládání povoleno až od kola 4 (early game stabilizace).
  • First meld requirement — bodový práh podle skóre týmu (15 / 50 / 90 / 120 b. dle úrovně).
  • Scoring — joker 50, A/2 20, J–K 10, 4–7 5. Going out bonus 300, red three 100/ks (800 za všechny 4), hand penalty se odečítá.
  • Going out — vyžaduje alespoň 1 canastu (Game.js:823–829).
  • Winning — 10 000 bodů.

Frontend bez frameworku. CSS Grid layout: opponent nahoře, opponenti vlevo a vpravo otočení o 90°, můj hand dole, melds týmů po stranách, stock + discard + scores ve středu.

  • Card rendering — HTML div s CSS styling, Unicode suit symboly (♥♦♣♠), jokeři 🃏. Rozdílné velikosti pro vlastní ruku (80×115) vs. opponent (70×100).
  • Pending melds builder — vyber karty, sestavuj sehru, submitni najednou nebo více sloh dohromady (game.js:745–825).
  • Team colors — modrá pro tým A, oranžová pro tým B.
  • Live score — body i meld count v reálném čase.
  • Test mode přes ?test query — vytvoří 3 dummy hráče, abych si mohla zkusit late-game scénáře bez 4 lidí na lince.

Hotové (hratelné teď):

  • Multiplayer lobby a místnosti s 6-znakovým kódem.
  • Plná 2v2 hra s 108 kartami.
  • Všechna pravidla kanasty (melds, wild, scoring, frozen pile, red threes, going out, first meld threshold).
  • Real-time synchronizace přes Socket.io.
  • Rejoin po výpadku spojení.

Rozpracované:

  • Bot AI — socket-side struktura je hotová (test mode vytvoří 3 boty), ale automata tahů ne. Boti zatím jen sedí.
  • Komplexní pile takedown scénářetakeDiscardPileWithMelds funguje pro běžné případy, edge cases nemám otestované.

Chybí:

  • Mobile responsivita (CSS je pro desktop, layout se rozbije pod tablet).
  • Chat (socket event existuje, UI ne).
  • Persistent storage (in-memory state se ztratí při restartu serveru).
  • Production logging (zatím console.log na všem).

Frozen pile mechanika a vícekrokové vykládání.

Kanasta povolí vzít celý discard pile, pokud z něj umíš vyložit sloha s pomocí top karty. Když je pile zamrzlý (wild nahoře), pravidla žádají, aby hráč měl v ruce 2 naturals stejné hodnoty jako top card. takeDiscardPileWithMelds() umí přijmout balík a okamžitě z něj poskládat víc sloh najednou — to je nejkomplikovanější část enginu.

First meld threshold závisí na týmovém skóre.

Bodový práh pro první sehru roste s úspěšností týmu (15 b. pod nulou, 50 do 1 500, 90 do 3 000, 120 nad 3 000). Engine musí po každém scoringu přepočítat práh pro další kolo a validovat první sehru proti aktuální hodnotě.

Privátní state per hráč přes Socket.io.

Server emituje plný state, ale klient nemá vidět ruku soupeřů. getStateForPlayer(playerId) projde state před každým broadcastem a redactuje cizí ruky — místo karet posílá jen jejich počet.

Going-out validation a kola scoring.

Hráč může jít „ven" jen pokud jeho tým má alespoň jednu canastu. Engine musí ověřit canasty na obou stranách, spočítat body za sloha mínus penalty za karty v ruce + bonusy za red threes a canasty, a rozhodnout o konci hry při překročení 10 000.

~3 880 řádků kódu (9 souborů)
969 řádků game enginu
1 210 řádků frontend JS
11+ socket eventů
108 karet (2×52 + 4 jokeři)
2–4 hráči, 2v2 týmy
0 frontend frameworků
0 databází (in-memory)