右図は、あから2010の通信の一部でも使っていた、USIをベースにしたプロトコルの簡略化した状態遷移図です。(ちなみに秋頃書いていたGPS将棋の紹介記事がコンピュータソフトウェア1月号に掲載予定です。USIにも触れていてこの図はここから取りました。)
図中で、>が上流から思考部への通信、<が思考部から上流への通信です。
基本的な状態遷移は図の実線の矢印部分です: プログラムは左のidle状態でスタートし、positionを受け取ったり、図では省略されていますがoptionなどの処理をします。go infiniteを受け取ると、思考に入りsearch状態に遷移します。search状態では、infoという文法で定期的に状態を上流に伝えます。stopを受け取ると searrh finished状態に遷移し、bestmove で最善手を報告してidleに戻ります。
さて、思考部を作っていると、stopが来る前に指し手が一手に定まったらbestmoveを返したくなります(特に既存のプログラムを微調整してUSIに対応する場合)。上流側も、もう指手が変化しないという状態を知ると良い場合があります(たとえば、いわゆる合議のように思考部の最善手を集約して指手を決める時に持ち時間を節約したい)。そこで、あから2010では、search状態から下の点線部分にあるように思考部がstopを待たずにsearch finished 状態に遷移することを許しました。すると、上流のstopと思考部のbestmoveが同時に起こったらどうなるか? という競合の問題を解決する必要があります。あから2010の場合は、図のsearch finishedやidle状態の点線のように、search以外の状態で到着したstopを無視するという解をとりました。(が、少し実装は面倒なので、この文章の末尾で別の案を提案します)
USIの元提案 (http://www.geocities.jp/shogidokoro/usi.html) や、将棋所バージョン (http://www.geocities.jp/shogidokoro/usi.html) では、go infinite に限ってstopが来るまではbestmoveの出力を禁止することで、競合が起こらないようにしています。しかし、go byoyomi 10000 等時間指定がある時は、依然競合があります。時間制限がある時はstopを使わないなら別ですが、それは不便ですし、仮に時間前のbestmove出力を禁止したとしても、stopが思考部に届くまでに10秒を越える等の例で分かるように、通信に遅延がある環境で頑健なクライアントを作りたい場合は競合に対処しておく必要が依然としてあるわけです。
ところで、囲碁のgtpというプロトコルでは、コマンドと応答に名前をつけることが可能です。具体例を示すと、usiのgoとbestmoveに対応する応答はgtpではgenmove b= E7
となりますが、ここでgenmoveの行頭に数値のidをつけると、0 genmove b0 = E7
というように、応答の先頭にもそのidを提示する規約になっています。http://www.lysator.liu.se/~gunnar/gtp/gtp2-spec-draft2/gtp2-spec.htmlの2.5節のあたり。
そこで、USIでもposition, go, info, stopにid=xで対応する局面を明示可能にして、position id=0 startposgo id=0 infiniteinfo id=0 pv 7g7fstop id=0bestmove id=0 7g7f
等でいかがでしょう。細かくは、goやstopで提示されるidは思考部が最後に受け取ったpositionと同じでなければならない、と決めておいたほうが良いかもしれません。またid=xをどこに挟むかは、理由があれば変更しても良いかもしれません。(コメント歓迎)
細かいところでいくつか
bestmove id=0 7g7fで、その後info id=0 7g7f 3c3d 2g2fであれば、34歩の局面を予想している、と解釈可能(ですよね)。gtpやgogui から輸入すると良さそうな機能としては、
コメントいただいたこともあり少し補足します
コマンドが1対1の関係になっていればidは一意に定まるわけですからidをつける必要はないと思います^^<br>もし私がUSI2.0を定義するのであればgoとbestmoveとstopが1セットになるように変更したいですね<br><br>・GUIはgoの次には必ずstopを送る<br>・GUIはgo byoyomi 10000 のような場合もstopを送る<br>・GUIはstopを送る前にbestmoveを受け取ったらstopを即座に送る<br><br>stopの直後にbestmoveと、bestmoveの直後にstopの2パターンだけであれば<br>USIエンジン側の待ち時間はほぼ0ですから競合しても問題はないだろうと思います
はい、実はGPS将棋やあから2010は、そうやってgo, bestmove, stopの回数を数えながら動いていたりします。<br>ただどちらが良いかというと、idを使った方が簡潔で直感的な実装になるのではないかと。
ponderについてですが単純にponderhitの代わりにstopを送るのでどうでしょうか<br>ponderが外れたら即座にstopを送る/ponderが当たれば時間ぎりぎりまで粘ってstopを送る<br>ようにGUIの側で対処すればgoとbestmoveとstopを1セットに保てるのでわかりやすいかなと
返信ありがとうございます<br>なんだか金子様の案がすばらしいような気がしてきました<br>あんまり余計な主張しますと自分でGUIを作らなければならなくなりますのでこの辺で失礼いたします…
1回のgo XXXX に対してbestmoveを返すのは一回という、goとbestmoveの1対1の対応関係さえあれば、いいだけのように思うのですが。すなわち、「stopに対してbestmoveを返す/応答する」という仕様がおかしくて、stopはbestmoveを返していないときの督促シグナルに過ぎないという扱いにします。<br>これですと何か問題があるのでしょうか?
はい、動きます。実際のところ、gpsusi.ccではgoとbestmoveの数を数えています。idを推す理由を追記してみました。
ああ、意味がわかりました。確かに相手番での先読み(USIのponderを使わずに独自の方法で先読みしていた場合)絡みの読み筋表示のためにはidがあったほうが良いですね。
基本的な状態遷移は図の実戦の矢印部分です:<br><br>「実線」だと思うのですが、「実戦」に化けてます。<br><br>どうでもいいところで遅いコメントですみません。
ありがとうございます