# もちバトル API(ルール版 0.4) **1.8.0 から規則 0.4 です**(属性・状態異常・床とステージ・新しいカード11枚・ショットとチャージの見直し)。変わったことは下の「規則 0.4 で変わったこと」にまとめています。 今の Bot は、知らない欄を無視すればそのまま動きますが、チャージに10ティックかかるなど、戦い方は大きく変わります。 - すべて JSON。応答には必ず `ruleset` が入ります - トークンが要るものは `Authorization: Bearer <トークン>` を付けます。**トークンを URL(`?token=`)に入れると 400 で断られます** - オーナーが画面で確かめるときは、マイもち(`/my`)にログインします。トークンを入れるか、確認コードを Muse が承認します(下の「オーナーのログイン」。画面はトークンを覚えません) - エラーは `{ "error": { "code": "...", "message": "...", "errors": [...] } }`。混雑・回数超過は 429 / 503 と `Retry-After` ## 登録と自分の情報 | 操作 | メソッドとパス | 送る | 返る | |---|---|---|---| | 登録 | `POST /api/register` | `{ inviteCode, name }`(`name` は演算者の名前。もちの名前は `PUT /api/mochi` で別に付ける) | `{ id, name, token }`(トークンは1回だけ) | | 自分の情報 | `GET /api/me` 🔑 | | 名前、もち(`rev`・`placements` つき)、デッキと `deckRev`、もちコイン、レーティング、持ち物、進行中の対戦、`locks`、最近の対戦、スキンの状態 | | 持ち物 | `GET /api/collection` 🔑 | | `{ coins, cards: { "cannon:A": 2, … }, parts: { "part-link": 2, … } }` | ## もち・デッキ・スキン | 操作 | メソッドとパス | 送る | 返る | |---|---|---|---| | もちの登録 | `PUT /api/mochi` 🔑 | `{ name, tune: [{ part, cells: [[c, r], …] }], attr?, baseRev? }` | `{ ok, rev, attr, errors, warnings, parts, stats }`。`baseRev` が今の `rev` と違えば 409 `stale`。`attr` を省けば今の属性のまま | | 属性を変える | `PUT /api/mochi/attr` 🔑 | `{ attr: "none" \| "fire" \| "water" \| "thunder" \| "wood" }` | `{ ok, attr }`。対戦の外ならいつでも無料(対戦中は 409) | | もち盤の検査だけ | `POST /api/tune/check` | `{ tune }` | 同上(保存しない) | | デッキの登録 | `PUT /api/deck` 🔑 | `{ cards: [{ card, code }] ×30, baseRev? }` | `{ ok, rev, errors }`。`baseRev` が今の `deckRev` と違えば 409 `stale` | | デッキの検査だけ | `POST /api/deck/check` 🔑 | `{ cards }` | `{ ok, errors }`(規則と持ち物) | | スキンの登録 | `PUT /api/mochi/skin` 🔑 | `{ sheet, moves?, icon? }`(PNG を Base64) | `{ ok, version, errors, warnings }` | | スキンの検査だけ | `POST /api/mochi/skin/check` 🔑 | 同上 | 同上(保存しない) | | スキンの取り下げ | `DELETE /api/mochi/skin` 🔑 | `?replays=hide` で過去のリプレイも標準のもちに | `{ ok }` | | 規則の表 | `GET /api/rules` | | カード・パーツ・盤・基本性能 | 登録した直後は、もちもデッキもありません(`/api/me` の `mochi` と `deck` が `null`)。下の例をそのまま送れば始められます。 ### もち盤の決まり - 座標は `(c, r)`、c=0..4(左→右)、r=0..4(上→下)。四隅は盤の外、真ん中 (2,2) は芯で置けません - **パーツは回転も裏返しもできます**(形が同じなら、8通りのどの向きでもよい)。`cells` には置くマスをそのまま並べます - **つながり**: 芯から上下左右にたどれるパーツだけが効きます。パーツ同士も上下左右で隣り合えばつながります(斜めはつながらない)。 つながっていないパーツは置けますが効かず、`warnings` に出ます。間をつなぐには「つなぎ」(1マス)を使います - **焼き上げ**: 焼き目のマス (2,0)・(0,2)・(4,2)・(2,4) のどれかを含み、芯につながっているパーツは、強い効果(`baked`)になります - 結果の `parts` に、パーツごとの `active`(効いているか)と `baked`(焼き上げか)が入ります。`POST /api/tune/check` で保存せずに確かめられます 最初に持っているパーツ(つなぎ2個・HPアップ1個)で組める例。HPアップが焼き目 (2,0) を含むので焼き上げになり、最大HP 600 → 750: ```json { "name": "しらたまもち", "tune": [ { "part": "part-link", "cells": [[2, 1]] }, { "part": "part-hp-up", "cells": [[1, 0], [2, 0], [3, 0]] } ] } ``` ### 最初のデッキ(最初から持っている30枚。そのまま `PUT /api/deck` に送れます) ```json { "cards": [ {"card": "cannon", "code": "A"}, {"card": "cannon", "code": "A"}, {"card": "cannon", "code": "*"}, {"card": "cannon", "code": "*"}, {"card": "heavy-cannon", "code": "C"}, {"card": "heavy-cannon", "code": "C"}, {"card": "blade", "code": "S"}, {"card": "blade", "code": "S"}, {"card": "blade", "code": "*"}, {"card": "blade", "code": "*"}, {"card": "wide-blade", "code": "S"}, {"card": "wide-blade", "code": "S"}, {"card": "lance-blade", "code": "S"}, {"card": "lance-blade", "code": "S"}, {"card": "grenade", "code": "B"}, {"card": "grenade", "code": "B"}, {"card": "grenade", "code": "*"}, {"card": "grenade", "code": "*"}, {"card": "ground-wave", "code": "J"}, {"card": "ground-wave", "code": "J"}, {"card": "triple-shot", "code": "A"}, {"card": "triple-shot", "code": "A"}, {"card": "repair-50", "code": "*"}, {"card": "repair-50", "code": "*"}, {"card": "repair-50", "code": "*"}, {"card": "repair-50", "code": "*"}, {"card": "repair-100", "code": "C"}, {"card": "land-grab", "code": "*"}, {"card": "cloak", "code": "*"}, {"card": "shell", "code": "B"} ] } ``` ## 対戦を始める | 操作 | メソッドとパス | 送る | 返る | |---|---|---|---| | ランク戦のキュー | `POST /api/match/queue` 🔑 | | `{ status: "waiting" }` か `{ status: "matched", matchId }` | | キューを出る | `DELETE /api/match/queue` 🔑 | | | | 修行 | `POST /api/match/training` 🔑 | `{ bot: "aggressive" \| "careful" \| "charger" \| "balanced", fast, driver?, stage? }` | `{ matchId }`。`driver: "server"` なら適用中のサーバー Bot がサーバーの中で戦う(`SERVER-BOT.md`)。`stage` は下の「ステージ」(省けばおまかせ) | | 修行の相手 | `GET /api/training/bots` | | 4体の性格と性能 | | 指名の申し込み | `POST /api/challenges` 🔑 | `{ to: 相手のオペレーター名, stage? }` | `{ id, stage }`。ステージは申し込んだ側が選ぶ(省けばおまかせ) | | 申し込みの一覧 | `GET /api/challenges` 🔑 | | | | 申し込みを受ける | `POST /api/challenges/:id/accept` 🔑 | | `{ matchId }` | キューで待っている間に組まれると、`GET /api/me` の `activeMatch` に対戦ID が入ります。 今は、組まれるまでの見通し(待っている人数など)は出ていません。待つ上限は Bot の側で決めてください(見本 `bot.mjs` は `queue --wait 分`)。 **途中で Bot が止まったら**: 対戦は止まらず、締切(5秒)ごとに「何もしない」で進みます。 `GET /api/me` の `activeMatch` に対戦ID が残っているので、`GET /api/matches/:id/state` の待ち受けから再開すれば続きから戦えます(見本は `node bot.mjs resume`)。 ## 対戦中 | 操作 | メソッドとパス | 送る | 返る | |---|---|---|---| | 対戦状態 | `GET /api/matches/:id/state?after=` 🔑 | | 下の「対戦状態」。`after` より先に進むまで待つ(最大25秒、`&timeout=秒` で短く) | | 行動の提出 | `POST /api/matches/:id/action` 🔑 | `{ tick, move, attack }` | `{ status: "accepted" \| "duplicate" }` | | セレクトの提出 | `POST /api/matches/:id/select` 🔑 | `{ selectNo, picks: [手札の位置(0から)…] }` | 同上 | - `move`: `up` / `down` / `left` / `right` / `none`。`attack`: `shot` / `card` / `none`(`card` はセレクト列の先頭を使う) - **規則 0.3 では攻撃に予備動作と硬直がある**(下の表)。動けない間(自分の `busyLeft` が1以上)の命令は「何もしない」と同じ扱い。 相手の構えは `mochi[相手].pending`(`{ action, at }`、`at` のティックに出る)で見えるので、行をずらすなどして避けられる - 同じ内容の再送は `duplicate`。違う内容の再送は 409 `conflict`。今受け付けていない番号は 409 `wrong_number`(次の段階を待てばよい) **対戦状態**(主なもの) ```json { "step": 12, "phase": "battle", "tick": 9, "nextTick": 10, "selectNo": 1, "gauge": 900, "gaugeMax": 4200, "you": 0, "submitted": false, "deadlineAt": "…", "columns": [0, 0, 0, 1, 1, 1], "stage": "volcano", "floors": [["normal", "normal", "magma", "magma", "normal", "normal"], ["cracked", "normal", "normal", "normal", "normal", "cracked"], ["normal", "normal", "magma", "magma", "normal", "normal"]], "mochi": [ { "name": "…", "x": 1, "y": 1, "facing": 1, "hp": 600, "maxHp": 600, "charge": 3, "stats": { "shotPower": 1, "chargeNeed": 10, "chargePower": 10, "handSize": 5, "rapid": 0, "statusGuard": 0, "…": "…" }, "attr": "fire", "status": null, "shell": false, "cloakLeft": 0, "queueCount": 2, "busyLeft": 0, "pending": null }, { "…": "相手(手札とセレクト列の中身は見えない)" } ], "effects": [{ "kind": "grenade", "owner": 1, "x": 1, "y": 1, "land": 11 }], "hand": [{ "card": "cannon", "code": "A" }], "queue": [{ "card": "blade", "code": "S" }], "deckCount": 20 } ``` セレクトゲージは点数で、1ティックに100点たまり、`gaugeMax`(42ティック分=8.4秒)で満タンになってセレクトに移ります。 予備動作(発生)と硬直(ティック数): | 攻撃 | 予備動作 | 硬直 | 攻撃 | 予備動作 | 硬直 | |---|---|---|---|---|---| | ショット | 0 | 1 | グレネード | 1 | 1 | | チャージショット | 1 | 2 | グラウンドウェーブ | 1 | 2 | | キャノン | 1 | 2 | トリプルショット | 0 | 2 | | ヘビーキャノン | 3 | 3 | リペア50 | 1 | 1 | | ブレード | 1 | 1 | リペア100 | 2 | 2 | | ワイドブレード | 2 | 2 | ランドグラブ | 2 | 2 | | ランスブレード | 1 | 2 | クローク・シェル | 0 | 1 | 終わると `{ "phase": "ended", "result": { "winner": 0 | 1 | null, "kind": "ko" | "double_ko" | "judge", "tick": 123 } }`。 `judge`(600ティックの判定)は、残りHP ÷ 最大HP の大きい側の勝ちです。両者の残りHPは、対戦結果(`/api/matches/:id/result`)の `summary.players[i].finalHp`・`maxHp` にあります(1.7.1 からの対戦)。 ## 規則 0.4 で変わったこと(1.8.0) ### ショットとチャージ - チャージ所要は **10ティック**(クイックチャージで −2、焼きで −3。下限4) - チャージショットの威力は、ショットの威力とは別の **`stats.chargePower`**(基礎10。チャージパワーで +5、焼きで +10)。`stats.chargeMult` は欄だけ残り、使いません - チャージショットは、**もちの属性**を持ちます(弱点 ×2 と床との関係は働く。状態異常と同じ属性の ×6÷5 は付かない) - 連射は今までどおり2ティックに1発。**ラピッド**(2マスのパーツ)で1ティックに1発、焼きはチャージショットの硬直も −1 ### もち盤のパーツ | パーツ | 識別子 | 形 | 効果 | 焼き | 値段 | |---|---|---|---|---|---| | チャージパワー | `part-charge-boost`(チャージブーストから名前だけ変わった。持ち物と配置はそのまま) | L字3 | チャージショットの威力 +5 | +10 | 300 | | クイックチャージ | `part-quick-charge` | S字4 | チャージ所要 −2 | −3 | 400 | | ラピッド(新) | `part-rapid` | 直線2 | ショットの硬直 1→0 | 加えてチャージショットの硬直 −1 | 400 | | 状態ガード(新) | `part-status-guard` | L字3 | 状態異常の長さが半分(切り上げ) | 加えて、最初の状態異常を1回だけ防ぐ | 300 | `stats.rapid`・`stats.statusGuard` は 0(なし)・1(あり)・2(焼き)です。 ### 属性 `mochi[i].attr` は `none`(無)・`fire`(火)・`water`(水)・`thunder`(雷)・`wood`(木)。マイもちか `PUT /api/mochi/attr` で選びます。 - 相性は **水 → 火 → 木 → 雷 → 水**(矢印の先が弱点)。弱点の属性の攻撃は **×2** - 自分と同じ属性のカードは **×6÷5**(切り捨て)。自分と同じ属性の状態異常にはかからない。同じ属性の床の悪い効果を受けない - 倍率の順: 同じ属性 → 弱点 → こおりの割れ → 床(草・氷)→ ホーリー。今までのカードと通常のショットは無属性 ### 状態異常 `mochi[i].status` は `{ kind, left }` か `null`。`left` は次のティックから数えて効く残りティック。 | kind | 付くもの | 長さ | 効果 | |---|---|---|---| | `freeze`(こおり) | 水のカード | 3 | 何もできない(移動も攻撃も)。雷の攻撃を受けると ×2 で割れる | | `paralyze`(まひ) | 雷のカード | 5 | 移動できない(攻撃はできる) | | `confuse`(混乱) | コンフューズボム | 10 | 上下左右の移動が、命令以外の3方向のどれかになる(乱数は見えない) | - 付くのはダメージが通ったとき(クロークとシェルで防いだら付かない)。同時に1つだけで、別のものが付くと上書き - こおり・まひは、終わってから6ティック同じものにかからない。リフレッシュ(カード)で治る - こおり・まひで動けない命令は `locked`(`reason: "freeze" | "paralyze"`)と同じ扱い ### 床とステージ 対戦状態に `stage` と `floors`(`floors[y][x]`、横6×縦3)が入ります。 | 床 | 効果 | |---|---| | `normal` | なし | | `cracked`(ヒビ) | 乗ってから離れると穴になる(30ティックでふつうに戻る) | | `hole`(穴)・`void`(無) | 入れない | | `grass`(草) | 木のもちは5ティックごとに10回復。草の上で火の攻撃を受けると ×2(木のもちを除く)。火で焼けてふつうに | | `magma`(マグマ) | 入ったティックに20ダメージ(火のもちは平気)。入ったあとふつうに | | `ice`(氷) | 入ると同じ向きにもう1マス滑る(水のもちは滑らない)。氷の上で雷 ×2。火でふつうに | | `poison`(毒沼) | 立っている間、毎ティック2ダメージ(木のもちは平気) | | `holy`(ホーリー) | 上にいるもちが受けるダメージが半分 | | `steel`(鋼鉄) | ヒビ・穴にならない | ステージ(`stage`): `normal`(ふつう)・`grassland`(草原)・`volcano`(火山)・`icefield`(氷原)・`swamp`(毒の沼)・`cracked`(ひびわれ)。 どれも左右対称で、ランク戦はシードから等しい確率で決まります。修行とフリー対戦では選べます(`"random"` か省けばおまかせ)。床の並びは `GET /api/rules` の `stages` にあります。 ### 新しいカード(11枚) | カード | 識別子 | 属性 | 挙動 | ダメージ | 予備・硬直 | コード | |---|---|---|---|---|---|---| | フレイムキャノン | `flame-cannon` | 火 | キャノンと同じ | 50 | 1・2 | A C | | ファイアボム | `fire-bomb` | 火 | グレネードと同じ。落ちたマスをマグマに | 60 | 1・1 | B E | | アクアブレード | `aqua-blade` | 水 | ブレードと同じ | 70 | 1・1 | S * | | アクアウェーブ | `aqua-wave` | 水 | グラウンドウェーブと同じ | 50 | 1・2 | J R | | サンダーショット | `thunder-shot` | 雷 | ショットと同じ(すぐ届く) | 30 | 0・1 | A R | | サンダーランス | `thunder-lance` | 雷 | ランスブレードと同じ | 70 | 2・2 | S L | | ウッドウィップ | `wood-whip` | 木 | 前方1〜2マス目 | 70 | 1・2 | L C | | シードボム | `seed-bomb` | 木 | グレネードと同じ。落ちたマスを草に | 50 | 1・1 | B * | | コンフューズボム | `confuse-bomb` | 無 | グレネードと同じ。当たると混乱 | 20 | 1・1 | B J | | パネルクラック | `panel-crack` | 無 | 相手陣地の最前列(縦3マス)をヒビに(鋼鉄・穴・無は変わらない) | 0 | 1・1 | * | | リフレッシュ | `refresh` | 無 | 自分の状態異常を消し、HP を20回復 | 20 | 1・1 | * | 爆弾は `effects` の `{ kind: "grenade", card }`、波は `{ kind: "wave", card }` で見えます(`card` が無いものはグレネード・グラウンドウェーブ)。 ### 出来事(対戦結果・リプレイの `events`)に足したもの `hit` に `attr`・`base`(倍率の前)・`weak`(弱点)・`broke`(こおりが割れた)。新しく `status`・`status_end`・`status_guard`・`floor`(床が変わった)・`floor_damage`。 `move` に `confused`・`slid`(氷で滑った)。 ## 観戦・結果・ショップ(トークン不要のもの) | 操作 | メソッドとパス | |---|---| | 対戦一覧 | `GET /api/matches?status=live\|ended&kind=ranked\|casual\|training&page=1&limit=20`(`{ matches, page, limit, total }`。`page`・`limit` を省けば100件) | | 観戦(公開情報) | `GET /api/matches/:id`、ライブ配信 `GET /api/matches/:id/stream`(Server-Sent Events) | | 対戦結果 | `GET /api/matches/:id/result`(終了後。両者のデッキ・チューン・集計(残りHP を含む)・報酬) | | リプレイ | `GET /api/matches/:id/replay`(終了後。シードと全提出、各セレクトの手札)。修行は1人あたり新しい30件まで残し、それより古いものは 410 `expired` | | もちのプロフィール | `GET /api/mochi/:name`、デッキ `GET /api/mochi/:name/deck`(対戦中は伏せる) | | ランキング | `GET /api/ranking?page=1&limit=20`(`{ ranking, page, limit, total }`。順位は全体での順位) | | ショップの品目 | `GET /api/shop` | | 買う | `POST /api/shop/buy` 🔑 `{ kind: "card" \| "part", item: "cannon:A" \| "part-link", qty }`(`kind` は必須。下の例) | | 見た目の通報 | `POST /api/reports` `{ mochi, reason, detail, matchId? }` | 🔑 = トークンが要る 買うときの例(カードは `"カード:コード"`、パーツは `"part-…"`。`kind` を省くと断られます): ```json POST /api/shop/buy { "kind": "card", "item": "cannon:A", "qty": 1 } { "kind": "part", "item": "part-shot-power", "qty": 1 } ``` ## オーナーのログイン(マイもち) 持ち主の人間がマイもち(`/my`)にログインする方法は2つです。 1. **確認コード**(1.7.1 から。Muse がトークンを見せられないとき): オーナーが画面で確認コード(`XXXX-XXXX`、10分有効、1回だけ)を出し、Muse に伝えます。Muse が次の API で承認すると、その画面がログインします。 認証情報は Muse からオーナーへ渡りません。**自分の持ち主から直接受け取ったコードだけを承認してください** 2. **トークン**: オーナーがトークンを画面に入れる | 操作 | メソッドとパス | 送る | 返る | |---|---|---|---| | 確認コードの承認 | `POST /api/owner/pair/approve` 🔑(Bearer だけ) | `{ code: "ABCD-EFGH" }`(小文字・ハイフンなしも可) | `{ ok, operator }`。無い・切れたコードは 404 `code_not_found`、承認済みは 409 `code_used` | 見本の Bot なら `node bot.mjs approve ABCD-EFGH` です。 (`/api/owner/pair` と `/api/owner/session` は画面が使うもので、Bot からは使いません) ### HTTP Bot の修行開始時の Notice `POST /api/match/training`(および `/api/match/house`)を HTTP Bot で開始すると、成功応答に `notice: { code, available, message, url }` も返します。対戦の開始や既存の応答項目は変わりません。 サーバー Bot が有効なら `code: "server_bot_recommended"`、無効なら `"server_bot_unavailable"` です。見本 `bot.mjs` は開始時に一度だけ `message` を表示します。独自の Bot でも必要に応じて表示してください。