# 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 ```powershell # 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 **4 `Series`**. - **`Series`** — exactly **8 `Round`s**; the starting player rotates per series/round. - **`Round`** — a bidding (`guess`/"tip") phase then a play phase of `8 - round_number` **`Stash`es**. Each round deals fewer cards as `round_number` grows. - **`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 in `Stash.get_winner()`, and follow-suit logic in `Round.play_card` forces playing a heart when you can't follow the led suit. - **`Card_values` are 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).** Defines `app`, `socketio`, and all `@socketio.on(...)` handlers (`create_game`, `register_player`, `start_game`, `play_card`, etc.). Supports **multiple concurrent games** via the module-global `games` dict keyed by game id (`gid`), with `Game`/`Player` wrapper 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 why `app.py` and `start.py` are one-line `from api import app`. - Known WIP/fixme spots: `create_game` hardcodes `gid = 'a'`; `play_card` references `get_player_cards` without calling it correctly. Expect rough edges here. - **`api/routes.py` + `api/templates/` + `api/forms.py` — legacy server-rendered HTTP frontend (dormant/broken).** It imports `bridzikInstance` from `api`, but that single shared instance is **no longer defined** in `api/__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) and `get_points_sums` (totals standings across all series/rounds). Used by the legacy routes. ### Config & infra - `config.py` holds a `Config` class with a dev `SECRET_KEY`; note the active `api/__init__.py` sets its own `SECRET_KEY` inline rather than loading this. - `Dockerfile` targets Python 3.9, runs `python -m app`, and bundles `debugpy` for remote debugging on port 5678. - `requirements.txt` pins `Flask-SocketIO` + `eventlet` (the chosen `async_mode`); the trailing `socket-cli` is marked for removal. ## Conventions - New game-rule logic belongs in `bridzik.py` and should come with a `unittest` case in `tests.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.