# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## What this is A mobile-first, multiplayer drinking-game hub (酒桌派对 / "Drinking Game Hub"). Players open a web page on their phone, enter a nickname, and create or join a 4-character room. The `GAMES` registry declares four games (大话骰 dice, 德州扑克 poker, 六张牌 six, 抓二游 catch), but **only 大话骰 (Liar's Dice, `game="dice"`) has real backend logic** — the other three exist only as lobby cards and static prototype boards. ## Running and testing No build step, no dependencies — pure Python standard library + vanilla JS/CSS. ```bash python3 server.py # starts ThreadingHTTPServer on 0.0.0.0:8765 ``` Then open `http://127.0.0.1:8765/live.html` (the real MVP). The server seeds one demo room on startup. Tests are standalone scripts, run individually: ```bash python3 test_flow.py # HTTP test — requires server.py already running on :8765 python3 test_multi_play.py # in-process test — imports server.py directly ``` Two test styles, distinguishable by their second line: - **HTTP tests** (`base='http://127.0.0.1:8765'`) drive the live server over the API; start `server.py` first. - **In-process tests** (`importlib.util.spec_from_file_location(...)`) import `server.py` and call functions directly with no HTTP. **Gotcha:** these hardcode the path `/var/minis/workspace/drinking-games-ui/server.py`. That path predates this checkout (`/home/xiaoyu/projects/drinking-games-ui`); update the literal before running in-process tests here. Tests assert by printing — there is no test runner or assertion framework. Read the printed output to judge pass/fail. ## Architecture **`server.py`** — the entire backend in one file. It is both the API server and the static file server (`SimpleHTTPRequestHandler` rooted at the repo dir). All state lives in the module-level `rooms` dict; **nothing is persisted** — restarting the server wipes every room. A room is a plain dict (see `create_room`) holding players, `status` (`waiting`/`playing`/`result`), `currentCall`, `pendingAction`, `lastResult`, `history`, and `roundCalls`. The API is a flat list of POST endpoints in `Handler.do_POST`, plus GET `/api/rooms` and `/api/room`. Every mutation function returns the room, and the handler wraps it with `public_room(room, viewer_id)` before sending. `public_room` is the **only** serialization boundary — it controls per-viewer visibility via `visible_dice` (you see your own dice while playing; everyone's dice only on reveal). When adding fields the client needs, add them in `public_room`, not just on the room dict. Errors are signaled by raising `ValueError` with a user-facing Chinese message; `do_POST` catches all exceptions and returns `{"ok": False, "error": str(e)}` with HTTP 400. Follow this pattern — validate and raise `ValueError`, don't return error dicts from game functions. **Frontends — two separate, unrelated UIs:** - `live.html` is the **real** app: a single self-contained file (inline `