思うだけで学ばない日記 2.0

思うだけで学ばない日記から移転しました☆!よろしくお願いします。

【何の】地獄巡りツアー!!!【罰ゲームだ?】

USIに対応すた、*1
USI超クールっすね
特に予測読みの処理手順がプロトコルに内包されてるところとかσ(・∀・)σ
それより前の実装では思考部は評価値と最善手のみ出力すれば十分であるという妄信に基づき思考部と置換表を作成して*2、予測読み処理まで好きに作ってるうちに*3、最終的にCSAサーバプロトコルの通信部との分離が不分明な作りになってしまってた;

とわいえ、思考部とGUI部の完全分離を標榜し神のごとき単純さを誇るUSIプロトコルにもいくつか瑕疵がないわけでもない希ガス、
パッと見わかりやすそうなのから逝く、

  • positionコマンドで毎回初期局面と全手順を送ってくるのが冗長

ま、これは思考部を単純にできてめちゃくちゃ助かっているので個人的にはさほど文句はない。ステートレスはこの場合ネ申、
とわいえ、毎回「position startpos moves (差し手たくさん)」という形で局面を送りつけてくる必然性には疑問を感じないでもない。たとえばこんなことが許されていいのでしょうか…

position startpos moves 9g9f 1c1d 8h9g 3a4b 9g8f 7c7d 8f9e 8c8d 9e8f 2a1c 6i6h 8a7c 8f9e 1d1e 9e8f 2c2d 1g1f 2d2e 1f1e 2e2f 2g2f 3c3d 1e1d 1c2e 2f2e 2b3c 1d1c+ 3c4d P*1b 1a1b 1c1b P*2g 2h2g 7c8e 2e2d 4d5e 2d2c+ 5e7c P*2b 7c5e 2b2a+ 6a6b 2g2e 3d3e L*5f 8e7g 5f5e 7g8i+ 5e5c+ 4b5c P*7b P*5h 6h5h 7a7b P*7c 7b7c 7i7h 8i9i B*3c 5c4b N*5d P*5c 5d4b+ 4a4b S*3a 5a6a 3a4b+ 9i9h 3c5e+ 6c6d P*1d L*5d 5e3c 5d5g 3c4c 6a7b 1d1c+ 8d8e 8f9e L*8d P*5d P*4h 4i4h P*2d 2e2d 5g5h+ 4h5h 9c9d 9e7g 5c5d 4c5d 7b8c P*5c G*6c 5c5b+ 6c5d

これは512文字を超えており、下手な固定長バッファで受けようとするとあふれてしまう*4
SFENはこのようなケースもちゃんと考えられており、任意局面を初期局面からの手数付きで表現する方法が用意されているというのに広く無視されいてる気がしてちょっと悲しい、、
(2011/10/01修正) USIサーバーが対局中に毎回startposと手順全部を送ってくるのにはちゃんと理由があった!手順抜きでいきなり第n局面を送りつけられると、千日手や連続王手の千日手の判定ができない、、orz

  • positionコマンドで送られてくる全手順を読み終えるまで自分が上手なのか下手なのかわからない(ことがある*5

これは思考部内部における局面の内部表現において、自己が仮想的に下手onlyとみなす場合にちょっと面倒くさい
全手順を読み終えるまではとりあえず自己が下手と仮定しておき、読み終えたときの手番が相手手番になっていたら(仮定が違ってたということで)上手と解釈し直す処理が要る。まあTop/Bottomフラグと局面の内部表現の上下を反転するだけっていやーだけだが、自己が上手か下手かは重要な情報である割に*6、それを知るためにpositionコマンドの解釈において実質2パス処理めいたロジックを大半の開発者が書かねばならないというのは通知のされ方があと一歩な気ガス、*7

  • ponderhit時に相手の消費時間が通知されない

自己手番において、bestmoveコマンドで最善手のみでなく予測手もUSIサーバに通知すると、USIサーバがpositionコマンドとgo ponderコマンドを送ってきて思考部は予測読みを開始できる。このときのgo ponderコマンドでは自己と相手双方の残り時間が通知されるわけだが…
予測読みが当たった時通知されるのはたった一語、ponderhitのみ。
タイミングからするとponderhitが来るのは、相手手番が終わって次の自己手番が実際に始まる時点なわけで、思考はそのまま続けて良いにしても、時間条件は最新状況に合わせて再計算したい。ponderhitシチュで最新状況とgo ponder時点とのゲームに関わる差異といえば相手の消費時間しかないわけだが、それを知るためだけのために、思考部*8でgo ponder受信からponderhit受信までの時間を独自に計測し、go ponder時点の相手の持ち時間から引き、その上で時間条件を再計算する作りにせねばならない。これは相手着手が消費時間付きで送られてくるCSAサーバプロトコルより不親切かな、と*9

  • ponderhit受信前後でPV通知の場合分けが要るのが面倒くさい&対称性が崩れてしまっている

通常思考中なら、自己手番の手から始まるPVが画面表示されれば良くて、それで辻褄が合う。
これは、探索ルーチンか何かがときおりinfo pvコマンドで最新PVを普通に通知すれば済む。
一方、予測読み中は、実際の盤面は相手手番であって相手が思考中なので、相手が指すであろう予測手から始まるPVが画面表示されて欲しい。しかし予測読みしている当方思考部は、すでに相手の予測手の先にある自己手番局面以降を探索中なので、上と同じ普通に最新PVを通知するロジックでは、相手の手をすっとばして自己手番の手から始まるPVを通知してしまう。
つまり通常思考中と予測読み中とでinfo pvコマンドの処理を変えねばならず、(position + go) v.s. (position + go ponder)のレベルでは成立すると見えた思考部の対称性がここで崩れる。
さらに、予測読みはponderhit受信をもって通常思考に切り替わり、これは予測読み処理に対して非同期に起こるから、上記PV通知のモード切り替え処理は、作りによってはクリティカルセクションが要って重くなりかねない。とくにinfo pv送信処理を探索ルーチンから直接呼ぶ場合、これはちょっと気になる*10
必要な情報はgo ponderやponderhitを送ってくる当のUSIサーバ側で完璧にわかるのだから、こんなのUSIサーバ側で面倒みて欲しい*11

  • stopが送られてきたとき最善手の候補が未定ならどうすれば良いのか

USIサーバからstopが送られてきたら思考部は即思考を中止し、bestmoveコマンドを送らねばならない。(すると通常はUSIサーバから次の指示(position + go)が来る。)
ここでのbestmove送信は思考部からUSIサーバに対する(思考停止の)完了通知の意味においてのみ必要とされるのだと思う。USIサーバはこのbestmoveで通知した手を無視する。
しかし、だからといって、当方予測読み開始とほとんど同時に相手が着手した場合等、stopを受信したのだが最善手の候補が未定のとき、bestmove resignと返したり、テキトーな手ということで反則手を返したりしても大丈夫なのか、実験してみるまではちょっと気になった、
いやそれだけの話なんですけども、、*12

以上USIプロトコル原案をろくすっぽ読みもせずに書いてみた(マテ、

読んでないついでに言うと、エンジンAが投了などして勝敗が決したときBが予測読みの最中だったとすると、Bは多分gameoverを受信してそれをstopと同等に扱うことが気体されると思うのだけど(つまり思考を即時中止し、適当な内容でbestmoveを返す)、そう作ったらgameoverが両方のエンジンに2回来るなりよ…

<1:info string *** Search done. [-8192, -8192], v=-8192
<1:bestmove resign
>1:gameover lose
>2:gameover win
<2:info time 116 depth 4 seldepth 4 nodes 13349 score cp -900 curmove 2d3e nps 115077 pv 5a5b 2d3e 9a9b 3e2d 1b1c
<2:bestmove 2d3e ponder 9a9b
>1:gameover lose
>2:gameover win
<1:info string UsiPonder::TermEngine(): Called.
<2:info string UsiPonder::TermEngine(): Called.
<1:info string Order file saving... (D:\_install7\TestShogi2\Grimoire5\Release\order_black.txt)
<2:info string Order file saving... (D:\_install7\TestShogi2\_work\order_black.txt)
>1:isready
>2:isready

■追記 2011/10/01

USIサーバーが対局中に毎回startposと手順全部を送ってくるのにはちゃんとした理由があったので直した。

*1:いんや正確には5月中順に対応してみたんだけど、無意味にFSMFSMした作りだったのを改めた

*2:置換表には手の情報が入っていない

*3:ゆえにCSAサーバから相手着手が来るまでの間、特別な時間制御の下で探索を繰り返して1局面づつ進めるやり方を選んだ

*4:または私は如何にしてistream::getline(char*, size_t)を止めてstd::getline(istream, string&)を使うようになったか

*5:startpos+(差し手たくさん)、という形で送ってくる場合。

*6:局面の内部表現における盤面の向きを基準に表された手をbestmoveコマンドやinfo pvコマンドで外に出す際決定的に要る

*7:個人的にはLL(1)的に物事が決まってホスイ、

*8:探索ルーチンそのものでなくてUSIサーバからの受信を担当するクライアント部分で良いが

*9:「相手の」持ち時間計算を独自にやる結果誤差が生じかねないし、「相手の」秒読み突入判定とか、それに伴う「相手の」持ち時間計算の切替も思考部開発者が自前で実装せねばならなくなる。USIサーバ側でできるはずの仕事なのに!

*10:そんなに頻繁に呼ぶ作りにするものではないが。

*11:USIサーバ側でできるはずの仕事なのに!!・2

*12:stopが来たということは予測読み非成立ということで、現実にならなかった予測局面において仮に詰みだったとしても現実にならなかった以上その事実は無視されるのが筋だと思うし実際の挙動もその方向になっているので問題ではないが。
反則手を返した場合理論上どうあるべきかは知らない。