レイアウト
Square は筋を先に、段を後に並べた 0 始まりの番号を使う。
したがって Square::raw() = file * 9 + rank であり、同じ筋の 9 マスは連続する。
bit 0..8 : 1 筋の 1..9 段
bit 9..17 : 2 筋の 1..9 段
...
bit 72..80 : 9 筋の 1..9 段
この順序では file_mask は各 9 ビットの連続範囲になり、rank_mask は 9 ビット間隔の集合になる。
二つの u64
Bitboard は 81 ビットを一つの Rust の u128 フィールドには置かない。
内部配列の p[0] は bit 0..62 を、p[1] は bit 63..80 を保持する。
Bitboard::ALL はこの二つの有効範囲だけを 1 にし、コンストラクタも同じ範囲を強制する。
p[0] : bit 62 ........................................ bit 0
p[1] : bit 17 ........................................ bit 0
= 盤面の bit 80 ............................... bit 63
packed_bits は p[1] を 63 ビット左へずらして連続した盤面ビット列を復元する。
raw_bits は p[1] を 64 ビット左へずらす内部用の値であり、二ワードのレーン境界を保つ。
二つの表現は同じ盤面集合を扱うが、bit 63 以降の位置が異なるため、相互に混ぜて保存・比較してはならない。
境界と変換
from_packed_bits は入力の bit 81 以上を捨て、連続表現から p を構築する。
from_parts も各ワードをマスクするため、外部から渡す二ワード値に盤外ビットがあっても Bitboard には残らない。
反対に from_raw_bits_unmasked は crate 内の二ワード演算と飛び利き計算が使う内部変換である。
これは中間計算で盤外ビットを保持できるようにするためであり、公開データの構築手段ではない。
整列
型は 16 バイト整列される。
x86_64 で SSE2 が有効なら、論理演算は二ワードを 128 ビットのレジスタとして読み書きする。
整列は型の ABI とレイアウトを安定させるが、実装は unaligned load/store intrinsic を用いるため、呼び出し側が別途ポインタ整列を管理する契約ではない。
幾何学との対応
座標番号は筋ごとに連続するため、縦方向の利きは 9 ビットの連続範囲として取り出せる。
横方向と斜め方向は連続したビット列ではないが、事前生成した方向レイを使うことで同じ集合演算へ持ち込める。
同じ筋: bit 36, 37, 38, 39, 40, 41, 42, 43, 44
同じ段: bit 4, 13, 22, 31, 40, 49, 58, 67, 76
この違いが、香の筋方向で小さな FILE_ATTACKS 表を使い、角と飛の他方向でレイ抽出を使う理由である。
マス番号の増減だけで方角を判断せず、File と Rank の関係または生成済みテーブルを使う。
端のマスで番号の算術を続けると、隣の筋・段へ回り込む誤りを作りやすい。
表示と向き
Debug 表示は段と筋を反転した順で盤面を出力するため、ビット番号の昇順とは見た目の走査順が一致しない。
一方、BitIter は raw index の昇順、すなわち pop_lsb の順でマスを返す。
表示、棋譜座標、内部ビット順を混同しないことが、反転とテーブル生成の検証で特に重要になる。