Bot Validation
How to activate a new bot through the validation process
Overview
New bots are created with pending status and must complete a validation game before they can join ranked play. This ensures that bots can connect, communicate via the MJAI protocol, and play a complete game without disconnecting or sending invalid actions.
Validation Flow
- Create a bot on the Bots page and copy the token (shown only once).
- Connect to the validation endpoint with Authorization header:
wss://game.riichi.dev/ws/validate Authorization: Bearer <your-token>
- The server spawns 3 internal tsumogiri bots and runs an East-only game.
- Your bot plays at seat 0 — respond to
request_actionevents normally. - If the game completes without your bot disconnecting and without your bot incurring a chombo penalty, the bot is automatically activated.
- After the game, the server sends a
validation_resultevent.
Pass / Fail Criteria
At the end of the validation game, your bot (seat 0) must satisfy both: (1) the WebSocket channel never dropped, and (2) the bot was not chombo'd by sending an invalid action. Timeouts alone do not cause failure — the server substitutes a default action (tsumogiri / pass) on timeout. However, invalid actions are not silently corrected — they trigger a chombo penalty, which fails validation.
| Scenario | Result |
|---|---|
| Game completes; bot stayed connected and never chombo'd | Pass |
| Bot times out on some actions (server applies defaults) | Pass |
| Bot sends an invalid action (chombo / mangan penalty) | Fail |
| Bot disconnects during the game | Fail |
Validation Result Events
After the validation game, the server sends a validation_result event:
On success:
{"type":"validation_result","passed":true} On failure (disconnected):
{"type":"validation_result","passed":false,"reason":"disconnected"} On failure (illegal action / chombo):
{"type":"validation_result","passed":false,"reason":"penalized (illegal action)"}Sample Bot (Python)
A minimal tsumogiri bot that always discards the drawn tile. This is sufficient to pass validation.
import asyncioimport jsonimport websocketsasync def bot(url: str): async with websockets.connect(url, additional_headers=HEADERS) as ws: my_seat = None last_tsumo = None while True: msg = json.loads(await ws.recv()) match msg["type"]: case "start_game": my_seat = msg.get("id", 0) case "tsumo": pai = msg.get("pai") if pai and pai != "?": last_tsumo = pai case "request_action": if last_tsumo: resp = { "type": "dahai", "actor": my_seat, "pai": last_tsumo, "tsumogiri": True, } else: resp = {"type": "none"} # Echo request_id for deterministic binding if "request_id" in msg: resp["request_id"] = msg["request_id"] await ws.send(json.dumps(resp)) last_tsumo = None case "end_game": breakTOKEN = "your-bot-token-here"HEADERS = {"Authorization": f"Bearer {TOKEN}"}asyncio.run(bot("wss://game.riichi.dev/ws/validate")) Save as validate_bot.py and run:
pip install websockets python validate_bot.py
Troubleshooting
Connection refused
Make sure the gameserver is reachable. Check that the URL uses wss:// (not https://).
"Bot is already validating"
Only one validation game per bot can run at a time. Wait for the current validation to finish, or restart the gameserver if a previous session is stuck.
"Token verification failed"
The token may be expired or invalid. Go to the Bots page and regenerate a new token. Ensure the bot status is pending — only pending bots can use the validation endpoint.
"Bot is inactive"
Bots that are already active or inactive cannot use the validation endpoint. Only pending bots need validation.
