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>
4.5 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What this is
A web implementation of Bridžik, a Slovak 4-player trick-taking/bidding card game played with a 32-card Slovak/German-suited deck. The codebase is in mid-migration from a legacy server-rendered HTTP frontend to a realtime Socket.IO frontend; both still live in api/. Code, comments, and exception messages are mostly in Slovak — keep new strings consistent with that.
Commands
# Run the app (starts on 0.0.0.0:5000)
python -m app # or: python app.py / python start.py — all just import `api`
# Run via Docker (app on :5000, debugpy on :5678)
docker-compose up --build
# Run the full test suite (pure-engine unittest)
python -m unittest tests -v
# Run a single test case / method
python -m unittest tests.StashCase
python -m unittest tests.StashCase.test_get_winner
There is no linter or build step configured.
Architecture
Game engine — bridzik.py (the important part)
Pure Python, no Flask dependency. All game rules live here and are exercised directly by tests.py. The state is a strict nested hierarchy, each level enforcing turn order and completion before delegating down:
Bridzik— a whole game = exactly 4Series.Series— exactly 8Rounds; the starting player rotates per series/round.Round— a bidding (guess/"tip") phase then a play phase of8 - round_numberStashes. Each round deals fewer cards asround_numbergrows.Stash(a "kopka" = one trick) — 4 cards, one per player;get_winner()resolves it.
Key rules encoded in the engine:
Card_colors.HEARTS(červeň) is the permanent trump — any heart beats any non-heart inStash.get_winner(), and follow-suit logic inRound.play_cardforces playing a heart when you can't follow the led suit.Card_valuesare ordered C7 < C8 < C9 < C10 < LOWER < UPPER < KING < ACE via custom comparison dunders.- Scoring: a player who exactly matches their guess scores
10 + guess, else 0 (Round.get_points_summary). - The total of the 4 guesses may not equal the number of tricks (the last bidder is constrained) — see
Round.add_player_guess.
Serialization: Card.JSONEncoder flattens a Card to {color, value} name strings. The double json.loads(json.dumps(...)) pattern used throughout the API exists to strip escaped slashes after custom encoding.
Web layer — api/
Two frontends coexist; api/__init__.py is the active one:
-
api/__init__.py— Socket.IO realtime server (current). Definesapp,socketio, and all@socketio.on(...)handlers (create_game,register_player,start_game,play_card, etc.). Supports multiple concurrent games via the module-globalgamesdict keyed by game id (gid), withGame/Playerwrapper classes and Socket.IO rooms. The server is started as an import side-effect —socketio.run(...)runs at the bottom of this module, which is whyapp.pyandstart.pyare one-linefrom api import app.- Known WIP/fixme spots:
create_gamehardcodesgid = 'a';play_cardreferencesget_player_cardswithout calling it correctly. Expect rough edges here.
- Known WIP/fixme spots:
-
api/routes.py+api/templates/+api/forms.py— legacy server-rendered HTTP frontend (dormant/broken). It importsbridzikInstancefromapi, but that single shared instance is no longer defined inapi/__init__.py(commented out), so these routes will not run as-is. Treat this as reference for the old single-game flow, not as live code, unless you are deliberately reviving it. -
api/utils.py—sort_card_list(orders a hand by suit then value) andget_points_sums(totals standings across all series/rounds). Used by the legacy routes.
Config & infra
config.pyholds aConfigclass with a devSECRET_KEY; note the activeapi/__init__.pysets its ownSECRET_KEYinline rather than loading this.Dockerfiletargets Python 3.9, runspython -m app, and bundlesdebugpyfor remote debugging on port 5678.requirements.txtpinsFlask-SocketIO+eventlet(the chosenasync_mode); the trailingsocket-cliis marked for removal.
Conventions
- New game-rule logic belongs in
bridzik.pyand should come with aunittestcase intests.py— keep the engine independent of Flask/Socket.IO. - Raise
BridzikException(Slovak message) for rule violations; the API layer catches it and re-emits a generic Slovak error to the client.