Bonanzaの指し手生成ルーチン完全解読(4)

■ Bonanzaの指し手生成ルーチン完全解読(4)


今回は指し手生成で使う利き関係のマクロや配列に関してその説明を書いておく。
なお、コメントは私が追加した。また無名列挙体の場合、私が名前を追加している。


■ abb_X_XXX_attacks


BoardPosにある駒の利きを表現するbitboard。これとbitboardの演算子だけわかっていれば指し手生成のソースの半分ぐらいは解読できる。

// abb_X_XXX_attacks[ifrom]は、ifromに先手駒があるときに利きのある場所を表現するbitmap
// ini_attack_tablesで初期化される。
extern bitboard abb_b_knight_attacks[ nsquare ]; // 先手の桂
extern bitboard abb_b_silver_attacks[ nsquare ]; // 先手の銀
extern bitboard abb_b_gold_attacks[ nsquare ];   // 先手の金
extern bitboard abb_w_knight_attacks[ nsquare ]; // 後手の桂
extern bitboard abb_w_silver_attacks[ nsquare ]; // 後手の銀
extern bitboard abb_w_gold_attacks[ nsquare ];   // 後手の金


ただ、指し手生成のソースを読む上で難しいのは、角、飛車の利きをどうやって一発で求めるのかだとか、pinされている駒かどうかの判定をどうするのかなどだと思うので、そのへんを中心に見ていく。


■ Direction

// 方向を意味するbit
enum Direction
{
  direc_misc           = b0000,			// 方向が縦横斜めの関係にない場合
  direc_file           = b0010, /* | */		// 方向が縦方向の関係である場合
  direc_rank           = b0011, /* - */		// 方向が横方向の関係にある場合
  direc_diag1          = b0100, /* / */		// 方向が右上から左下方向の斜め関係にある場合
  direc_diag2          = b0101, /* \ */		// 方向が左上から右下方向の斜め関係にある場合
  flag_cross           = b0010,			// 縦と横ならば持っているbitmask
  flag_diag            = b0100			// 斜め方向ならば持っているbitmask
};

// ifromとitoとの関係を示す。
extern Direction adirec[ nsquare ][ nsquare ];

BoardPosを2つ与えて、それが上下、左右、斜めの関係にあるかどうかを判定するためのテーブル。pinされているかの判定などに用いる。


■ IsDiscoverXK


空き王手になるかどうかの判定用マクロ。前述のDirection配列を用いてうまく処理を省略してある。

// 先手のfromに存在している駒をtoの地点に移動させたときに、その駒がpinされていて
// 先手の王を取られてしまうのか。
#define IsDiscoverBK(from,to)                                  \
          idirec = adirec[SQ_BKING][from],               \
          ( idirec!=direc_misc && ( idirec!=adirec[SQ_BKING][to] )   \
            && is_pinned_on_black_king( ptree, from, idirec ) )
		// 1) 先手玉とfromへの地点が縦横斜めの関係になければidrec == direc_miscで、
		// その場合はその駒がpinされていることはあり得ないので
		// IsDiscoverBKはfalse。
		// 2) また、fromの駒をpinされている方向に移動させる場合も
		// これが空き王手になることはあり得ないのでfalse。
		// 3) そのいずれにも属さない場合は
		// きちんとidirec方向(の延長上)にfromにある駒がpinされているのかどうかを
		// テストする必要がある。

// 後手版
#define IsDiscoverWK(from,to)                                  \
          idirec = adirec[SQ_WKING][from],               \
          ( idirec!=direc_misc && ( idirec!=adirec[SQ_WKING][to] )   \
            && is_pinned_on_white_king( ptree, from, idirec ) )

■ is_pinned_on_XXXXX_king

// isquareにある先手の駒が、idirec方向にpinされているのか。
// この駒を動かすと先手玉を取られてしまうときtrue。
bool is_pinned_on_black_king( const Tree * restrict ptree, BoardPos isquare,
                        Direction idirec );

bool is_pinned_on_white_king( const Tree * restrict ptree, BoardPos isquare,
                        Direction idirec );

■ abb_minus_rays , abb_plus_rays

// abb_plus_raysは、BoardPosの位置から、右、左下、下、右下方向が1であるbitboardを合成(bitwise or)したもの
extern bitboard abb_plus_rays[ nsquare ];

// abb_plus_raysは、BoardPosの位置から、左上、上、右上、左方向が1であるbitboardを合成(bitwise or)したもの
extern bitboard abb_minus_rays[ nsquare ];


香の利きを生成するのによく利用されている。fromの縦方向が1であるようなbitboardを持ってきて、それと abb_minus_rays[from] とを bitwise andすれば、先手の香の利きになる。つまり次のマクロである。ただし、このマクロは実際にはBonanzaのソース全体を通してsearch.cの1箇所でのみ使われているだけである。たぶん、保木さんがかなり後になってから作ったマクロではないかと思う。

// 香の利き
#define AttackBLance(bb,i)  bb = abb_minus_rays[i] & AttackFile(i)
#define AttackWLance(bb,i)  bb = abb_plus_rays[i] &  AttackFile(i)