# モンテカルロ木探索 設計仕様書
アルファ碁解体新書より
## コメント
- Wは勝ち数
- Nはこの枝の総プレイアウト数
- 兄弟ノードや横
- 親ノードは上
- 子ノードは下
## メモ
- ゲーム木はどこか比較的にグローバルでアクセスできる、とりあえずインスタンス変数にしておく
- せっかく探索したゲーム木は勝率情報の10分の1ぐらい次に使いまわせそうだから出来たらやりたいが、本質的には10分の1なので効かなさそう。
$$
UCB1 = (w/n) + (2 (\log t)/n)^{1/2}
$$
- n: 総プレイアウト
- w: 勝ち数
- t: 兄弟ノード全体のプレイアウト回数
## フローチャート
```flow
st=>start: root node
e=>end: simulation end
ifleaf=>condition: sはリーフノードであるか?
select=>subroutine: 選択:勝率+バイアス(UCB1)が一番大きい手を選択
next_move=>operation: s<=move(s,a)
ifnthr=>condition: 試行回数>=Nthr
expand=>subroutine: 展開:子ノードを追加してノード情報を初期化
evaluate=>subroutine: 評価:プレイアウトを実行し、勝敗をzに格納(勝ち1,負け0)
ifroot=>condition: sがルートノードか?
update=>subroutine: 更新:N<=N+1,W<=W+z
back_move=>operation: s<=unmove(s,a)
st->ifleaf
select->next_move->ifleaf
evaluate->ifroot
update->back_move->ifroot
expand->ifleaf
ifleaf(yes)->ifnthr
ifleaf(no)->select
ifnthr(yes)->expand
ifnthr(no)->evaluate
ifroot(yes)->e
ifroot(no)->update
```
## 各処理について
分かりやすさのために、この単位で関数化する。
1. 選択
勝率+バイアス(UCB1)が一番大きい手を選択。
これは、リーフノードまで繰り返せばよし、ナイーブにやるとO(N)、PQを使用するとO(NlogN)。丸ごと関数化して、リーフノードのポインタ(位置情報でよし実際にポインタである必要はなし)を返す。
相手の手の場合は相手基準のUCB1を返すようにする。
2. 展開
Nthr以上に読み込まれたら展開する。展開時は勝率情報などは全て0にする。UCB1の式の計算時に工夫をして無限でエラーを吐かないようにするかつ、最優先でキューが割り当てられるようにする。(選択)
3. 評価
なるべく早くプレイアウトを終わらせる工夫が必要。とりあえずは既存手法を用いてよい。
4. 更新
きちんと子ノードから親ノードを更新させるのを忘れないようにする。
5. 終了
最終的にUCB1が最も高い値を手として打つ。
## 実際の設計
0. クラスについて
MonteCarloPlayerを大本のクラスとする。
ゲーム木の情報を保存するものとしてMonteCarloTreeを使用する。
ノードは、MonteCarloNode
エッジは、特に情報を持たせるつもりも現状ないため、ノードからノードの配列番号参照による。
インスタンス変数 MonteCarloNode
- TurnPlayer: 相手自分情報、先攻後攻
- parent: 親ノードの番号
- children: 子ノードの番号
- プレイアウト数
- 勝ち数(引き分けは負け)
-
インスタンスメソッドとして以下の機能を実装。 MonteCarloNode
- init: 開始時に相手の駒を近い順から青に決め打つ。相互に駒の種類は見えないようにする。
- search: 大本。
- select: 展開されているノードをUCB1でたどっていって、、、
- expand: Nthrは随時いい感じにする。デバッグのため結果のグラフを表示する機能が欲しい。
- evaluate: 既存手法の移植
- update: 子ノードの数、親ノードの数はN+1、W+1で何とかなるが。兄弟ノードのプレイアウト数という情報が必要のため、既に通ってきた子ノード以外のノードに加算する。
とりあえずはこれで実装。
2. 選択
3. 展開
4. 評価
この本では、値域が[0, 1]だが、引き分けが発生するガイスターでは、値域が[1,-1]となるため、UCB1の更新式が異なる。
間違えやすいので注意
$$
UCB1 = (w/2n) + 0.5 + (2 (\log t)/n)^{1/2}
$$
5. 更新
## 知見
- なるべく早くプレイアウトを迎えた方が良い。
- 無限ループするこのゲームでは、早く終局を迎えるために何らかの工夫をしなければならない。
- 200ターンで引き分け終了はありがたい。=>引き分けは0で処理。
- よって悲観的な戦略はあまり適さない=>取るときは赤、ゴールは青など
- ランダムプレイアウトの際に前に進む確率を上げてみた=>目に見えた変化はないが上下運動のループは減った。
- 紫駒戦略は悲観的になりすぎる。=>どっちにしろ負けるからゴール前の青ゴマを取らなくなる。=>とればワンチャンあってもなくなる。
- 紫駒戦略と猪突AIは相性が悪い。
- かといってゴールに近い駒を青駒と決め打ちすると原始モンテカルロ(赤駒を取らせる戦略を好む)に勝てなくなる。
- よって駒色の予測が非常に重要。
## 便利情報
- 重み付きグラフの表示ができるツールがあると非常にデバッグ時に便利。