Commit Graph

46 Commits

Author SHA1 Message Date
tim 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
tim 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
tim 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
tim 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
tim 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
tim 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
tim 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
Frantisek F cd3d84319e Add main socket communication 2022-07-05 21:28:05 +02:00
Frantisek F 3053040da9 Add docker and sockets 2022-07-05 15:58:23 +02:00
Jakub Senderák 2281e030f6 ui enhancements 2021-04-18 17:40:38 +02:00
Jakub Senderák 889f787f29 shuffler added as explicit dependency 2021-04-18 17:40:38 +02:00
Jakub Senderák 618e632ce4 sort_card_list moved to utils and refactored 2021-04-18 17:40:38 +02:00
Jakub Senderák 834e03c172 main module name change 2021-04-18 17:40:38 +02:00
Jakub Senderák 40b18cbf41 previous stash added to display 2021-04-18 17:40:38 +02:00
Jakub Senderák 71c9e33b85 Bridzik.get_previous_stash added 2021-04-18 17:40:38 +02:00
Jakub Senderák 6ebff98db7 guesses display order fix 2021-04-18 17:40:38 +02:00
Jakub Senderák f4fd27a5c1 added points summary 2021-04-18 17:40:38 +02:00
Jakub Senderák aa7b3c1a02 requirements 2021-04-18 17:40:38 +02:00
Jakub Senderák d12f5c093c ui tweaks, admin form 2021-04-18 17:40:37 +02:00
Jakub Senderák d657dedf5f first working prototype 2021-04-18 17:40:37 +02:00
Jakub Senderák a43a7c4881 added config - form csrf tag 2021-04-18 17:40:37 +02:00
Jakub Senderák e851da47ce Flask api init 2021-04-18 17:40:37 +02:00
Jakub Senderák f7dd5d44ed added guess interval validation 2021-04-18 17:22:26 +02:00
Jakub Senderák 2fb53d1c3d removed Card class dependency 2020-04-22 14:12:50 +02:00
Jakub Senderák 357d36b89a Bridzik.get_standings fix 2020-04-21 13:41:45 +02:00
Jakub Senderák df52e6ce4b gitignore add for images 2020-04-21 13:39:50 +02:00
Jakub Senderák 3187c42678 Bridzik.get_status fix 2020-04-20 15:23:04 +02:00
Jakub Senderák 93f98aa3cf gitignore update 2020-03-30 22:51:38 +02:00
Jakub Senderák 122c4ba675 reorganized module 2020-03-30 22:27:43 +02:00
Jakub Senderák 4067582077 Initial comments removed 2020-03-30 21:21:54 +02:00
Jakub Senderák a5b4629287 Series.play_card fix 2020-03-30 21:07:49 +02:00
Jakub Senderák 4bba93a03b Card.JSONEncoder added 2020-03-30 21:07:49 +02:00
Jakub Senderák 1f17eede1a top class init 2020-03-30 21:07:38 +02:00
Jakub Senderák c5207f3ad9 Series init 2020-03-28 21:20:37 +01:00
Jakub Senderák 3f3ba86ca3 Round.get_points_summary added 2020-03-28 09:15:58 +01:00
Jakub Senderák c1a7eb0e54 Round.get_stashes_summary renamed 2020-03-28 09:04:04 +01:00
Jakub Senderák 9da7a6a3f5 Card repr added 2020-03-28 07:26:53 +01:00
Jakub Senderák 07a494a40b Round.get_winner_summary added 2020-03-28 07:22:52 +01:00
Jakub Senderák ada5d74fd4 Stash.get_winner fix 2020-03-28 07:19:29 +01:00
Jakub Senderák 9885571222 refactor 2020-03-27 22:34:55 +01:00
Jakub Senderák 93e51762f5 additional tests for Round class 2020-03-27 22:28:04 +01:00
Jakub Senderák 404002c50e Round - input valdation fix 2020-03-27 22:27:24 +01:00
Jakub Senderák 5098a1aa9a stash.add_card arguments reordered 2020-03-27 15:17:58 +01:00
Jakub Senderák 38bf52c867 Initial commit 2020-03-26 16:37:01 +01:00