# ゾブリストハッシュ

ゾブリストハッシュは、局面を固定長の整数値へ高速に写像するハッシュ方式である。
[置換表](/shogi/shogiwiki/search/transposition-table/)や局面再訪判定で広く使われる。

## 概要

局面をハッシュ化する目的は、

- 同一局面を高速に識別する
- [置換表](/shogi/shogiwiki/search/transposition-table/)のキーにする
- [千日手](/shogi/shogiwiki/search/repetitions/)などの反復局面を検出する

ことである。

ゾブリストハッシュでは、局面の各特徴に乱数を割り当て、
それらを XOR して局面キーを作る。

将棋では少なくとも、

- 盤上の駒配置
- 手番
- 持ち駒

をハッシュに含める必要がある。

## 差分更新できるのが強み

XOR は同じ値をもう一度 XOR すると元に戻るので、
指し手ごとに全局面を作り直さなくてもよい。

たとえば駒が移動したら、

- 元の位置の駒キーを外す
- 先の位置の駒キーを足す
- 手番キーを反転する

といった差分更新で済む。

## 実装例

```cpp
uint64_t zobrist[PIECE_NB][SQ_NB];
uint64_t sideKey;

uint64_t computeHash(const Position& pos) {
    uint64_t key = 0;

    for (Square sq = SQ_11; sq <= SQ_99; ++sq) {
        Piece pc = pos.pieceOn(sq);
        if (pc != NO_PIECE) {
            key ^= zobrist[pc][sq];
        }
    }

    if (pos.sideToMove() == WHITE) {
        key ^= sideKey;
    }

    return key;
}
```

差分更新は次のようになる。

```cpp
key ^= zobrist[piece][from];
key ^= zobrist[piece][to];
key ^= sideKey;
```

将棋では持ち駒の増減や成りもハッシュ更新に含める必要がある。

## 衝突

ハッシュなので、異なる局面が同じキーになる衝突は理論上避けられない。
そのため実装では、

- 64 bit を使う
- 部分キーではなくフルキーも照合する
- 置換表で key を保存する

といった対策を取ることが多い。

## 将棋AIでの注意点

将棋ではチェスと違って持ち駒があるので、
その分ハッシュ設計が少し複雑になる。
また、

- 打つ手
- 成る手
- 千日手

もあるため、差分更新のバグが起きやすい。

## 関連項目

- [置換表](/shogi/shogiwiki/search/transposition-table/)
- [トランスポジション](/shogi/shogiwiki/search/transposition/)
- [千日手](/shogi/shogiwiki/search/repetitions/)

## 参考にしたホームページ

- [Chessprogramming Wiki: Zobrist Hashing](https://www.chessprogramming.org/Zobrist_Hashing)
- [Chessprogramming Wiki: Transposition Table](https://www.chessprogramming.org/Transposition_Table)