# Wizard online An online version of the card game Wizard (rules: `wizard_instructions/`). **Detailed documentation of the whole codebase: [`docs/`](docs/README.md).** | Component | Path | Tech | |-----------|------|------| | Frontend | `wizard_website/` | React + TypeScript + Vite, CSS modules, served by nginx | | Game server | `wizard_server/` | Python, FastAPI, websockets | | Game logic | `wizard_server/game_logic/` | Pure Python package `wizard_game` (no dependencies) | | AI player | `wizard_ai/` | Python; RL training skeleton + websocket service | ``` browser ──wss /wizard/api/ws──────▶ Caddy ──/ws──▶ wizard_server:8083 ◀──ws /internal/ai── wizard_ai ──https /wizard/website/──▶ Caddy ──/────▶ wizard_website:8082 (network wizard_internal) ``` ## How it works 1. A player opens the website, enters a name and creates a lobby (and joins it). 2. The lobby shows a code (and an invite link, `?lobby=CODE`) others use to join. 3. The host can add AI players, toggle the *hidden bids* option, and start the game (3–6 players). The sum of all bids may never equal the number of cards in hand (always on). 4. The server runs the game with `wizard_game` and sends every player a personal view (`Observation`) after each move. Reloading the page reconnects to the same seat. A player who leaves a running game is replaced by an AI player. 5. When an AI seat has to act, the server sends its `Observation` to the AI service over the internal websocket and applies the answered action. If the AI service is unavailable or too slow, a random legal move is played so games never get stuck. The `Observation` / action JSON format is defined once in `wizard_game` and shared by the frontend, the server and the AI, so AI models see exactly the same data in training and in production. ## Deployment ```bash cp .env.example .env # set AI_SHARED_SECRET docker network create wizard_website # if they don't exist yet docker network create wizard_api docker compose up -d --build ``` - `wizard_website` joins the external network `wizard_website` (port 8082). - `wizard_server` joins the external network `wizard_api` (port 8083) and `wizard_internal`. - `wizard_ai` joins only `wizard_internal`, created by compose as an internal network. In the Caddy reverse proxy, `/wizard/website/*` goes to `wizard_website:8082` and `/wizard/api/*` to `wizard_server:8083`, both with the prefix stripped. The website uses relative asset paths and derives the API address from its own URL (`../api/ws`), so it works under any prefix as long as the website and API are siblings. `/internal/ai` refuses requests that come through the proxy (forwarding headers present), in addition to requiring `AI_SHARED_SECRET`. Trained AI checkpoints go into `wizard_ai/models/latest/` (mounted read-only). Until one exists the AI service uses the built-in heuristic player. ## Development ```bash python -m venv .venv && . .venv/bin/activate pip install -r wizard_server/requirements-dev.txt -r wizard_ai/requirements-dev.txt (cd wizard_server && uvicorn app.main:app --reload) # :8000 (cd wizard_ai && python -m wizard_ai serve) # connects to :8000 (cd wizard_website && npm install && npm run dev) # :5173 (cd wizard_server && pytest) # game logic + server tests (cd wizard_ai && pytest) ```