# サーバー Bot(1.7.0 から) Bot の JS をサーバーに登録すると、対戦の毎ティックに HTTP で通信しなくても、サーバーの中で Bot が戦います。 通信の往復が無いので、修行がとても速くなります(手元の測定で、500 ティックの修行が1秒ほど)。 今は**修行だけ**で使えます。ランク戦・フリー対戦は、今までどおり HTTP の Bot(`bot.mjs`)で戦います(ランク戦でも使えるようにする予定です)。 ## 流れ 1. `server-bot.js`(見本)を元に Bot を書く 2. 登録する: `MOCHI_URL=... MOCHI_TOKEN=... node upload-bot.mjs server-bot.js --apply` - サーバーが検査します(読み込み → 見本の相手と短い対戦)。例外・計算の上限超え・受け付けられない提出が1つでもあると不合格です - 合格した版だけ適用できます(`--apply` で適用まで行う) 3. 修行する: `POST /api/match/training` に `{ "bot": "balanced", "driver": "server" }`(既定で高速) - マイもち(`/my`)の「Bot」タブからも始められます - **応答は対戦が始まった時点で返り、対戦はそのあとサーバーの中で進みます**。応答の直後に結果を見ると、まだ「対戦中」です。 数秒待ってから `GET /api/matches/<対戦ID>/result` を取りに行ってください。まだ終わっていなければ 409 `running` が返るので、 2〜3秒おきに取り直します(多くは数秒で終わります。混んでいるときは、もう少しかかります) 4. ログを見る: `GET /api/matches/<対戦ID>/botlog`(自分の Bot の `console.log` と失敗。オーナーだけ) ## Bot の形 1ファイルの JS です。次の関数を定義します(`init` は無くてもよい)。 ```js function init(info) { /* info: { you, ruleset, matchId }。対戦の最初に1回 */ } function select(view) { return [/* 手札の番号 */]; } // セレクトのとき function act(view) { return { move: "none", attack: "none" }; } // バトルの毎ティック ``` `view` は HTTP の `GET /api/matches/:id/state` の中身と同じ形です(`API.md` の「対戦状態」)。 `bot.mjs` から通信の部分を外せば、そのまま移せます。 ## 決まりと上限 | 項目 | 上限・決まり | 超えたとき | |---|---|---| | ソースの大きさ | 256KB | 登録できない | | 使えないもの | `import`・`require`・`fetch`・タイマー・ファイル(そもそも無い) | — | | メモリ | 1体 16MB | その対戦では止まる(以後「何もしない」) | | 1回の判断の計算 | 20 単位(1単位 ≒ 関数呼び出し・ループ約5000回。手元のパソコンで約3ミリ秒) | そのティックは「何もしない」 | | 1試合の計算の合計 | 2400 単位(1回の上限 × 120) | 残りのティックは「何もしない」 | | 1回の実時間 | 1秒(数え漏れの保険) | そのティックは「何もしない」 | | `console.log` | 1試合 8KB | それより後は残さない | | 版 | 5つまで。1日20回まで登録 | 古い版を `DELETE /api/bots/:version` で消す | | `Math.random()` | 対戦ごとに決まった種で動く(同じ対戦なら同じ並び) | — | | `Date.now()` | 対戦の開始時刻のまま進まない | — | - 判断が締切(5秒)までに返らなかったティックも「何もしない」になります - 続けて20回失敗すると、その対戦では止まります(対戦は最後まで進み、結果と報酬は普通に付きます) - 対戦の途中で止まった試合が**3試合続くと、その版は停止**され、適用も外れます(直して新しい版を登録してください) - 計算の量は実時間ではなく回数で数えるので、サーバーが混んでいても不公平になりません ## API | 操作 | メソッドとパス | 中身 | |---|---|---| | 登録 | `POST /api/bots` 🔑 `{ source, note? }` | `{ version, status: "checking", existing }`。同じ中身なら前の版を返す | | 版の一覧 | `GET /api/bots` 🔑 | `{ applied, versions: [{ version, status, size, createdAt, note, applied, strikes, report }], uploadsLeftToday }` | | 1つの版 | `GET /api/bots/:version` 🔑 | `status` は `checking`・`passed`・`failed`・`disabled`。`report.check` に検査の結果 | | 適用 | `PUT /api/bots/applied` 🔑 `{ version }` | `{ version: null }` で使わない。対戦中は 409 | | 消す | `DELETE /api/bots/:version` 🔑 | 適用中と、進行中の対戦が使っている版は消せない | | 修行 | `POST /api/match/training` 🔑 `{ bot, driver: "server", fast? }` | `{ matchId, botVersion }`。実行枠が空いていなければ 503(少し待ってやり直す) | | ログ | `GET /api/matches/:id/botlog` 🔑 | `{ logs: [{ seat, version, log }] }`。自分の席だけ | 🔑 = トークンが要る(`Authorization: Bearer <トークン>`)