Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ビットボード

ビットボードは 9 × 9 の盤面にあるマス集合を 81 個のビットで表す Bitboard である。

駒種ごとの配置、先後の占有、利き、ピンの候補を同じ集合演算として扱えるため、合法手生成と局面更新の共通の基盤になる。

Square::raw()0..81 がビット位置に対応し、SQ_11 は bit 0、SQ_99 は bit 80 である。

表現の二層

公開される Bitboard の実体は #[repr(C, align(16))][u64; 2] であり、サイズは常に 16 バイトである。

前半のワードには bit 0..62、後半のワードには bit 63..80 を詰め、残りのビットは盤外として扱う。

from_partsfrom_packed_bits、否定演算は盤外ビットをマスクするため、通常の API を通る値は 81 マスの範囲に保たれる。

packed_bits は連続した bit 0..80 の u128 表現を返す。

内部の raw_bits は後半ワードを bit 64 から置く演算用の表現であり、飛び利きの u128 演算以外に意味を持つ保存形式ではない。

この区別により、盤面座標と連続した 81 ビットの契約を保ったまま、二つの u64 レーンをそのまま演算できる。

使い分け

単一マスは Bitboard::from_square、筋・段・敵陣は file_maskrank_maskpromotion_zone で生成する。

複数集合の結合、交差、差分はそれぞれ |&and_not を使う。

集合を列挙するときは for sq in &bb または pop_lsb を使い、空集合は EMPTYis_empty で表す。

ビットボードは駒そのものや手番を含まないため、どの集合を表すかは利用側の変数名と Position の不変条件で決まる。

なぜ集合として持つのか

盤面配列は「あるマスに何の駒があるか」を一点ずつ答える表現である。

対してビットボードは「条件を満たす全マス」を一度に答える表現である。

例えば自駒、敵駒、空きマスを集合で持てば、移動先候補は駒の利きと空きマスまたは敵駒の和集合との AND で得られる。

王手判定、遮蔽、ピン、回避手の候補も、まずマス集合を絞ることで局所的な走査に落とせる。

この利点は 81 マスを毎回全走査しないことだけではない。

複数の条件を一つの式へ合成できるため、候補の意味と除外条件を近くに保てることが重要である。

敵玉への利き  ∩  自駒以外の移動先  =  王手候補の移動先
候補の利き    ∩  敵玉の周辺        =  玉を攻撃する候補

配列との分担

Position は各マスの駒を調べるための盤面配列も保持する。

ビットボードだけでは、立っているビットが歩なのか銀なのかを判別できないからである。

局面更新では盤面配列で駒を読み、駒種・色・占有のビットボードを差分更新する。

手生成では駒種別ビットボードから候補の始点を取り、盤面配列で必要な駒情報を補う。

この二重表現は同じ事実を二回持つため、更新経路が両方を同期させることが不変条件になる。

読み方の例

次の式は、ある駒種の移動先から自駒のあるマスを除く。

let destinations = attacks.and_not(position.bitboards().by_color(us));

destinations は合法手そのものではない。

成り、二歩、王手放置、打ち駒などの規則は、後続の手生成と合法性判定が加える。

ビットボードの式は候補集合を高速に作る層であり、将棋規則のすべてを単独で表す層ではない。

この章