探索木のvalidatorを書こう!! その2

Bonanzaで実装されている主要なvalidatorは次の3つと言えるでしょう。

// valid.c
int is_move_valid( tree_t * restrict __ptree__, unsigned int move, int turn );
int exam_tree( const tree_t * restrict ptree );
// debug.c
int exam_bb( const tree_t *ptree );


一つ目は指し手が合法かどうかを判定する関数です。これは、killer moveの指し手が現局面に適用できるか(合法かどうか)を判定するのにも使いますので、高速化が要求されます。


二つ目と三つ目の関数は、前者は探索木が正常かどうか、後者は盤面(bitboard)が正常かどうかを判定します。前者の途中で(assertのなかで)後者を呼び出していますので、前者は後者の処理を含みます。


注意しなくてはならないのは、exam_bbはそのままassertに突っ込んで使えるように( assert(exambb(bb)); のように書くの意味) 、正常終了時に1を返し、異常終了時に0を返す仕様となっているのですが、exam_treeのほうは、異常終了時に負、正常終了時に0以上を返す仕様となっています。後者はassertにはそのまま突っ込んで使えませんのでご注意ください。


また、前者はini_game(盤面の初期化。CSAファイル読み込み時などに呼び出される)のなかから呼び出されます。後者はassert中から呼び出されるので、NDEBUG(リリース時にdefineされるシンボル)がdefineされているとassertで書かれた部分は消失しますので、デバッグビルドでないと呼び出されることはありません。exam_treeからも

  assert( exam_bb( ptree ) );

のように呼び出されているので、デバッグビルド時はexam_tree内からもexam_bbは呼び出されません。


何故このような設計になっているのかは私にはよくわからないのですが、おそらく、
・指し手生成ルーチンなど、基本的な部分のバグを発見するためのきつめのチェック = exam_bb
・与えられた局面(CSAファイルの読み込みなどで)の正当性をチェックする = exam_tree
という使い分けが前提としてあるのではないかと思います。


そして、指し手生成ルーチンなどは十分に枯れているので、exam_bbはデバッグ時以外で呼び出す必要はないだろう(あるいはデバッグ時ですら呼び出す必要はないだろう)ということなのだと思います。


個人的にはassertマクロでvalidatorを呼び出すスタイルでプログラムを書くと、リリース時にvalidatorを有効にするのが面倒くさいので、assertマクロ自体を自作して、exam_bbを呼び出すのか、呼び出さないのかをコンパイルオプションで切り替えられるほうが好みなのですが。


つづく