最近USIプロトコルという通信プロトコルを使って将棋の思考エンジンを呼び出すものを作る機会がありました。
このUSIプロトコルというのは技術屋の立場から見るととてもプロトコルとは呼べないものでけっこう苦労したので書き留めておきます。
USIプロトコルとは
USIプロトコルは将棋アプリのGUI等(以下、UI)と思考エンジン(以下、エンジン)が標準入出力で通信するためプロトコルです。USIプロトコルに対応しているUIとエンジンであれば自由に組み合わせて使うことができます。
UIはエンジンをサブプロセスとして起動して、棋譜データなどのコマンドをサブプロセスの標準入力に送信します。
エンジンはコマンドを受け取ると思考を開始して次の手を標準出力で返します。
コマンドはいくつかあるのですが、共通の仕様は以下の通りです。
- コマンドの送信は改行コード
- コマンドとオプションなどは半角スペースで区切る
主なコマンドには以下のようなものがあります。
コマンド | 正常時の応答 | 説明 |
---|---|---|
usi |
id name ... id author ... option ... usiok
|
エンジン起動時に最初に送るコマンド |
isready |
readyok |
対局開始前に送る |
setoption name ... value ... |
なし | オプションの設定 |
usinewgame |
なし | 対局開始時に送る |
position ... moves ... |
なし | 局面を送る |
go ... |
bestmove ... |
思考を開始させて次の手を受け取る |
quit |
エンジンを終了させる |
以下はUSIプロトコル通信の例です。わかりやすくするためにUIから送るコマンドの行頭に>
を付けてあります。
> usi
id name ...
id author ...
option ...
usiok
> isready
readyok
> usinewgame
> positon startpos moves 7g7f
> go byoyomi 1000
bestmove 8c8d
> positon startpos moves 7g7f 8c8d 1g1f
> go byoyomi 1000
bestmove 3c3d
USIプロトコルのツラいところ
USIプロトコルの共通仕様は非常にシンプルなのですが、シンプルであるが故に色々と辛いところがあります。
- コマンドを送信しても応答がないコマンドがある
- エラーに関する規定がない
コマンドを送信しても応答がないコマンドがある
一般的に標準入出力やソケット通信などのプロトコルでは、コマンドを受け取った側は正常に処理できたかどうかの結果を返すのが常識です。
ところがUSIプロトコルのコマンドの中には送信した後にエンジンから応答がないものがあります。これではコマンドが正常に実行できたか確認できません。
また応答があるものとないものがあるとUI側でコマンドを実行する作りが統一できません。
エラーに関する規定がない
USIプロトコルではコマンドが不正だった場合などエラーに関する規程が一切ありません。
この為、エンジンや状況によってエラー発生時の挙動が様々です。
例えば将棋所というGUIアプリに付属の Lesserkai では以下のような挙動でした。
> hoge
> postion startpos moves hoge
illegal move hoge
position startpos moves 1a9i
illegal move 1a9i
ちなみに自分が使っていたエンジンでは以下のような挙動でした。
> hoge
info string unknown command: hoge
> positon startpos moves hoge
info string unknown command: positon startpos moves hoge
position startpos moves 1a9i
go
info string OptTime 0sec MaxTime 0sec Progress = 1.296% Threads = 4
bestmove 9c9d
このようにエラーの形式がバラバラではUI側では受け取りようがありません。
参考:他のプロトコルのレスポンス
これだけ見ても「何がいけないの?」という人もいると思うので、参考までに他のプロトコルでは応答がどのような形式になっているか書いておきます。
基本的にはプロトコル全体で応答の形式を規定しているのでエラーの発生を確実で受け取れるようになっています。
HTTP
リクエストに対して必ずレンスポンスが返ってきます。
HTTPレスポンスでは1行目はステータス行で、HTTP/1.1 200 OK
のようにHTTPステータスコードが含まれています。
POP3
リクエストに対して必ずレンスポンスが返ってきます。
POP3サーバーはコマンドを受け取ると成功、不成功に応じて+OK
または、-ERR
から始める文字列を返します。
エラーの場合には-ERR
の後にエラーメッセージを返します。
SMTP
リクエストに対して必ずレンスポンスが返ってきます。
リプライコード+リプライメッセージという形式になっています。
これからプロトコルを設計する人へ
プロトコル設計で重要なのは共通で使える仕様を決める事です。正常時だけでなく異常時の振る舞いも含めて考える必要があります。
通信相手によって送受信する形式が違っていたり、コマンドによって応答やエラーの形式がバラバラではプロトコルとは言えません。
これからソケット通信や標準入出力のプロトコルを設計するという人は、この辺りを踏まえて共通で使えるように設計しましょう。