Bonanzaの高速一手詰め

思うところあって、Bonanzaの一手詰めルーチン(mate1ply.c)を読んでいます。
一見、長くて複雑そうなのですが、長いのは駒の種類ごとに細かく場合分けしてあるためで、思ったほど複雑な処理をしているわけではなかったようです。
自分が読んでみた内容をざっと書いてみます。(間違っていたらすみません)


大まかな流れはこんな感じだと思います。

(1)駒を打つ王手で詰むか判定
 飛、角、金、銀、桂、香
  ※当然ながら、歩の処理はない。
   また、香の処理は飛を持たない場合のみ行う

(2)駒を動かす王手で詰むか判定
 龍、飛(1〜3段目/4〜9段目)、馬、角(1〜3段目/4〜9段目)、
 金・と・成香・成桂・成銀、銀(1〜3段目/4段目/5〜9段目)、桂、香、歩
  ※段で分かれているのは、成れる場合と成れない場合で処理を分けているため

留意点としては、
・移動先は相手王の周囲8マスに限定されている(離れた場所に打つ手は見ていない)
・隣接の王手に限定(開き王手は見ない)
というところだと思います。処理の簡略化のためと思われます。

(1)の基本的な流れは、飛車の場合でいうと
・駒を打てる位置(空きマス)がONになったbitboard(bb_drop)を生成
・相手王の周囲8マスのうち、王手をかけられる上下左右4マスがONになったbitboard(bb)を生成し、bb_dropとのANDをとる
・bbのONになっている各マスについて;
 ・自分の利きがあるかチェック。なければ詰まない
 ・そのマスに自分が飛車を打った後、相手の王が逃げられるかチェック
 ・そのマスに自分が飛車を打った後、相手が王以外の駒でそれを取れるかチェック
 ・上記のチェックが全てOKなら詰み、一つでもNGなら次のマスをチェック

他の駒種類の場合は、持ち駒の組み合わせを考慮したりで少し複雑ですが、大体は同じ流れです。

(2)の基本的な流れは、龍の場合でいうと
・駒を動かせる位置(自分の駒がないマス)がONになったbitboard(bb_move)を生成
・盤上に存在する自分の龍について;
 ・今いる位置からの先手龍の利きを保有するbitboard(bb_attacks)を生成 
 ・bb_moveとbb_attacksと後手王の周囲8マスのANDを取り、龍の移動先を保有するbitboard(bb_check)を生成する
 ・bb_checkのONになっている各マスについて;
  ・自分の利きがあるかチェック。なければ詰まない
  ・相手の王が逃げられるかチェック
  ・開き王手でないなら(後述)、相手が王以外の駒でそれを取れるかチェック
  ・龍を動かすことで、逆王手になっていないかチェック
  ・上記のチェックが全てOKなら詰み、一つでもNGなら次のマスをチェック

王手駒を取るのに「王以外の駒で」と限定している理由はよく分かりません。*1

「開き王手でないなら」というのは、たとえばこんな場合です。

                              • +
・ ・v金 ・v王| ・ 飛 ・ ・ ・| ・ ・ ・v銀 金| ・ ・ ・ ・ 香| ・ ・ ・ ・ ・|

ここで▲22金とすると、同時に14の香の利きも通るようになるので、これは隣接の王手でもあり、開き王手でもあります。
▲12金や▲22飛成は単に隣接の王手です。
なぜこの条件があるのか、しばらく悩みましたが…
隣接の王手でかつ開き王手でもあるなら、この2つを同時に受けるためには、王が動いて、隣接王手をかけている駒を取るしかありません。
ところが、その王手駒には攻め方の利きがある(なければその前のチェックでNG)ので、取ることができず、結局詰みになります。
従って開き王手の場合は、王以外の駒で取れるかをチェックする意味がない、ということのようです。

他の駒種類の場合は、成不成や動けない位置の考慮がありますが、大体同様の処理です。

保木さんが"cryptic"と書かれているのは、各種判定用のbitboardを作る部分ではないでしょうか。
たとえば前の例で、相手王の上下左右4マスがONになったbitboardを作る箇所がありますが、プログラムでは
「先手の金の利きを持ったbitboardと、後手の金の利きを持ったbitboardのANDを取る」
といったやり方をしていたりします。
分かってみればなるほど、ですが…。

*1:静止探索の中で、王が動く手を読んでいることと関係しているのでしょうか。