アリーナ · P6
異なる人の AI が、 同じマップで優劣を決める
公平性はレフェリーにしか保証できず、Bot の自主性には頼れません。Bot は共有メモリに一切触れられず、レフェリーが視界でフィルタリングした観測だけを受け取り、アクションを提出することしかできません。各アクションは先に所有権がチェックされます。
設計中 · 基礎実験を実施中 今すぐフェアモードで書く
3 つの形態
「公平性を保証できるか」で並べる
このプロジェクトの観測能力は、まさに「クライアントが全プレイヤーの状態を保持している」ことに由来します。これはあなた自身のマシンでも同じです。だから公平性は、誰もが信頼するレフェリーからしか生まれません。まず A を作り、同じプロトコルのまま B へ移行します。C は娯楽用です。
A
ローカルアリーナ
1 台のマシンで 1 つのゲーム。Bot はそれぞれスロットを 1 つ持ち、レフェリープロセスが代わりに命令を出す
レフェリーがすべてを管理:視界、所有権、ティック
向いている用途:開発とデバッグ、ローカルリーグ
B
ホスト型ラダー
A をサーバーに移す。プレイヤーは Bot をアップロードし、サーバーがサンドボックスで実行
A と同じ + コードの隔離
向いている用途:公開大会、ランキング
C
各自でオンライン対戦
各自が自分のマシンでゲーム + Bot を動かし、LAN で対戦
保証できない:注入したクライアントはマップ全体を読める
向いている用途:友人同士の親善試合
レフェリー
1 つのプロセスが両陣営に代わって命令する
Bot Aどんな言語でも
Bot Bどんな言語でも
WebSocket · JSON
レフェリー(信頼済み)
- 編成 マップを選んでゲームを開始し、スロットごとに種族と難易度を設定
- ティック T ゲームミリ秒ごとに 1 ティック(デフォルト 200)。ロックステップモードでは観測を送る間ゲームを一時停止
- 観測 スナップショット → 各スロットの視界でフィルタリング → JSON
- アクション 「ユニットがこのスロットに属するか」をチェック → 予算(ティックあたりの件数、APM)→ ファストレーンでバッチ送信
- 記録 毎ティックの観測サマリー + 各アクション:リプレイ、振り返り、学習データに利用可能
- 判定 あるスロットの建物がなくなる → 敗退。タイムアウト → 残存戦力 / 資源で判定
ファストレーン · player レーン ×2
1 つのゲーム · 2 つのスロット
プロトコル草案 v0
観測を受け取り、アクションを返す
JSON を送受信できるプログラムなら何でも出場できます。LLM にティックごとにアクションを直接出力させることも可能です。SDK で書いた Python の Bot はコードを変更する必要がありません。
| 観測 | 自軍ユニット + 自軍の視界内の敵ユニットのみ。クリープと金鉱は視界に応じて、または開始時に公開 |
| アクション上限 | 1 ティック最大 32 件、超過分は切り捨て(コマンドの連打でレフェリーが遅くなるのを防ぐ) |
| 同一ユニット | 同じティック内では最後の 1 件のみ有効 |
| 時間予算 | ティック長 × 0.9。超過したティックはパス。遅い側が損をするだけで、他を巻き込まない |
| ロックステップ | 厳密な公平性が必要な場合、観測を送る間ゲームを一時停止し、全員分がそろう(またはタイムアウトする)まで待ってから再開。マシンの速さが結果に影響しない |
| スタート地点の交換 | 同じ Bot の組み合わせで両者がそれぞれ 1 回ずつ戦い、マップの非対称性を打ち消す |
| ラダー | Elo / TrueSkill。各ペア最低 20 試合行ってから結論を出す(2 試合で判別できる最小の勝率差は約 60 ポイント) |
{"t": "obs", "tick": 57, "gameMs": 11400, "me": 1,
"res": {"gold": 320, "lumber": 150, "food": [18, 30]},
"units": [
{"id": 101, "type": "hfoo", "owner": 1,
"x": -4500, "y": 2200, "hp": 380, "hpMax": 420}],
"visibleEnemies": [
{"id": 733, "type": "ogru", "owner": 2,
"x": -3900, "y": 2500, "hp": 700}],
"deadlineMs": 180} 毎ティック 1 つ。このスロットのユニットとその視界内の敵だけを含みます。
id はレフェリーが割り当てる安定した番号で、試合中は変わりません。 {"t": "act", "tick": 57, "actions": [
{"do": "attack", "unit": 101, "target": 733},
{"do": "train", "unit": 5, "code": "hfoo"},
{"do": "cast", "unit": 7, "spell": "thunderbolt", "target": 733},
{"do": "move", "unit": 102, "x": -5000, "y": 2000}]} アクションの集合 = api.json の
category == "command" のもので、引数名も同じです。遅れて届いた act は破棄し、そのティックはパスとして扱います。 from openwar3 import Bot
class MyBot(Bot):
def on_tick(self, g): # 同じコード:ローカルでは g は Game、アリーナでは g は ArenaGame
for w in g.idle_workers():
g.gather(w, g.nearest(g.gold_mines(), w))
# ローカル: python tools/play.py --bot my_bot.py --fair
# アリーナ: 同名のメソッドで、内部は WebSocket。Bot のコードは 1 行も変えない インターフェース層の抽象化はこのために設計されています。SDK で書いた Bot は
Game を ArenaGame に替えるだけで出場できます。 基礎実験
順番に進め、前の実験が通らなければ次に進まない
| # | 実験 | 判定基準 | 既知の事項 |
|---|---|---|---|
| 1 | 1 つのゲームでスロットを 2 つ開き、どちらも組み込み AI なし | 両陣営のユニットがどちらも動かない | スロットの AI フラグと難易度フィールドは判明済み |
| 2 | ローカル以外のスロットに代わって命令を出す | 敵スロットのワーカーが実際に採掘に行く | 最も重要な項目。通らなければ「Bot ごとに 1 クライアント、本物のオンライン対戦」に後退するしかない |
| 3 | 任意のプレイヤー番号で視界を問い合わせる | 同じ地点について 2 つのスロットで異なる結果が返る | エンジンの可視性チェックは任意のプレイヤー番号を受け付ける |
| 4 | プログラムによる勝敗判定 | 一方の建物が全滅したら 1 秒以内に検知 | イベントバスにより直接的な入口がある |
| 5 | ロックステップの一時停止 / 再開 | 一時停止中にアクションを集め、再開後に反映 | 一時停止は検証済み |
ついでの検証:リファレンスブレインは現在、マップ全体の情報(コンピューター対戦相手の攻撃目標など)に大きく依存しています。アリーナに出るにはファサード API に切り替え、視界内のユニットだけを使う必要があります。マップ全体の情報を失ったとき、どれだけの強さが残るでしょうか?