Commit Graph
42 Commits
Author SHA1 Message Date
timandClaude Opus 4.8 8e18325660 Frontend: hamburger menu v hlavicke na mobile
Hlavicka GameListu sa na uzkych displejoch nezmestila do jedneho
riadku. Pod sm: breakpointom sa polozky zbalia do dropdownu.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 01:16:40 +02:00
timandClaude Opus 4.8 973c279cbd Boti: pauza pred vedenim kopky, spravne poradie broadcastov po tahu
Bot doteraz vedel zahodit prvu kartu novej kopky uz po BOT_MOVE_DELAY_SECONDS
(0.8s), zatial co frontend zmetaciu animaciu predoslej kopky prehraval
1650ms -- karta tak "vyletela" uprostred zmetania. _run_bot_turns teraz pri
vedeni novej kopky caka TRICK_SWEEP_SECONDS (1.7s), zhodne s SETTLE_MS+
COLLECT_MS v GameTable.tsx.

_run_bot_turns tiez posielal player_cards PRED game_status (opacne ako
human play_card handler) -- pri prechode do noveho kola tak klient dostal
novu ruku skor, nez vedel, ze zacalo nove kolo, a fixne ju hned zobrazil.
Poradie broadcastov je teraz zhodne s human handlerom.

GameTable/Hand: ruka noveho kola sa zobrazi az po dobehnuti zmetacej
animacie poslednej kopky predchadzajuceho kola (displayedHand + freeze
efekt), a Hand.tsx rezervuje fixny priestor pre karty aj ked je ruka
docasne prazdna, aby layout neposkakoval.

tests/test_bots.py: nulovanie TRICK_SWEEP_SECONDS v setUpClass, aby testy
zostali rychle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 01:09:59 +02:00
timandClaude Fable 5 b1010ae008 Frontend: pridavanie botov v lobby
Hostitel prida na volne sedadlo bota (+ Bot = heuristika, + AI bot =
natrenovana siet) alebo bota odoberie; boti maju vlastnu ikonu a flag
is_bot v rosteroch. Surove ucty bot:<kind>-<n> sa vsade zobrazuju cez
displayName ako "Bot 2" / "AI bot 1" (stol, kopka, tabulky, historia).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 18:51:14 +02:00
timandClaude Fable 5 23de3ae3ac API: in-process boti ako hraci
Boti su sedadla bez socketu: ucty bot:<kind>-<n> v tabulke players
(nehijacknutelne, recyklovane medzi hrami), handlery add_bot/remove_bot
(len hostitel, pred startom) a tahova slucka _run_bot_turns s pauzou
BOT_MOVE_DELAY_SECONDS a MC/inferenciou v executori. Druhy: heuristic,
random, neural (pure-Python siet; bez suboru vah jasna chyba a pri
restore fallback na heuristiku). Botie sedadla preziju restart servera.

Oprava po ceste: emit kariet hracovi so sid=None (offline sedadlo po
restore) broadcastoval jeho karty vsetkym klientom -- preskakuje sa.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 18:51:14 +02:00
timandClaude Fable 5 9a750756c5 RL: pure-Python inferencia natrenovanej siete
py -m rl.export vyexportuje checkpoint do rl/weights/neural-bot.json
(bit-exact float32, 1.3 MB) a rl/pure_net.py ho hra bez torch/numpy
(stdlib forward pass, ~16 ms/tah). Testy parity: logity aj akcie sa
zhoduju s torch, identicke trajektorie celych kol. Natrenovany model:
6.7-6.9 b/kolo proti vsetkym baseline-om (heuristika prekonana).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 18:49:55 +02:00
timandClaude Fable 5 8f2449a408 RL: self-play PPO trening siete
BridzikNet (trup + guess/play/value hlavy s maskovanim), self-play
generator so zdielanou sietou na 4 sedadlach a opponent mixingom
(random/heuristicke sedadla pre robustnost), vlastny clipped-PPO
so skalovanim odmien a lr/entropy annealom. Torch je len trenovacia
zavislost na hoste (requirements-rl.txt); checkpointy a logy su
gitignorovane. Spustenie: py -m rl.train.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 18:49:55 +02:00
timandClaude Fable 5 e1733f4943 RL: baseline boti a evaluacny harness
RandomPlayer, HeuristicPlayer (MC tipper nad rozdaniami neznamych kariet
+ tipom riadena hracia heuristika) a McPlayer (MC ohodnotenie kazdeho
kandidatskeho tahu nad rozdaniami konzistentnymi s dedukovanymi voidmi).
Evaluacia s rotaciou sedadiel: py -m rl.evaluate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 18:49:55 +02:00
timandClaude Fable 5 3710a68e37 RL: encoding observacii, akcne masky a Round prostredie
Zaklad RL vrstvy (rl/DESIGN.md): egocentricky rotovana observacia
(ruka, videne karty, tipy, kopka, dedukovane voidy -- 233 dim), masky
legalnych tipov/kariet zrkadliace pravidla enginu a RoundEnv (jedno kolo
= jedna self-play epizoda). Fuzz-testy vynucuju zhodu masiek s enginom.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 18:49:55 +02:00
timandClaude Fable 5 1b14ace2cd Engine: Card a farby/hodnoty su hashovatelne
Vlastne __eq__ rusilo zdedeny __hash__, takze karty ani enumy sa nedali
pouzit ako kluce dictov a v setoch. Hash doplneny konzistentne s __eq__.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 18:49:55 +02:00
timandClaude Opus 4.8 f17f85ebd9 Trik: zmetanie po kazdom stichu, ovalne meno, dohrata hra mizne z lobby
Zmetacia animacia doteraz dobehla len na konci kola/serie (dlha pauza) --
mid-round ju hned prerusila dalsia zahrata karta, lebo `finishing` zavisela
na activeCards === 0. Teraz drzi kopku v strede az do konca animacie a
zablokuje hranie karty, kym sa nezmetie; boot-guard zabrani falosnej
animacii pri reconnecte na uz rozohratu hru.

PlayerCircle: ovalne oramovanie mena teraz rastie s dlzkou mena
(fit-content + strop + ellipsis) namiesto pevnej sirky.

api: hra dohrata do konca (4 serie) sa hned vytrati z lobby zoznamu,
namiesto toho aby visela navzdy ako "Začatá"/"Pokračovať" aj ked uz ma
v DB ended_at.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 18:49:40 +02:00
timandClaude Opus 4.8 558c8ec2d9 TASKS.md: drobne formatovanie zoznamu Hotovo
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:01:30 +02:00
timandClaude Opus 4.8 b1ca4bc78d TASKS.md: presun dokoncenej ulohy (scrollbar skore) do Hotovo
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:01:14 +02:00
timandClaude Opus 4.8 6c95c64398 Skore: slim scrollbar v style hry namiesto OS defaultu
Zoznam kol v Standings (herny sidebar aj mobilny panel) pouzival hrubu
systemovu lavicu scrollbaru, ktora nesedela do velvet/gold temy. Novy
.velvet-scroll (index.css) da tenky priehladny track a zlaty polopriehladny
thumb (webkit + Firefox scrollbar-color).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:00:56 +02:00
timandClaude Opus 4.8 ca0cf413ee TASKS.md: presun dokoncenej ulohy (trik animacia) do Hotovo
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:00:04 +02:00
timandClaude Opus 4.8 fb90737944 Trik: posledna karta sa prida bez blikania, kopka sa zmetie k vitazovi
Doteraz sa pri hodeni 4. karty cela kopka na frame stratila (displayedStash
padol na lingeredStash nastaveny az v useEffekte) a nasledne sa vsetky karty
znovu vlietli. Teraz sa previous_stash cita synchronne, takze posledna karta
len pribudne k trom uz leziacim, a potom sa cela kopka odsunie animaciou
smerom k sedadlu, ktore kopku vyhralo (stashWinner podla pravidiel enginu).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 10:59:42 +02:00
tim 1fbba5a7e1 TASKS.md: presun dvoch dokoncenych ulohy do Hotovo 2026-07-07 00:21:49 +02:00
tim 80929e7fed Skore: scrollovatelny zoznam s collapse serii, zrusenie preciarkavania
Standings.tsx rozdeleny na fixnu hlavicku, scrollovatelny zoznam kol
(overflow-y-auto, na mobile max-h-45vh) a fixne sucty, takze pri viacerych
odohratych kolach je vidno cely priebeh. Dokoncena seria (riadok Sigma) je
teraz klikatelna a zbaluje/rozbaluje svoje kola.

Zaroven zruseny line-through pri neuspesnom tipe v hernom pohlade aj v
detaile hry (History.tsx) -- neuspesny tip sa teraz len zobrazuje tlmenou farbou.
2026-07-07 00:21:34 +02:00
tim 9ade92d7fe Postgres kontajner: nastavenie timezone na Europe/Bratislava 2026-07-04 19:48:40 +02:00
timandClaude Fable 5 5b9d6342ad Auth: nedokoncena registracia sa da dokoncit novym QR kodom
Ucet bez potvrdeneho kodu (auth_token is NULL a totp_last_step == 0)
uz neblokuje meno: opakovany register_account vyda novy secret (stary
QR prestane platit) a login vyhodi RegistrationIncomplete, na ktoru
server odpovie novym QR -- klient sa prepne na registracny tab.
Admin statistiky vykazuju nedokoncene registracie osobitne, Hraci
celkom pocita len potvrdene ucty. Novy event register v analytike.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 17:34:03 +02:00
timandClaude Fable 5 53274607b1 Donate stranka: PAY by square QR s vyberom sumy
Linka Na kavu v hlavicke lobby vedie na /donate. QR sa generuje
dynamicky (balicek bysquare) pre presety 1/2/5 EUR alebo vlastnu sumu;
IBAN sa da skopirovat do schranky. Navstevy loguje existujuci
pageview beacon.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 16:54:49 +02:00
timandClaude Fable 5 aed0cd5d7f Analytika: /auth sa uz nesleduje -- 1:1 duplicita landing eventu
Na /auth sa da dostat len plnym loadom stranky (vsetky interne
redirecty nan su REPLACE a beacon ich skipuje) a kazdy plny load uz
posiela event landing. Kazdy riadok /auth by tak mal dvojicku z toho
isteho loadu. Skip na frontende aj v _SKIPPED_PATHS (pokryje aj stare
cache-ovane verzie frontendu). Stare /auth riadky v DB ostavaju.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 16:06:39 +02:00
timandClaude Fable 5 42f8dfdcf0 Analytika: landing event zachytava prvu navstevu aj s referrerom
Navsteva zacinajuca na / (preklik z FB/Messengeru, natukanie domeny)
doteraz nezanechala v page_views ziadny zaznam: / je v skip liste
beaconu a gate redirect na /auth je REPLACE navigacia, takze sa skipli
oba hopy a referrer sa stratil. trackLanding() v main.tsx posle raz za
kazdy plny load stranky pomenovany event landing s document.referrer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 15:39:11 +02:00
tim 652a5aa66e Merge branch 'fixy' 2026-07-04 14:05:19 +02:00
tim 7be89e70c8 pridanie pgAdmin 2026-07-04 13:45:41 +02:00
timandClaude Sonnet 5 28bb045274 Rozsirenie analytiky: login event, prepinac vsetci/prihlaseni, plny nazov krajiny
Pageview beacony uz neposielaju player_id z klienta (nedovereny vstup na
neautentifikovanom endpointe) -- prihlasenie sa eviduje server-side ako
event "login" (aj z registracie), s player_id skutocneho uctu. Admin
dashboard dostal prepinac scope vsetci/prihlaseni, ktory konzistentne
pocita grafy aj tabulky (kazdy login samostatne, navstevnicky den pre
anonymnu navstevnost). PageView.country teraz uklada cely anglicky nazov
krajiny namiesto ISO kodu (potrebna zmena schemy). Pridane sledovanie
klikov na "Pravidla hry" aj z GameList.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 12:43:37 +02:00
tim 7886b3a6b8 fix rusenie hier ked vsetkych odpoji 2026-07-03 19:16:41 +02:00
tim c4800fca0e Oprava hashu commitu v TASKS.md pre hotovu ulohu 2026-07-03 10:41:51 +02:00
tim 49ac0d3e78 Kopirovat celu URL linku na hru namiesto holeho gid v Lobby
TASKS.md: uloha presunuta z Nedokoncene do Hotovo.
2026-07-03 10:41:40 +02:00
tim 3615fa2bb1 frontendove optimalizacie 2026-07-03 01:11:26 +02:00
tim 7a5202195b docker-compose update external network 2026-07-01 21:04:04 +02:00
tim 4f0b706c28 docker-compose pre prod (non final) 2026-07-01 20:05:40 +02:00
timandClaude Sonnet 5 fbe0c3aa18 Add login lockout and encrypt Player.totp_secret/auth_token at rest
Per-username lockout (5 failed TOTP attempts / 5 min) stops account-targeted
brute force regardless of source IP. Player.totp_secret is now Fernet-
encrypted (ENCRYPTION_KEY env, db/crypto.py) instead of stored in plaintext,
and auth_token is stored as a SHA-256 hash rather than the raw session token.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-01 19:49:56 +02:00
timandClaude Sonnet 5 0845562a21 Add self-hosted usage analytics: pageview tracking + admin stats dashboard
Records pageviews (path, referrer, browser/OS/device, IP, GeoIP country) via
a POST /api/track beacon into a new PageView table, and exposes aggregated
daily/breakdown stats behind a token-gated GET /api/admin/stats endpoint with
brute-force lockout. Frontend gets a /admin dashboard (charts + breakdown
tables) built on recharts, with a switchable per-day pageviews chart.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-01 19:43:10 +02:00
timandClaude Sonnet 5 c59dca754f Tidy up root-level files: drop dead config.py, group tests into tests/
- Remove config.py, an unused Flask SECRET_KEY leftover from before the
  legacy HTTP backend was replaced by the Socket.IO/ASGI server.
- Move tests.py / tests_history.py / test_socket.py into a tests/
  package as test_engine.py / test_history.py / test_socket.py, and
  update CLAUDE.md's documented commands to match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-01 00:37:25 +02:00
timandClaude Sonnet 5 2c2f07c2ec Apply velvet-table redesign, fix game lifecycle and history bugs
Frontend:
- Dark green/gold "velvet table" visual redesign across the whole app
  (Auth, Lobby, GameList, GameTable, History, GameOver, modals), with
  Playfair Display/DM Sans typography and a centralized Tailwind palette.
- Desktop game table fit-scales to fill the window; mobile gets
  overlapping hand/trick layouts and larger touch-friendly cards.
- Standings sidebar now groups completed rounds by series with a
  per-series subtotal row, struck-through tips on missed bids.
- History page rewritten into a scoreboard-style detail view (player
  totals beside names, series grouped 2-up on desktop / stacked on
  mobile) and gained game names, completed/abandoned status, and a
  button to reopen a prematurely-ended game back into the lobby.

Backend:
- Fix started games being deleted from memory (and vanishing from
  everyone's lobby) when all players disconnect; only `end_game` tears
  down a started game now.
- Fix a crash writing a timezone-aware datetime into the naive
  `ended_at` Postgres column.
- Add `reopen_game`/`restore_game` to un-end a prematurely-ended game
  from history and resume it from the lobby.
- Let any seated player end an abandoned game once the host is
  offline, not just the host, so the game isn't stuck forever.
- Expose SERIES_PER_GAME/ROUNDS_PER_SERIES as named constants on the
  engine so the persistence layer derives game-completion rules from
  bridzik.py instead of re-encoding them.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-01 00:11:42 +02:00
timandClaude Opus 4.8 30c32b7714 Add persistence layer: TOTP auth, game history, restore
- db/ package: async SQLAlchemy engine + Player/Game/Guess models
- api/auth.py: passwordless TOTP login (pyotp), session token via socket auth
- api/history.py: record guesses/points, DB-backed standings, restore
  unfinished games on startup, host-only end_game
- api/__init__.py: auth-gated handlers, accounts map, rejoin via account
- frontend: Auth (QR + code) and History pages, resume/end-game in lobby/table
- docker-compose: real PostgreSQL service wired via DATABASE_URL
- tests_history.py for the persistence/auth layer; refresh CLAUDE.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 23:09:50 +02:00
tim beaf142ee4 Add React frontend and clean up legacy HTTP backend 2026-06-15 22:20:56 +02:00
timandClaude Opus 4.8 b8e2d15e27 Add leave_game and enrich game_status for frontend integration
- leave_game: explicit exit from the lobby/game. Before start it frees
  the seat; after start it keeps the seat (token reconnect still works)
  and marks the player offline, dropping the game once empty.
- Seat assignment now picks the lowest free order, so leaving a lobby
  before start no longer collides order numbers on the next join.
- game_status is now self-contained: adds players roster
  (order/name/connected), series_number, round_number and cards_in_round
  (= trick count / max bid), so the game view no longer has to stitch the
  lobby snapshot.
- Add Game.player_by_order helper.

Tests: +5 (roster/round meta, leave-before-start frees seat, leave drops
empty game, leave started game keeps seat offline, leave noop). Suite: 34.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 23:56:02 +02:00
timandClaude Opus 4.8 821c7e81ce Add CLAUDE.md and Slovak game-rules doc
CLAUDE.md documents the engine/web architecture and dev commands for
future Claude Code sessions. PRAVIDLA.md captures the full Bridzik rules
(extracted from the engine) in Slovak.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 23:40:42 +02:00
timandClaude Opus 4.8 d47eb03bce Migrate Socket.IO server to ASGI, fix bugs, harden, add socket tests
Replace Flask-SocketIO + eventlet with python-socketio AsyncServer on an
ASGI app served by uvicorn (Python 3.14). The server is no longer started
as an import side-effect; `python -m app` runs uvicorn for dev and the
Docker image runs `uvicorn api:app`.

Bug fixes:
- create_game now mints a real uuid gid and returns it to the creator
  (was hardcoded 'a').
- play_card resolves the player's hand and plays the selected Card (was
  indexing a method and crashing).

Hardening:
- Identity binding: every action derives the seat from the connection
  (sid -> {gid, order}); clients no longer pass a player number, closing
  the hidden-cards cheat where any client could request any hand.
- Secure token-based reconnect (per-player secret token).
- disconnect handler marks players offline and drops empty games (no
  more leaked games), notifying the room via player_connection.
- Guards for unknown gid, double start_game, and bad input; engine
  exception messages are forwarded instead of swallowed.
- Lobby payload is public-only (no sids/tokens); game_status carries a
  completed flag.
- /health endpoint via other_asgi_app; env-driven CORS and logging.

Infra:
- Dockerfile -> python:3.14-slim, uvicorn CMD, drop dead venv lines.
- requirements.txt -> python-socketio/engineio + uvicorn; drop eventlet,
  Flask-SocketIO, Flask-Session.
- docker-compose: drop unused debugpy port and obsolete version key.
- Remove redundant start.py; gitignore /.venv.

Tests: test_socket.py drives the handlers (identity binding, lobby
privacy, reconnect, disconnect cleanup, error handling, play flow).
Full suite: 29 passing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 23:40:32 +02:00
timandClaude Opus 4.8 aa1b037c1a Fix guess-validation tests for high round numbers
test_get_active_player and test_is_guessing_completed built a round 6
(only 8-6=2 tricks) but bid 4, which the engine correctly rejects
(guess must be <= number of tricks). Adjust the bids to legal values
while preserving each test's intent (player 0 stays the unique high
bidder so it leads the first stash).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 23:40:13 +02:00
tim 00937aa89f Add Bridzik.get_player_cards and make get_status player-agnostic
Engine support for the per-connection Socket.IO API: hands are fetched
explicitly via get_player_cards(player) and the shared status no longer
embeds a single player's cards.
2026-06-13 23:40:04 +02:00