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.

pending → validation game → active

Validation Flow

  1. Create a bot on the Bots page and copy the token (shown only once).
  2. Connect to the validation endpoint with Authorization header:
    wss://game.riichi.dev/ws/validate
    Authorization: Bearer <your-token>
  3. The server spawns 3 internal tsumogiri bots and runs an East-only game.
  4. Your bot plays at seat 0 — respond to request_action events normally.
  5. If the game completes without your bot disconnecting and without your bot incurring a chombo penalty, the bot is automatically activated.
  6. After the game, the server sends a validation_result event.

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.