goboard.py の中の GameState のところのデータがどうなっているかが、ゾブリストハッシュの
理解のポイントだと思う。
そこで、GameStateクラスに処理がわたったとき、プロパティがどうなるかを追いかけてみた。
コンストラクタの各プロパティに値がセットされた時を調べてみた。
"goboard.py"
245 if self.previous_state is None:
246 self.previous_states = frozenset()
247 else:
248 self.previous_states = frozenset( # 以下の3行の式の値を調べる
249 previous.previous_states | # <1>
250 {( previous.next_player, previous.board.zobrist_hash())})
# <1> | -- 論理和 -- 0と0は0、1と0は1、0と1は1、1と1は1。
previous_states に格納されるデータを調べるために、以下を挿入した。
251 if previous is not None:
252 print("previous.previous_states:")
253 explain(previous.previous_states)
254 print("previous.next_player")
255 explain(previous.next_player)
256 print("previous.board.zobrist_hash()")
257 explain(previous.board.zobrist_hash())
1) まず、黒が第1手をC3にうったとき、previousの値(盤面状態)は以下のようになる。
previous.previous_states -- frozenset() # 空の集合
previous.next_player -- Player.black
previous.board.zobrist_hash() -- 0
ここから、以下の式はどうなるか?
248 self.previous_states = frozenset(
249 previous.previous_states |
250 {( previous.next_player, previous.board.zobrist_hash())})
このように、previous_satesは空集合とハッシュ値0の和集合になる。
self.previous_states = frozenset(
{()} | {( Player.black, 0 )})
2) 次に白が G5 と打った時、previous の値(盤面状態)は以下である。
previous.previous_states -- frozenset({(<Player.black: 1>, 0)})
previous.next_player -- Player.white
previous.board.zobrist_hash() -- 7685528180565542275
ここから、以下の式はどうなるか?
248 self.previous_states = frozenset(
249 previous.previous_states |
250 {( previous.next_player, previous.board.zobrist_hash())})
self.previous_states = frozenset(
{(<Player.black: 1>, 0)} | {( <Player.white>, 7685528180565542275 )})
= ({(<Player.black: 1>, 0), ( <Player.white>, 7685528180565542275 )})
つまり、{(, 0)} と {( , 7685528180565542275 )}の和集合である
3) 次に黒が D3 と打った時、previous の盤面状態は以下である。
previous.previous_states -- frozenset(
{(<Player.black: 1>, 0), (<Player.white: 2>, 7685528180565542275)})
previous.next_player -- Player.black
previous.board.zobrist_hash() -- 8139394106448984692
ここから、以下の式はどうなるか?
self.previous_states = frozenset(
previous.previous_states |
{( previous.next_player, previous.board.zobrist_hash())}) # <1>
self.previous_states = frozenset(
{(<Player.black: 1>, 0), (<Player.white: 2>, 7685528180565542275)} |
{( <Player.black>, 8139394106448984692 )})
= {(<Player.black: 1>, 0),
(<Player.white>, 7685528180565542275),
(<Player.black>, 8139394106448984692)}
4) 次に白が J9 と打った時、previous の盤面状態は以下である。
previous.previous_states -- frozenset(
{(<Player.black: 1>, 8139394106448984692),
(<Player.black: 1>, 0),
(<Player.white: 2>, 7685528180565542275)})
previous.next_player -- Player.white
previous.board.zobrist_hash() -- 8684821617119420740
ここから、以下の式はどうなるか?
248 self.previous_states = frozenset(
249 previous.previous_states |
250 {( previous.next_player, previous.board.zobrist_hash())})
self.previous_states = frozenset(
{(<Player.black: 1>, 8139394106448984692),
(<Player.black: 1>, 0),
(<Player.white: 2>, 7685528180565542275)}) |
{( <Player.white>, 8684821617119420740 )})
= {(<Player.black: 1>, 0),
(<Player.white>, 7685528180565542275),
(<Player.black>, 8139394106448984692),
(<Player.white>, 8684821617119420740)}
結論
GameStateでは、盤の情報を (Player, hash) というタプルでまとめ、差し手が進むたびに そのタプルを集合として追加している。
コウの判定では、現在の盤面状況を表わすタプルと、過去の指し手の盤面情報のタプルと 比べ、同じであればコウと判定している。