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

Zobrist ハッシュ

前提知識: 差分更新と状態管理

Position は盤上配置、持ち駒、手番を ZobristKey にまとめる。 キー幅は既定で 64 bit、hash-128 feature の有効時は 128 bit になる。 キーは置換表、局面同一性の高速な絞り込み、反復判定の補助に使える。

XOR で更新できる理由

Zobrist hashing は「駒、升、色」などの状態要素へ決まった値を割り当て、それらを XOR する。 同じ値を二度 XOR すると元に戻るため、駒を消す操作と加える操作は同じ XOR で表せる。 通常手では移動元の駒を外し、移動先の捕獲駒を外し、移動後の駒を加え、手番を反転する。

持ち駒の増減も同じ原理で、変更前枚数の寄与を外して変更後枚数の寄与を加える。 このため全盤面を走査せずに apply/undo のキーを更新できる。

二種類のキー

key() は盤上配置、持ち駒、手番を含む局面キーを返す。 board_key() は盤上配置と手番だけを含み、持ち駒を含まない。 持ち駒を含む同一性が必要な用途では key() を使い、持ち駒を別に比較する用途では board_key()hand(color) を組み合わせる。

let full = position.key();
let board = position.board_key();
assert_ne!(full, board);

この例の不一致は一般に期待できるが、持ち駒がない局面では同じ値になりうる。

完全キーは概念的には次の XOR 合成である。

key = board pieces ^ side-to-move ^ hand-count contributions
board_key = board pieces ^ side-to-move

各持ち駒の寄与は駒種、色、枚数で決まる。 一枚ずつ同じ乱数を XOR するのではなく、現在枚数に対応する寄与を使うため、同種の複数枚を区別できる。

差分更新と先読み

通常の apply/undo はキーを state stack と同期して更新する。 局面を変更せずに更新後のキーを知りたいときは key_afterkey_after_movekey_after_null を使う。 盤面と手番だけの更新後キーが必要なときは board_key_after または board_key_after_move を使う。

let after = position.key_after(mv);
let mut next = position.clone();
next.apply_move32(mv);
assert_eq!(after, next.key());

先読み API は渡された手の合法性を検査しない。 非合法な手を渡した場合の値は局面キーとして利用しない。

apply/undo の差分更新は key_afterboard_key_after の結果と整合する。 キー処理を変更する場合は、SFEN からの再構築、全合法手の apply/undo、先読み API の一致を同時に検証する。

反復と衝突

Zobrist key は完全な局面証明ではなく、異なる局面が同じ値になる衝突の可能性を持つ。 反復の優劣判定のように持ち駒の比較が必要な箇所では、ライブラリは手の枚数も確認する。 外部の永続ストアでキーを同一性の唯一の根拠にするときは、必要な盤面情報や SFEN を併せて保持する。

既定の 64 bit key でも hash-128 の 128 bit key でも、衝突が起きたかどうかを 値の形式だけから検出することはできない。 テストではキーのランダム性よりも、apply/undo 後の一致、全再構築との一致、持ち駒変更の反映を確認する。

部分キー

partial_keys() は pawn、minor、non-pawn、material value のキャッシュを返す。 これらは Position の現在 state から読む検索・評価連携向けの値であり、独自の評価関数の代替ではない。 独自評価の状態は MoveApplyFactsMoveDelta32 を使ってエンジン側で管理する。

置換表に渡す手順

探索ノードでは key() を読み、エンジン側の置換表を probe する。 子ノードを probe したいときは key_after(mv) を求め、必要なら engine 固有の prefetch を発行してから apply する。 Position は key を返すが、置換表の容量、衝突解決、世代管理、置換方針には関与しない。

次に読む

探索エンジンとの統合 で、キーと差分を検索側へ渡す境界を確認する。