# bottournaments — Bot Protocol v1 Bots connect to the referee over WebSocket and exchange JSON messages. The referee is authoritative for everything: state, legality, clocks. Endpoint: `ws://:8100/ws` (wss:// + domain in production) ## Message flow 1. **Authenticate** (within 10s of connecting): ```json {"type": "auth", "api_key": "bt_..."} ``` Server replies `{"type": "auth_ok", "bot": ""}` or `{"type": "error", "error": "auth_failed"}`. 2. **Join the queue** (within 30s): ```json {"type": "join_queue", "game": "blindgambit"} ``` Server replies `{"type": "queued"}`. You'll be paired with the next bot in line. 3. **Match start** (server → you): ```json {"type": "game_start", "match_id": "...", "game": "blindgambit", "you": 0, "opponent": "rivalbot"} ``` 4. **Each round** (server → you): ```json {"type": "move_request", "obs": { ... }, "deadline_ms": 5000} ``` The `obs` object is everything you may know (see below). Reply within the deadline: ```json {"type": "move", "card": 7} ``` 5. **Round outcome** (server → you): ```json {"type": "round_result", "round_no": 1, "prize": 8, "pot": 8, "outcome": "WIN"} ``` `outcome` is `WIN`, `LOSS`, or `TIE` — from your perspective. **Bids are never revealed. Neither is the seed.** 6. **Match end** (server → you): ```json {"type": "game_end", "match_id": "...", "result": "WIN", "your_score": 58, "opp_score": 33, "reason": "completed"} ``` `result`: `WIN`, `LOSS`, `DRAW`, `WIN (opponent forfeit)`, `LOSS (forfeit)`. `reason`: `completed`, `timeout`, `illegal`, `disconnect`. ## The observation object ```json { "you": 0, "round_no": 4, "current_prize": 10, "pot": 13, "carry": 3, "your_hand": [2, 5, 6, 9, 10, 11, 12, 13], "your_score": 8, "opp_score": 7, "opp_cards_remaining": 8, "prizes_remaining": [1, 2, 4, 5, 6, 10, 11, 12], "history": [ {"round_no": 1, "prize": 8, "pot": 8, "outcome": "WIN"}, {"round_no": 2, "prize": 7, "pot": 7, "outcome": "LOSS"}, {"round_no": 3, "prize": 3, "pot": 3, "outcome": "TIE"} ] } ``` ## Forfeits (enforced, no appeals) - No `move` within `deadline_ms` → **forfeit** - Card not in `your_hand` → **forfeit** - Malformed message during a match → **forfeit** - Disconnect mid-match → **forfeit** ## Reference client `referee/client.py` is a complete working bot in ~40 lines. Wrap your own strategy: implement `choose(obs) -> card` and plug it in. ## Fair play One funded account per person. Bots that collude, soft-play, or dump across accounts forfeit standings and entry eligibility. Full move logs (with timings) are retained server-side for audit; bids remain sealed even after the match. ## Tournaments (v2) Join an open event instead of the casual queue: ```json {"type": "join_tournament", "tournament_id": 1} ``` Server confirms with `t_queued` (`joined`/`size`). When the lobby fills: `tournament_start` (round count + full field), then games arrive as normal `game_start`/`move_request` cycles — **stay connected between games**. Each Swiss round, every pairing plays the SAME shuffled prize deck twice (seats swapped): deal luck cancels across the field. Match points: 1 per game win, 0.5 per draw. After the last round: ```json {"type": "tournament_end", "standings": [["solidA", 5.0, 1254], ...]} ``` Elo (K=32, start 1200) updates per game and is public at `GET /leaderboard`. Final standings persist server-side.