Pythonで〇×ゲームのAIを一から作成する　その160　αβ 法の視覚化のための Mbtree_Anim の修正 その 1

# 目次と前回の記事

https://qiita.com/ysgeso/items/2381dd4e3283cbed49a0

https://qiita.com/ysgeso/items/42c2140766dd59906a02

## これまでに作成したモジュール

以下のリンクから、これまでに作成したモジュールを見ることができます。

| リンク | 説明 |
|:--|:--|
| [marubatsu.py](https://github.com/ysgeso/marubatsu/blob/master/160/marubatsu.py)  | Marubatsu、Marubatsu_GUI クラスの定義|
| [ai.py](https://github.com/ysgeso/marubatsu/blob/master/160/ai.py)  |  AI に関する関数 |
| [test.py](https://github.com/ysgeso/marubatsu/blob/master/160/test.py)  |  テストに関する関数 |
|  [util.py](https://github.com/ysgeso/marubatsu/blob/master/160/util.py) |  ユーティリティ関数の定義。現在は `gui_play` のみ定義されている |
| [tree.py](https://github.com/ysgeso/marubatsu/blob/master/160/tree.py)  | ゲーム木に関する Node、Mbtree クラスの定義  |
| [gui.py](https://github.com/ysgeso/marubatsu/blob/master/160/gui.py)  |  GUI に関する処理を行う基底クラスとなる GUI クラスの定義  |

AI の一覧とこれまでに作成したデータファイルについては、下記の記事を参照して下さい。

https://qiita.com/ysgeso/items/10d1d01192c014173e4b

# αβ 法の視覚化のための Mbtree_Anim の修正

前回の記事では __`calc_score_for_anim`__ で行った αβ 法の計算手順の視覚化の検討と、視覚化に必要なデータの記録の実装を行いました。今回の記事では __Mbtree_Anim を修正して αβ 法の視覚化__ を行います。

なお、__実際の実装の作業__ では、表示内容、表示位置、表示色などについて __様々な試行錯誤を行いました__ が、その試行錯誤の過程をすべて説明すると記事が長くなりすぎるので __最終的な試行錯誤の結果の実装__ について説明することにします。

また、今回の記事の実装は視覚化の方法の一つにすぎません。もっと良い視覚化の方法を思いついた方はその実装にぜひチャレンジしてみて下さい。

## 視覚化の検討のおさらい

[前回の記事](https://qiita.com/ysgeso/items/42c2140766dd59906a02#%E8%A1%A8%E7%A4%BA%E5%86%85%E5%AE%B9%E3%81%A8%E8%A1%A8%E7%A4%BA%E3%81%AB%E5%BF%85%E8%A6%81%E3%81%AA%E6%83%85%E5%A0%B1%E3%81%AE%E6%A4%9C%E8%A8%8E)で行った __αβ 法の視覚化の方法__ と、__フレームに記録するデータ__ の検討結果は以下の通りです。

### 常に表示する内容

常に表示する内容と、表示するために必要な情報は以下の表の通りです。[以前の記事](https://qiita.com/ysgeso/items/ddcbd0cb9ab62656173e#%E5%B8%B8%E3%81%AB%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B%E5%86%85%E5%AE%B9)で検討したミニマックス法での表示と共通する内容は省略しました。

|表示内容|必要な情報|
|:--|:--|
|__α 値と β 値の初期値__<br>__範囲の色分け__<br>__範囲の説明__|  α 値と β 値の初期値 |

### "start" 以外の状態で表示する内容

__"start" 以外__ の状態で表示する内容と、表示するために必要な情報は以下の通りです。

|表示内容|必要な情報|
|:--|:--|
|__置換表の範囲__| 置換表の範囲の下界と上界|
|__置換表の登録の区別__| 置換表に登録されているかどうか|

### フレームの状態ごとに表示する内容

それぞれのフレームの状態で表示する内容は以下の通りです。上記の "start" 以外の状態で表示する内容は省略しました。なお、ミニマックス法と共通する内容には先頭に（※）を記述しました。

* __"start"__ の状態
なし
* __"tt"__ の状態
    * 置換表にノードの評価値が登録されている場合
        * （※）赤字で「置換表に登録済」を表示
        * ミニマックス法で表示した置換表に登録されていた評価値は __表示しない__
        * 置換表による枝狩りが行われている場合
            * $l=u$ の場合は赤字で「置換表による枝狩り（exact value）」を表示
            * $u≦α$ の場合は赤字で「置換表による枝狩り（fail low）」を表示
            * $β≦l$ の場合は赤字で「置換表による枝狩り（fail high）」を表示
        * 置換表による枝狩りが行われていない場合
            * α 値と β 値の初期値が更新される場合は赤字で「α 値または β 値の初期値の更新」を表示
    * 登録されていない場合
        * （※）黒字で「置換表に未登録」を表示
* __"score"__ の状態
    * （※）数直線上にそのフレームでのノードの評価値を表示
    * （※）数直線上に子ノードの評価値を表示
    * そのフレームでの α 値と β 値
* __"update"__ の状態
    * （※）数直線上にそのフレームでのノードの評価値を表示
    * ノードの評価値が更新された場合
        * （※）赤字で「評価値の更新」を表示
    * 更新されていない場合  
        * （※）黒字で「評価値の更新なし」を表示
    * そのフレームでの α 値と β 値
    * α 狩りが行われた場合は赤字で「α 狩り」と表示
    * β 狩りが行われた場合は赤字で「β 狩り」と表示
* __"end"__ の状態
    * （※）数直線上にそのフレームでのノードの評価値を表示
    * このノードの評価値の種類によってフレームの状態を以下のように表示する
        * fail low の場合は「評価値の確定（fail low）」を表示
        * exact value の場合は「評価値の確定（exact value）」を表示
        * fail high の場合は「評価値の確定（fail high）」を表示
    * 置換表を利用する場合
        * 置換表による枝狩りが行われた場合
            * （※）黒字で「置換表に登録されていたデータを利用」を表示
        * 置換表による枝狩りが行われていない場合
            * （※）赤字で「置換表への登録」を表示
            * 計算された範囲
            * 登録する範囲

### フレームに記録するデータ

`calc_score_for_anim` が記録する [フレームのデータを表す dict の内容](https://qiita.com/ysgeso/items/42c2140766dd59906a02#%E5%BF%85%E8%A6%81%E3%81%AA%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E8%A8%98%E9%8C%B2%E3%81%99%E3%82%8B-dict-%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E6%A7%8B%E9%80%A0) は以下の通りです。

|キー|キーの値の意味|
|:--|:--|
|__"alphaorig"__|α 値の初期値|
|__"betaorig"__|β 値の初期値|
|__"registered_in_tt"__|（※）置換表にノードの評価値が登録されていたかどうか|
|__"lower_bound"__|（※）置換表の範囲の下界|
|__"upper_bound"__|（※）置換表の範囲の上界|
|__"tt_pruned"__|置換表による枝狩りが行われたかどうか |
|__"ab_updated"__|α 値または β 値の初期値が更新されたかどうか|
|__"ab_pruned"__|α 狩りまたは β 狩りが行われたかどうか |
|__"score_type"__|ノードの評価値の種類 |
|__"clower_bound"__|計算された範囲の下界|
|__"cupper_bound"__|計算された範囲の上界|
|__"tlower_bound"__|登録する範囲の下界|
|__"tupper_bound"__|登録する範囲の上界|

## 数直線の表示の改良

以下のような __数直線の表示の改良__ を思いつきましたので、実装することにします。

__これまでは__ 数直線の両端に必ず負の無限大と正の無限大を表す __-∞ と ∞ を表示していました__ が、__ルートノードの α 値と β 値__ を評価値の最小値と最大値で __初期化する場合は__ 負の無限大と正の無限大は計算の際に利用しないので __それらを表示しないようにする__ ことにします。

# 常に表示する内容の表示の実装

実装する内容が多いので、今回の記事では __常に表示する内容の表示を実装__ することにします。どのように Mbtree_Anim クラスを修正すれば良いかについて少し考えてみて下さい。

## Figure の大きさの変更

上記で __検討した内容を表示するため__ にはフレームの情報を表示する __Figure の大きさが足りない__ ので、下記のプログラムのように __`calc_score_for_anim` で αβ 法__ で計算が行なわれていた場合は __Figure の大きさを大きくする__ ように __`create_widgets`__ を修正することにします。なお、__Figure の大きさは試行錯誤の結果決めたもの__ です。

* __15、16 行目__：`calc_score_for_anim` で作成されたデータで、αβ 法で計算されている場合のみ作成する Figure の大きさを (7, 2) とするように修正する

```python
 1  from tree import Mbtree_Anim
 2  import ipywidgets as widgets
 3  import matplotlib.pyplot as plt
 4  
 5  def create_widgets(self):
元と同じなので省略
 6      with plt.ioff():
 7          self.fig = plt.figure(figsize=[self.width * self.size,
 8                                          self.height * self.size])
 9          self.ax = self.fig.add_axes([0, 0, 1, 1])
10          self.fig.canvas.toolbar_visible = False
11          self.fig.canvas.header_visible = False
12          self.fig.canvas.footer_visible = False
13          self.fig.canvas.resizable = False 
14          if self.isscore and hasattr(self.mbtree, "ablist_by_score"):
15              if self.mbtree.calculated_by_calc_score_for_anim and not self.mbtree.minimax:
16                  self.abfig = plt.figure(figsize=(7, 2))
17              else:
18                  self.abfig = plt.figure(figsize=(7, 1))
元と同じなので省略
19  
20  Mbtree_Anim.create_widgets = create_widgets
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```python
from tree import Mbtree_Anim
import ipywidgets as widgets
import matplotlib.pyplot as plt

def create_widgets(self):
    self.play = widgets.Play(max=self.nodenum - 1, interval=500)
    self.prev_button = self.create_button("<", width=30)
    self.next_button = self.create_button(">", width=30)
    self.frame_slider = widgets.IntSlider(max=self.nodenum - 1, description="frame")
    self.interval_slider = widgets.IntSlider(value=500, min=1, max=2000, description="interval")
    widgets.jslink((self.play, "value"), (self.frame_slider, "value"))    
    widgets.jslink((self.play, "interval"), (self.interval_slider, "value"))

    with plt.ioff():
        self.fig = plt.figure(figsize=[self.width * self.size,
                                        self.height * self.size])
        self.ax = self.fig.add_axes([0, 0, 1, 1])
        self.fig.canvas.toolbar_visible = False
        self.fig.canvas.header_visible = False
        self.fig.canvas.footer_visible = False
        self.fig.canvas.resizable = False 
        if self.isscore and hasattr(self.mbtree, "ablist_by_score"):
            if self.mbtree.calculated_by_calc_score_for_anim and not self.mbtree.minimax:
                self.abfig = plt.figure(figsize=(7, 2))
            else:
                self.abfig = plt.figure(figsize=(7, 1))
            self.abax = self.abfig.add_axes([0, 0, 1, 1])
            self.abfig.canvas.toolbar_visible = False
            self.abfig.canvas.header_visible = False
            self.abfig.canvas.footer_visible = False
            self.abfig.canvas.resizable = False 
        else:
            self.abfig = None
            
    if self.abfig is not None:
        self.node_label = widgets.Label("選択中のノード内の移動")
        self.node_first_button = self.create_button("<<", width=40)
        self.node_prev_button = self.create_button("<", width=30)
        self.node_next_button = self.create_button(">", width=30)
        self.node_last_button = self.create_button(">>", width=40)

Mbtree_Anim.create_widgets = create_widgets
```
</details>

<details><summary><strong>修正箇所</strong></summary>

```diff_python
from tree import Mbtree_Anim
import ipywidgets as widgets
import matplotlib.pyplot as plt

def create_widgets(self):
元と同じなので省略
    with plt.ioff():
        self.fig = plt.figure(figsize=[self.width * self.size,
                                        self.height * self.size])
        self.ax = self.fig.add_axes([0, 0, 1, 1])
        self.fig.canvas.toolbar_visible = False
        self.fig.canvas.header_visible = False
        self.fig.canvas.footer_visible = False
        self.fig.canvas.resizable = False 
        if self.isscore and hasattr(self.mbtree, "ablist_by_score"):
-           self.abfig = plt.figure(figsize=(7, 1))
+           if self.mbtree.calculated_by_calc_score_for_anim and not self.mbtree.minimax:
+               self.abfig = plt.figure(figsize=(7, 2))
+           else:
+               self.abfig = plt.figure(figsize=(7, 1))
元と同じなので省略

Mbtree_Anim.create_widgets = create_widgets
```
</details>

上記の修正後に下記のプログラムで __`calc_score_by_ab`__ で計算したデータをファイルから読み込んで __Mbtree_Anim で表示__ すると、実行結果のように __これまでと同じ大きさ__ でフレームの情報を表示する Figure が作成されることが確認できます。

```python
from tree import Mbtree

mbtree = Mbtree.load("../data/abtree_root")
Mbtree_Anim(mbtree, isscore=True)
```

実行結果（ゲーム木の部分の表示は省略します）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/e4942328-8625-411e-ad00-cae462081c77.png" width="500">

また、下記のプログラムで __`calc_score_for_anim`__ で __ミニマックス法__ で計算したデータを __Mbtree_Anim で表示__ すると、実行結果のように __これまでと同じ大きさ__ でフレームの情報を表示する Figure が作成されることが確認できます。

```python
mbtree.calc_score_for_anim(mbtree.root, minimax=True)
Mbtree_Anim(mbtree, isscore=True)
```

実行結果

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/6ec7056a-cdd4-421d-93ec-b1b0a1b35fda.png" width="500">

下記のプログラムで __`calc_score_for_anim`__ で __αβ 法__ で計算したデータを __Mbtree_Anim で表示__ すると、実行結果のように __Figure の縦幅が大きな Figure が作成される__ ことが確認できます。なお、__表示する内容の修正はまだ行なっていない__ ので、現時点では中に表示される内容は実行結果のように __縦に広がって表示されます__。

```python
mbtree.calc_score_for_anim(mbtree.root, minimax=False)
Mbtree_Anim(mbtree, isscore=True)
```

実行結果

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/5dc0b6d8-9caa-43d4-a97f-48886faa292d.png" width="500">

## Axes の表示範囲の修正と数直線の表示の修正

上記の実行結果で __表示内容が縦に広がって表示される__ ようになったのは、Figure の大きさを変えたにも関わらず、__Axes の表示範囲を変えていないから__ です。そのため、αβ 法で計算された場合は __`update_frameinfo`__ の中の __Axes の表示範囲を設定する処理__ を下記のプログラムのように修正する必要があります。また、__数直線の表示の修正__ は簡単に行えるので下記のプログラムでついでに __修正しました__。

* __11 行目__：ミニマックス法で計算されているかどうかの判定を何度も行う必要が生じるので、__`minimax`__ というローカル変数に __ミニマックス法で計算されているかどうか__ を表す __`self.mbtree.minimax` を代入__ して __プログラムを短く記述できる__ ようにした
* __17、18 行目__：αβ 法で計算されている場合の Axes の y 座標の表示範囲を広げるように修正した。なお、Figure の幅は変わらないので x 座標の表示範囲に関する処理を修正する必要はない
* __27 ~ 30 行目__：ルートノードの α 値と β 値の初期値を設定しない場合のみ、数直線の負の無限大と正の無限大に -∞ と ∞ を表示するように修正した

```python
 1  from marubatsu import Marubatsu
 2  import matplotlib.patches as patches
 3  
 4  def update_frameinfo(self):
 5      def calc_coord(score):
 6          return min(max(minus_inf, score), plus_inf)
 7          
 8      framedata = self.mbtree.ablist_by_score[self.play.value]
 9      status = framedata["status"]
10      maxnode = self.selectednode.mb.turn == Marubatsu.CIRCLE
11      minimax = self.mbtree.minimax
12  
13      self.abax.clear()
14      self.abax.set_xlim(-4, 23)
15      if minimax:
16          self.abax.set_ylim(-1.5, 1.5)
17      else:
18          self.abax.set_ylim(-4.3, 2.3)
19      self.abax.axis("off")
20  
21      minus_inf = -3 if self.mbtree.shortest_victory else -2
22      plus_inf = 4 if self.mbtree.shortest_victory else 2
23      
24      # 数直線の描画    
25      self.abax.plot(range(minus_inf, plus_inf + 1), [0] * (plus_inf + 1 - minus_inf) , "|-k")
26      for num in range(minus_inf, plus_inf + 1):
27          if num == minus_inf:
28              numtext = "" if self.mbtree.init_ab else "-∞"
29          elif num == plus_inf:
30              numtext = "" if self.mbtree.init_ab else "∞"
31          else:
32              numtext = num
33          self.abax.text(num, -1, numtext, ha="center") 
元と同じなので省略
34  
35  Mbtree_Anim.update_frameinfo = update_frameinfo
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```python
from marubatsu import Marubatsu
import matplotlib.patches as patches

def update_frameinfo(self):
    def calc_coord(score):
        return min(max(minus_inf, score), plus_inf)
        
    framedata = self.mbtree.ablist_by_score[self.play.value]
    status = framedata["status"]
    maxnode = self.selectednode.mb.turn == Marubatsu.CIRCLE
    minimax = self.mbtree.minimax
    
    self.abax.clear()
    self.abax.set_xlim(-4, 23)
    if minimax:
        self.abax.set_ylim(-1.5, 1.5)
    else:
        self.abax.set_ylim(-4.3, 2.3)
    self.abax.axis("off")

    minus_inf = -3 if self.mbtree.shortest_victory else -2
    plus_inf = 4 if self.mbtree.shortest_victory else 2

    # 数直線の描画    
    self.abax.plot(range(minus_inf, plus_inf + 1), [0] * (plus_inf + 1 - minus_inf) , "|-k")
    for num in range(minus_inf, plus_inf + 1):
        if num == minus_inf:
            numtext = "" if self.mbtree.init_ab else "-∞"
        elif num == plus_inf:
            numtext = "" if self.mbtree.init_ab else "∞"
        else:
            numtext = num
        self.abax.text(num, -1, numtext, ha="center")        

    # メッセージの表示
    linenum = 4
    textlist = [""] * linenum
    textcolorlist = ["black"] * linenum

    algorithm = "mm 法" if self.mbtree.minimax else "αβ法"
    use_tt = "〇" if self.mbtree.use_tt else "×"
    shortest_victory = "〇" if self.mbtree.shortest_victory else "×"
    init_ab = "〇" if self.mbtree.init_ab else "×"
    textlist[0] = f"{algorithm}　置換表 {use_tt}　最短 {shortest_victory}"
    if not self.mbtree.minimax:
        textlist[0] += f"　初期値 {init_ab}"
    
    textlist[1] = f"深さ {self.selectednode.mb.move_count} "
    if maxnode:
        textlist[1] += "max node"
    else:
        textlist[1] += "min node"
    
    statusdata = {
        "start": {
            "text": "処理の開始",
            "color": "white"
        },
        "tt": {
            "text": "置換表の処理",
            "color": "honeydew"
        },
        "score": {
            "text": "子ノードの評価値",
            "color": "lightyellow"
        },
        "update": {
            "text": "更新処理",
            "color": "lightcyan"
        },
        "end": {
            "text": "評価値の確定",
            "color": "lavenderblush"
        },
    }
    textlist[2] = statusdata[status]["text"]
    facecolor = statusdata[status]["color"]
    
    arrowprops = { "arrowstyle": "->"}
    leftx = -3
    rightx = 4
    centerx = (leftx + rightx) / 2
    # そのフレームでのノードの評価値の表示
    if status in ["score", "update", "end"]:
        score = framedata["score"]
        score_coord = calc_coord(score)
        text_coord = leftx if maxnode else rightx
        ha = "left" if maxnode else "right"
        self.abax.plot(score_coord, 0, "ok")
        self.abax.annotate(f"score = {score}", xy=(score_coord, 0),
                           xytext=(text_coord, 1), arrowprops=arrowprops, ha=ha)        
    # 子ノードの評価値の表示
    if status == "score":
        childscore = framedata["childscore"]
        childscore_coord = calc_coord(childscore)
        text_coord = rightx if maxnode else leftx
        ha = "right" if maxnode else "left"
        self.abax.plot(childscore_coord, 0, "og")
        self.abax.annotate(f"cscore = {childscore}", xy=(childscore_coord, 0),
                           xytext=(text_coord, 1), arrowprops=arrowprops, ha=ha)        
    # 置換表にデータが登録されていたかどうかの表示
    elif status =="tt":
        if framedata["registered_in_tt"]:
            textlist[3] = "置換表に登録済"
            textcolorlist[3] = "red"
            score = framedata["lower_bound"]
            score_coord = calc_coord(score)
            self.abax.plot(score_coord, 0, "om")
            self.abax.annotate(f"置換表の評価値 = {score}", xy=(score_coord, 0),
                            xytext=(centerx, 1), arrowprops=arrowprops, ha="center")        
        else:
            textlist[3] = "置換表に未登録"
    # ノードの評価値が更新されたかどうかの表示
    elif status == "update":
        if framedata["updated"]:
            textlist[3] = "評価値の更新"
            textcolorlist[3] = "red"
        else:
            textlist[3] = "評価値の更新なし"
    # 置換表に登録したかどうかの表示
    elif status == "end":
        if self.mbtree.use_tt:
            if framedata["registered_in_tt"]:
                textlist[3] = "置換表に登録されていたデータを利用"
            else:
                textlist[3] = "置換表への登録"
                textcolorlist[3] = "red"

    self.abfig.set_facecolor(facecolor)
    for i in range(linenum):
        self.abax.text(5, 1 - i * 0.7, textlist[i], c=textcolorlist[i])

    num_calculated = framedata["num_calculated"]
    num_pruned = framedata["num_pruned"]
    num_total = num_calculated + num_pruned
    num_ratio = num_calculated / num_total if num_total != 0 else 0
    prev_framedata = self.mbtree.ablist_by_score[self.prev_frame]
    prev_num_calculated = prev_framedata["num_calculated"]
    prev_num_pruned = prev_framedata["num_pruned"]
    prev_num_total = prev_num_calculated + prev_num_pruned
    diff_num_calculated = num_calculated - prev_num_calculated
    diff_num_pruned = num_pruned - prev_num_pruned
    diff_num_total = num_total - prev_num_total
    diff_num_ratio = diff_num_calculated / diff_num_total if diff_num_total != 0 else 0

    textlist = [ "計算済", "枝狩り", "合計", "割合" ]
    datalist = [ num_calculated, num_pruned, num_total, f"{num_ratio * 100:.1f}%"]
    diff_datalist = [ f"{diff_num_calculated:+d}", f"{diff_num_pruned:+d}", 
                    f"{diff_num_total:+d}", f"{diff_num_ratio * 100:.1f}%"]
    for i in range(4):
        self.abax.text(15, 1 - i * 0.7, textlist[i])
        self.abax.text(19.5, 1 - i * 0.7, datalist[i], ha="right")
        self.abax.text(22.5, 1 - i * 0.7, diff_datalist[i], ha="right")
        
Mbtree_Anim.update_frameinfo = update_frameinfo
```
</details>

<details><summary><strong>修正箇所</strong></summary>

```diff_python
from marubatsu import Marubatsu
import matplotlib.patches as patches

def update_frameinfo(self):
    def calc_coord(score):
        return min(max(minus_inf, score), plus_inf)
        
    framedata = self.mbtree.ablist_by_score[self.play.value]
    status = framedata["status"]
    maxnode = self.selectednode.mb.turn == Marubatsu.CIRCLE
+   minimax = self.mbtree.minimax

    self.abax.clear()
    self.abax.set_xlim(-4, 23)
-   self.abax.set_ylim(-1.5, 1.5)
+   if minimax:
+       self.abax.set_ylim(-1.5, 1.5)
+   else:
+       self.abax.set_ylim(-4.3, 2.3)
+   self.abax.axis("off")

    minus_inf = -3 if self.mbtree.shortest_victory else -2
    plus_inf = 4 if self.mbtree.shortest_victory else 2
    
    # 数直線の描画    
    self.abax.plot(range(minus_inf, plus_inf + 1), [0] * (plus_inf + 1 - minus_inf) , "|-k")
    for num in range(minus_inf, plus_inf + 1):
        if num == minus_inf:
-           numtext = "-∞"
+           numtext = "" if self.mbtree.init_ab else "-∞"
        elif num == plus_inf:
-           numtext = "∞"
+           numtext = "" if self.mbtree.init_ab else "∞"
        else:
            numtext = num
        self.abax.text(num, -1, numtext, ha="center") 
元と同じなので省略

Mbtree_Anim.update_frameinfo = update_frameinfo
```
</details>

`update_frameinfo` は `calc_score_for_anim` で作成されたデータを表示するプログラムなので、__上記の修正によって `calc_score_by_ab` で作成されたデータの表示は変わりません__。そのため以後の確認作業では特に理由がない限り `calc_score_for_anim` で計算したデータに対してのみ行うことにします。

上記の修正後に下記のプログラムで __ミニマックス法__ で __ルートノードの α 値と β 値を初期化しない__ 場合のデータを Mbtree_Anim で表示すると、実行結果のようにこれまでと同じように __フレームの情報が正しく表示される__ ことが確認できます。余裕がある方は他のフレームでの表示も確認してみて下さい。

```python
mbtree.calc_score_for_anim(mbtree.root, minimax=True)
Mbtree_Anim(mbtree, isscore=True)
```

実行結果

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/139c56a1-3bc1-4a7f-bf92-0109af843851.png" width="500">

下記のプログラムで __αβ 法__ で __ルートノードの α 値と β 値を初期化する__ 場合のデータを Mbtree_Anim で表示すると、実行結果のように __表示される内容が縦に伸びなくなった__ ことが確認できます。また、__数直線に ―∞ と ∞ が表示されなくなった__ ことも確認できます。

```python
mbtree.calc_score_for_anim(mbtree.root, minimax=False, init_ab=True)
Mbtree_Anim(mbtree, isscore=True)
```

実行結果

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/cf39de1f-2a84-456b-bb8c-889213bed215.png" width="500">

## 文字の表示位置の変更

上記の実行結果では、__真ん中と右に表示される内容__ が Figure の __上部から少し下の位置に表示__ されていますが、これは __Axes の表示範囲の y 座標の上部を 1.5 から 2.3 に変更したため__ です。そこで、__それらの表示を Figure の上部に移動__ することにします。

また、現状では真ん中の部分には最大で 4 行分のメッセージを表示しますが、試行錯誤の結果筆者が考えた __αβ 法__ の表示方法では __9 行分のメッセージを表示する必要がある__[^1] ので 9 行分のメッセージを表示できるように修正することにします。

下記はそのように `update_frameinfo` を修正したプログラムです。

* __3 ~ 8 行目__：ミニマックス法と αβ 法のそれぞれの場合について、真ん中のメッセージの行数を `linenum` に、一番上の行のメッセージの表示の y 座標を `linetop` に代入する
* __13 行目__：真ん中のメッセージの表示位置を計算する式の中で、一番上の行のメッセージの表示座標を表す数値を `linetop` に置き換えるように修正した
* __19 ~ 21 行目__：右のメッセージの表示に関しても同様の修正を行った

```python
 1  def update_frameinfo(self):
元と同じなので省略
 2      # メッセージの表示
 3      if minimax:
 4          linenum = 4
 5          linetop = 1
 6      else:
 7          linenum = 9
 8          linetop = 1.7
 9      textlist = [""] * linenum
10      textcolorlist = ["black"] * linenum
元と同じなので省略
11      self.abfig.set_facecolor(facecolor)
12      for i in range(linenum):
13          self.abax.text(5, linetop - i * 0.7, textlist[i], c=textcolorlist[i])
元と同じなので省略
14      textlist = [ "計算済", "枝狩り", "合計", "割合" ]
15      datalist = [ num_calculated, num_pruned, num_total, f"{num_ratio * 100:.1f}%"]
16      diff_datalist = [ f"{diff_num_calculated:+d}", f"{diff_num_pruned:+d}", 
17                      f"{diff_num_total:+d}", f"{diff_num_ratio * 100:.1f}%"]
18      for i in range(4):
19          self.abax.text(15, linetop - i * 0.7, textlist[i])
20          self.abax.text(19.5, linetop - i * 0.7, datalist[i], ha="right")
21          self.abax.text(22.5, linetop - i * 0.7, diff_datalist[i], ha="right")
22          
23  Mbtree_Anim.update_frameinfo = update_frameinfo
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```python
def update_frameinfo(self):
    def calc_coord(score):
        return min(max(minus_inf, score), plus_inf)
        
    framedata = self.mbtree.ablist_by_score[self.play.value]
    status = framedata["status"]
    maxnode = self.selectednode.mb.turn == Marubatsu.CIRCLE
    minimax = self.mbtree.minimax
    
    self.abax.clear()
    self.abax.set_xlim(-4, 23)
    if minimax:
        self.abax.set_ylim(-1.5, 1.5)
    else:
        self.abax.set_ylim(-4.3, 2.3)
    self.abax.axis("off")

    minus_inf = -3 if self.mbtree.shortest_victory else -2
    plus_inf = 4 if self.mbtree.shortest_victory else 2

    # 数直線の描画    
    self.abax.plot(range(minus_inf, plus_inf + 1), [0] * (plus_inf + 1 - minus_inf) , "|-k")
    for num in range(minus_inf, plus_inf + 1):
        if num == minus_inf:
            numtext = "" if self.mbtree.init_ab else "-∞"
        elif num == plus_inf:
            numtext = "" if self.mbtree.init_ab else "∞"
        else:
            numtext = num
        self.abax.text(num, -1, numtext, ha="center")        

    # メッセージの表示
    if minimax:
        linenum = 4
        linetop = 1
    else:
        linenum = 9
        linetop = 1.7
    textlist = [""] * linenum
    textcolorlist = ["black"] * linenum

    algorithm = "mm 法" if self.mbtree.minimax else "αβ法"
    use_tt = "〇" if self.mbtree.use_tt else "×"
    shortest_victory = "〇" if self.mbtree.shortest_victory else "×"
    init_ab = "〇" if self.mbtree.init_ab else "×"
    textlist[0] = f"{algorithm}　置換表 {use_tt}　最短 {shortest_victory}"
    if not self.mbtree.minimax:
        textlist[0] += f"　初期値 {init_ab}"
    
    textlist[1] = f"深さ {self.selectednode.mb.move_count} "
    if maxnode:
        textlist[1] += "max node"
    else:
        textlist[1] += "min node"
    
    statusdata = {
        "start": {
            "text": "処理の開始",
            "color": "white"
        },
        "tt": {
            "text": "置換表の処理",
            "color": "honeydew"
        },
        "score": {
            "text": "子ノードの評価値",
            "color": "lightyellow"
        },
        "update": {
            "text": "更新処理",
            "color": "lightcyan"
        },
        "end": {
            "text": "評価値の確定",
            "color": "lavenderblush"
        },
    }
    textlist[2] = statusdata[status]["text"]
    facecolor = statusdata[status]["color"]
    
    arrowprops = { "arrowstyle": "->"}
    leftx = -3
    rightx = 4
    centerx = (leftx + rightx) / 2
    # そのフレームでのノードの評価値の表示
    if status in ["score", "update", "end"]:
        score = framedata["score"]
        score_coord = calc_coord(score)
        text_coord = leftx if maxnode else rightx
        ha = "left" if maxnode else "right"
        self.abax.plot(score_coord, 0, "ok")
        self.abax.annotate(f"score = {score}", xy=(score_coord, 0),
                           xytext=(text_coord, 1), arrowprops=arrowprops, ha=ha)        
    # 子ノードの評価値の表示
    if status == "score":
        childscore = framedata["childscore"]
        childscore_coord = calc_coord(childscore)
        text_coord = rightx if maxnode else leftx
        ha = "right" if maxnode else "left"
        self.abax.plot(childscore_coord, 0, "og")
        self.abax.annotate(f"cscore = {childscore}", xy=(childscore_coord, 0),
                           xytext=(text_coord, 1), arrowprops=arrowprops, ha=ha)        
    # 置換表にデータが登録されていたかどうかの表示
    elif status =="tt":
        if framedata["registered_in_tt"]:
            textlist[3] = "置換表に登録済"
            textcolorlist[3] = "red"
            score = framedata["lower_bound"]
            score_coord = calc_coord(score)
            self.abax.plot(score_coord, 0, "om")
            self.abax.annotate(f"置換表の評価値 = {score}", xy=(score_coord, 0),
                            xytext=(centerx, 1), arrowprops=arrowprops, ha="center")        
        else:
            textlist[3] = "置換表に未登録"
    # ノードの評価値が更新されたかどうかの表示
    elif status == "update":
        if framedata["updated"]:
            textlist[3] = "評価値の更新"
            textcolorlist[3] = "red"
        else:
            textlist[3] = "評価値の更新なし"
    # 置換表に登録したかどうかの表示
    elif status == "end":
        if self.mbtree.use_tt:
            if framedata["registered_in_tt"]:
                textlist[3] = "置換表に登録されていたデータを利用"
            else:
                textlist[3] = "置換表への登録"
                textcolorlist[3] = "red"

    self.abfig.set_facecolor(facecolor)
    for i in range(linenum):
        self.abax.text(5, linetop - i * 0.7, textlist[i], c=textcolorlist[i])

    num_calculated = framedata["num_calculated"]
    num_pruned = framedata["num_pruned"]
    num_total = num_calculated + num_pruned
    num_ratio = num_calculated / num_total if num_total != 0 else 0
    prev_framedata = self.mbtree.ablist_by_score[self.prev_frame]
    prev_num_calculated = prev_framedata["num_calculated"]
    prev_num_pruned = prev_framedata["num_pruned"]
    prev_num_total = prev_num_calculated + prev_num_pruned
    diff_num_calculated = num_calculated - prev_num_calculated
    diff_num_pruned = num_pruned - prev_num_pruned
    diff_num_total = num_total - prev_num_total
    diff_num_ratio = diff_num_calculated / diff_num_total if diff_num_total != 0 else 0

    textlist = [ "計算済", "枝狩り", "合計", "割合" ]
    datalist = [ num_calculated, num_pruned, num_total, f"{num_ratio * 100:.1f}%"]
    diff_datalist = [ f"{diff_num_calculated:+d}", f"{diff_num_pruned:+d}", 
                    f"{diff_num_total:+d}", f"{diff_num_ratio * 100:.1f}%"]
    for i in range(4):
        self.abax.text(15, linetop - i * 0.7, textlist[i])
        self.abax.text(19.5, linetop - i * 0.7, datalist[i], ha="right")
        self.abax.text(22.5, linetop - i * 0.7, diff_datalist[i], ha="right")
        
Mbtree_Anim.update_frameinfo = update_frameinfo
```
</details>

<details><summary><strong>修正箇所</strong></summary>

```diff_python
def update_frameinfo(self):
元と同じなので省略
    # メッセージの表示
-   linenum = 4
+   if minimax:
+       linenum = 4
+       linetop = 1
+   else:
+       linenum = 9
+       linetop = 1.7
    textlist = [""] * linenum
    textcolorlist = ["black"] * linenum
元と同じなので省略
    self.abfig.set_facecolor(facecolor)
    for i in range(linenum):
-       self.abax.text(5, 1 - i * 0.7, textlist[i], c=textcolorlist[i])
+       self.abax.text(5, linetop - i * 0.7, textlist[i], c=textcolorlist[i])
元と同じなので省略
    textlist = [ "計算済", "枝狩り", "合計", "割合" ]
    datalist = [ num_calculated, num_pruned, num_total, f"{num_ratio * 100:.1f}%"]
    diff_datalist = [ f"{diff_num_calculated:+d}", f"{diff_num_pruned:+d}", 
                    f"{diff_num_total:+d}", f"{diff_num_ratio * 100:.1f}%"]
    for i in range(4):
-       self.abax.text(15, 1 - i * 0.7, textlist[i])
+       self.abax.text(15, linetop - i * 0.7, textlist[i])
-       self.abax.text(19.5, 1 - i * 0.7, datalist[i], ha="right")
+       self.abax.text(19.5, linetop - i * 0.7, datalist[i], ha="right")
-       self.abax.text(22.5, 1 - i * 0.7, diff_datalist[i], ha="right")
+       self.abax.text(22.5, linetop - i * 0.7, diff_datalist[i], ha="right")
        
Mbtree_Anim.update_frameinfo = update_frameinfo
```
</details>

実行結果はこれまでと同様なので省略しますが、上記の修正後に下記のプログラムで __ミニマックス法での表示が正しく行なわれる__ ことが確認できます。

```python
mbtree.calc_score_for_anim(mbtree.root, minimax=True)
Mbtree_Anim(mbtree, isscore=True)
```

下記のプログラムで αβ 法の場合で計算したデータに対して __真ん中と右の表示が Figure の上部から行なわれる__ ようになったことが確認できます。なお、ここまでで __最短の勝利を目指す場合の表示の確認を行なっていなかった__ ので、下記ではその確認も行なっており、__数直線の範囲が正しく表示される__ ことが確認できます。

```python
mbtree.calc_score_for_anim(mbtree.root, minimax=False, shortest_victory=True)
Mbtree_Anim(mbtree, isscore=True)
```

実行結果

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/0dfd3a1b-1a07-4d5c-804a-edb31a217436.png" width="500">

## α 値と β 値の初期値と範囲の色分けの表示

本記事では __α 値と β 値の初期値__ を以下のように表示することにしました。もっと良い表示を思いついた方は自由に変更して下さい。

* α 値を数直線上に赤い丸で表示し、その注釈を数直線の左上に表示する
* β 値を数直線上に青い丸で表示し、その注釈を数直線の右上に表示する
* 注釈は子ノードの評価値などの表示と重ならないように、そのすぐ上に表示する[^2]

また、__範囲の色分け__ は以下のように表示することにしました。色分けの方法は[以前の記事](https://qiita.com/ysgeso/items/fd421d90cd1f47e38cdb#%E7%AF%84%E5%9B%B2%E3%81%94%E3%81%A8%E3%81%AE%E8%89%B2%E5%88%86%E3%81%91)で __`calc_score_by_ab` で計算したデータを Mbtree_Anim で色分けする方法とほぼ同様__ です。

* exact value の範囲を黄色で塗りつぶす
* 枝狩りを行わない fail low または fail high の範囲を灰色で塗りつぶす
* 枝狩りを行う fail low または fail high の範囲を水色で塗りつぶす

下記は そのように __`update_frameinfo`__ を修正したプログラムです。

* __6 ~ 21 行目__：αβ 法の場合に範囲の色分けを行う。行う処理は[以前の記事](https://qiita.com/ysgeso/items/fd421d90cd1f47e38cdb#%E7%AF%84%E5%9B%B2%E3%81%94%E3%81%A8%E3%81%AE%E8%89%B2%E5%88%86%E3%81%91)で説明した処理とほぼ同じなので忘れた方は復習すること。また、この処理は数直線の描画より前に行う必要がある点に注意すること[^3]
* __29 ~ 35 行目__：数直線上に α 値と β 値の初期値を色のついた丸で描画し、上部にその注釈を描画するように修正した

```python
 1  def update_frameinfo(self):
元と同じなので省略
 2      minus_inf = -3 if self.mbtree.shortest_victory else -2
 3      plus_inf = 4 if self.mbtree.shortest_victory else 2
 4  
 5      # 範囲の色分け
 6      if not minimax:
 7          alphaorig = framedata["alphaorig"]
 8          betaorig = framedata["betaorig"]
 9          alphaorig_coord = calc_coord(alphaorig)
10          betaorig_coord = calc_coord(betaorig)
11          color = "lightgray" if maxnode else "aqua"
12          rect = patches.Rectangle(xy=(minus_inf, -0.5), width=alphaorig_coord-minus_inf,
13                                  height=1, fc=color)
14          self.abax.add_patch(rect)
15          rect = patches.Rectangle(xy=(alphaorig_coord, -0.5), width=betaorig_coord-alphaorig_coord,
16                                  height=1, fc="yellow")
17          self.abax.add_patch(rect)
18          color = "aqua" if maxnode else "lightgray"
19          rect = patches.Rectangle(xy=(betaorig_coord, -0.5), width=plus_inf-betaorig_coord,
20                                  height=1, fc=color)
21          self.abax.add_patch(rect)
22      # 数直線の描画    
23      self.abax.plot(range(minus_inf, plus_inf + 1), [0] * (plus_inf + 1 - minus_inf) , "|-k")
元と同じなので省略
24      arrowprops = { "arrowstyle": "->"}
25      leftx = -3
26      rightx = 4
27      centerx = (leftx + rightx) / 2
28      # α 値 と β 値の初期値の表示
29      if not minimax:
30          self.abax.plot(alphaorig_coord, 0, "or")
31          self.abax.annotate(f"α = {alphaorig}", xy=(alphaorig_coord, 0),
32                          xytext=(leftx, 1.7), arrowprops=arrowprops, ha="left")        
33          self.abax.plot(betaorig_coord, 0, "ob")
34          self.abax.annotate(f"β = {betaorig}", xy=(betaorig_coord, 0),
35                          xytext=(rightx, 1.7), arrowprops=arrowprops, ha="right")        
36      # そのフレームでのノードの評価値の表示
37      if status in ["score", "update", "end"]:
元と同じなので省略
38          
39  Mbtree_Anim.update_frameinfo = update_frameinfo
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```python
def update_frameinfo(self):
    def calc_coord(score):
        return min(max(minus_inf, score), plus_inf)
        
    framedata = self.mbtree.ablist_by_score[self.play.value]
    status = framedata["status"]
    maxnode = self.selectednode.mb.turn == Marubatsu.CIRCLE
    minimax = self.mbtree.minimax
    
    self.abax.clear()
    self.abax.set_xlim(-4, 23)
    if minimax:
        self.abax.set_ylim(-1.5, 1.5)
    else:
        self.abax.set_ylim(-4.3, 2.3)
    self.abax.axis("off")

    minus_inf = -3 if self.mbtree.shortest_victory else -2
    plus_inf = 4 if self.mbtree.shortest_victory else 2

    # 範囲の色分け
    if not minimax:
        alphaorig = framedata["alphaorig"]
        betaorig = framedata["betaorig"]
        alphaorig_coord = calc_coord(alphaorig)
        betaorig_coord = calc_coord(betaorig)
        color = "lightgray" if maxnode else "aqua"
        rect = patches.Rectangle(xy=(minus_inf, -0.5), width=alphaorig_coord-minus_inf,
                                 height=1, fc=color)
        self.abax.add_patch(rect)
        rect = patches.Rectangle(xy=(alphaorig_coord, -0.5), width=betaorig_coord-alphaorig_coord,
                                 height=1, fc="yellow")
        self.abax.add_patch(rect)
        color = "aqua" if maxnode else "lightgray"
        rect = patches.Rectangle(xy=(betaorig_coord, -0.5), width=plus_inf-betaorig_coord,
                                 height=1, fc=color)
        self.abax.add_patch(rect)
    # 数直線の描画    
    self.abax.plot(range(minus_inf, plus_inf + 1), [0] * (plus_inf + 1 - minus_inf) , "|-k")
    for num in range(minus_inf, plus_inf + 1):
        if num == minus_inf:
            numtext = "" if self.mbtree.init_ab else "-∞"
        elif num == plus_inf:
            numtext = "" if self.mbtree.init_ab else "∞"
        else:
            numtext = num
        self.abax.text(num, -1, numtext, ha="center")        

    # メッセージの表示
    if minimax:
        linenum = 4
        linetop = 1
    else:
        linenum = 9
        linetop = 1.7
    textlist = [""] * linenum
    textcolorlist = ["black"] * linenum

    algorithm = "mm 法" if self.mbtree.minimax else "αβ法"
    use_tt = "〇" if self.mbtree.use_tt else "×"
    shortest_victory = "〇" if self.mbtree.shortest_victory else "×"
    init_ab = "〇" if self.mbtree.init_ab else "×"
    textlist[0] = f"{algorithm}　置換表 {use_tt}　最短 {shortest_victory}"
    if not self.mbtree.minimax:
        textlist[0] += f"　初期値 {init_ab}"
    
    textlist[1] = f"深さ {self.selectednode.mb.move_count} "
    if maxnode:
        textlist[1] += "max node"
    else:
        textlist[1] += "min node"
    
    statusdata = {
        "start": {
            "text": "処理の開始",
            "color": "white"
        },
        "tt": {
            "text": "置換表の処理",
            "color": "honeydew"
        },
        "score": {
            "text": "子ノードの評価値",
            "color": "lightyellow"
        },
        "update": {
            "text": "更新処理",
            "color": "lightcyan"
        },
        "end": {
            "text": "評価値の確定",
            "color": "lavenderblush"
        },
    }
    textlist[2] = statusdata[status]["text"]
    facecolor = statusdata[status]["color"]
    
    arrowprops = { "arrowstyle": "->"}
    leftx = -3
    rightx = 4
    centerx = (leftx + rightx) / 2
    # α 値 と β 値の初期値の表示
    if not minimax:
        self.abax.plot(alphaorig_coord, 0, "or")
        self.abax.annotate(f"α = {alphaorig}", xy=(alphaorig_coord, 0),
                           xytext=(leftx, 1.7), arrowprops=arrowprops, ha="left")        
        self.abax.plot(betaorig_coord, 0, "ob")
        self.abax.annotate(f"β = {betaorig}", xy=(betaorig_coord, 0),
                           xytext=(rightx, 1.7), arrowprops=arrowprops, ha="right")        
    # そのフレームでのノードの評価値の表示
    if status in ["score", "update", "end"]:
        score = framedata["score"]
        score_coord = calc_coord(score)
        text_coord = leftx if maxnode else rightx
        ha = "left" if maxnode else "right"
        self.abax.plot(score_coord, 0, "ok")
        self.abax.annotate(f"score = {score}", xy=(score_coord, 0),
                           xytext=(text_coord, 1), arrowprops=arrowprops, ha=ha)        
    # 子ノードの評価値の表示
    if status == "score":
        childscore = framedata["childscore"]
        childscore_coord = calc_coord(childscore)
        text_coord = rightx if maxnode else leftx
        ha = "right" if maxnode else "left"
        self.abax.plot(childscore_coord, 0, "og")
        self.abax.annotate(f"cscore = {childscore}", xy=(childscore_coord, 0),
                           xytext=(text_coord, 1), arrowprops=arrowprops, ha=ha)        
    # 置換表にデータが登録されていたかどうかの表示
    elif status =="tt":
        if framedata["registered_in_tt"]:
            textlist[3] = "置換表に登録済"
            textcolorlist[3] = "red"
            score = framedata["lower_bound"]
            score_coord = calc_coord(score)
            self.abax.plot(score_coord, 0, "om")
            self.abax.annotate(f"置換表の評価値 = {score}", xy=(score_coord, 0),
                               xytext=(centerx, 1), arrowprops=arrowprops, ha="center")        
        else:
            textlist[3] = "置換表に未登録"
    # ノードの評価値が更新されたかどうかの表示
    elif status == "update":
        if framedata["updated"]:
            textlist[3] = "評価値の更新"
            textcolorlist[3] = "red"
        else:
            textlist[3] = "評価値の更新なし"
    # 置換表に登録したかどうかの表示
    elif status == "end":
        if self.mbtree.use_tt:
            if framedata["registered_in_tt"]:
                textlist[3] = "置換表に登録されていたデータを利用"
            else:
                textlist[3] = "置換表への登録"
                textcolorlist[3] = "red"

    self.abfig.set_facecolor(facecolor)
    for i in range(linenum):
        self.abax.text(5, linetop - i * 0.7, textlist[i], c=textcolorlist[i])

    num_calculated = framedata["num_calculated"]
    num_pruned = framedata["num_pruned"]
    num_total = num_calculated + num_pruned
    num_ratio = num_calculated / num_total if num_total != 0 else 0
    prev_framedata = self.mbtree.ablist_by_score[self.prev_frame]
    prev_num_calculated = prev_framedata["num_calculated"]
    prev_num_pruned = prev_framedata["num_pruned"]
    prev_num_total = prev_num_calculated + prev_num_pruned
    diff_num_calculated = num_calculated - prev_num_calculated
    diff_num_pruned = num_pruned - prev_num_pruned
    diff_num_total = num_total - prev_num_total
    diff_num_ratio = diff_num_calculated / diff_num_total if diff_num_total != 0 else 0

    textlist = [ "計算済", "枝狩り", "合計", "割合" ]
    datalist = [ num_calculated, num_pruned, num_total, f"{num_ratio * 100:.1f}%"]
    diff_datalist = [ f"{diff_num_calculated:+d}", f"{diff_num_pruned:+d}", 
                    f"{diff_num_total:+d}", f"{diff_num_ratio * 100:.1f}%"]
    for i in range(4):
        self.abax.text(15, linetop - i * 0.7, textlist[i])
        self.abax.text(19.5, linetop - i * 0.7, datalist[i], ha="right")
        self.abax.text(22.5, linetop - i * 0.7, diff_datalist[i], ha="right")
        
Mbtree_Anim.update_frameinfo = update_frameinfo
```
</details>

<details><summary><strong>修正箇所</strong></summary>

```diff_python
def update_frameinfo(self):
元と同じなので省略
    minus_inf = -3 if self.mbtree.shortest_victory else -2
    plus_inf = 4 if self.mbtree.shortest_victory else 2

    # 範囲の色分け
+   if not minimax:
+       alphaorig = framedata["alphaorig"]
+       betaorig = framedata["betaorig"]
+       alphaorig_coord = calc_coord(alphaorig)
+       betaorig_coord = calc_coord(betaorig)
+       color = "lightgray" if maxnode else "aqua"
+       rect = patches.Rectangle(xy=(minus_inf, -0.5), width=alphaorig_coord-minus_inf,
+                                height=1, fc=color)
+       self.abax.add_patch(rect)
+       rect = patches.Rectangle(xy=(alphaorig_coord, -0.5), width=betaorig_coord-alphaorig_coord,
+                                height=1, fc="yellow")
+       self.abax.add_patch(rect)
+       color = "aqua" if maxnode else "lightgray"
+       rect = patches.Rectangle(xy=(betaorig_coord, -0.5), width=plus_inf-betaorig_coord,
+                                height=1, fc=color)
+       self.abax.add_patch(rect)
    # 数直線の描画    
    self.abax.plot(range(minus_inf, plus_inf + 1), [0] * (plus_inf + 1 - minus_inf) , "|-k")
元と同じなので省略
    arrowprops = { "arrowstyle": "->"}
    leftx = -3
    rightx = 4
    centerx = (leftx + rightx) / 2
    # α 値 と β 値の初期値の表示
+   if not minimax:
+       self.abax.plot(alphaorig_coord, 0, "or")
+       self.abax.annotate(f"α = {alphaorig}", xy=(alphaorig_coord, 0),
+                          xytext=(leftx, 1.7), arrowprops=arrowprops, ha="left")        
+       self.abax.plot(betaorig_coord, 0, "ob")
+       self.abax.annotate(f"β = {betaorig}", xy=(betaorig_coord, 0),
+                          xytext=(rightx, 1.7), arrowprops=arrowprops, ha="right")        
    # そのフレームでのノードの評価値の表示
    if status in ["score", "update", "end"]:
元と同じなので省略
        
Mbtree_Anim.update_frameinfo = update_frameinfo
```
</details>

実行結果はこれまでと同様なので省略しますが、上記の修正後に下記のプログラムで __ミニマックス法での表示が正しく行なわれる__ ことが確認できます。

```python
mbtree.calc_score_for_anim(mbtree.root, minimax=True)
Mbtree_Anim(mbtree, isscore=True)
```

下記のプログラムで __αβ 法__ の場合で計算したデータに対して __数直線上に α 値と β 値の初期値が表示__ され、__上部に注釈が表示される__ ことが確認できます。また、下図では __数直線上の全ての範囲が exact value__ なので __全ての範囲が黄色で塗りつぶされる__ ことが確認できます。

```python
mbtree.calc_score_for_anim(mbtree.root, minimax=False, shortest_victory=True)
Mbtree_Anim(mbtree, isscore=True)
```

実行結果

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/1a43cb25-c7e1-4660-9e98-eb886596293d.png" width="500">

__ルートノードの α 値と β 値の初期値を設定__ した場合はルートノードでも fail low と fail high の範囲が存在し、下記のプログラムの実行結果のように __max ノードでの fail low と fail high の範囲が正しく色分けされる__ ことが確認できます。

```python
mbtree.calc_score_for_anim(mbtree.root, minimax=False, shortest_victory=True, init_ab=True)
Mbtree_Anim(mbtree, isscore=True)
```

実行結果

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/6c0227ef-1c09-44f0-af7c-c59666ae09c0.png" width="500">

また、__上段の >__ をクリックすると下図のように __min ノードでの fail low と fail high__ の範囲の色が __上図と逆__ になり、__正しく色分けされる__ ことが確認できます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/f9c1f5b2-3f1e-4309-8f68-a8505331d9a0.png" width="500">

## 注釈の表示に関する問題点と修正

実は上記のプログラムには一つ __問題があります__。どのような問題があるかについて少し考えてみて下さい。

上図で __下段の >__ をクリックすると、下図のように __"score" のフレームが表示__ されますが、その際に __α 値と β 値の初期値の注釈の矢印__ と、__このノードの評価値や子ノードの評価値__ が __重なって見づらくなってしまう__ という問題があります。この問題を解決する方法について少し考えてみて下さい。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/679c5eb9-d0ca-4a96-9b18-72d689361a9e.png" width="500">

この問題を解決する方法として、本記事では __α 値と β 値の初期値の注釈__ から数直線への __矢印__ を、そのノードの評価値や子ノードの評価値が数直線上に表示されない __"start" と "tt" のフレームのみで表示する__ ことにします。また、__α 値と β 値の初期値__ は色分けされた範囲の __境目なので__、矢印で示さなくてもその値が __どこにあるかが見た目からわかる__ ので、__数直線上の丸印__ も __"start" と "tt" のフレームのみで表示__ することにします。

なお、__ミニマックス法__ で __置換表にノードの評価値が登録されている場合__ は __"tt" のフレーム__ で __数直線上にその評価値を表示__ するため同様の問題が発生するのではないかと思う人がいるかもしれません。__αβ 法__ の場合で __置換表にミニマックス値の範囲が登録されていた場合__ は次回の記事で説明する __別の方法で表示__ することで __表示が重ならないようにします__。

下記はそのように __`update_frameinfo`__ を修正したプログラムです。

* __8 ~ 14 行目__："start" と "tt" のフレームの場合は先程と同様の表示を行うように修正する
* __15 ~ 17 行目__：それ以外の状態のフレームの場合は α 値と β 値の初期値のメッセージだけを表示するように修正する

```python
 1  def update_frameinfo(self):
元と同じなので省略
 2      arrowprops = { "arrowstyle": "->"}
 3      leftx = -3
 4      rightx = 4
 5      centerx = (leftx + rightx) / 2
 6      # α 値 と β 値の初期値の表示
 7      if not minimax:
 8          if status == "start" or status == "tt":
 9              self.abax.plot(alphaorig_coord, 0, "or")
10              self.abax.annotate(f"α = {alphaorig}", xy=(alphaorig_coord, 0),
11                                 xytext=(leftx, 1.7), arrowprops=arrowprops, ha="left")        
12              self.abax.plot(betaorig_coord, 0, "ob")
13              self.abax.annotate(f"β = {betaorig}", xy=(betaorig_coord, 0),
14                                 xytext=(rightx, 1.7), arrowprops=arrowprops, ha="right")        
15          else:
16              self.abax.text(leftx, 1.7, f"α = {alphaorig}", ha="left")   
17              self.abax.text(rightx, 1.7, f"β = {betaorig}", ha="right")       
元と同じなので省略
18          
19  Mbtree_Anim.update_frameinfo = update_frameinfo
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```python
def update_frameinfo(self):
    def calc_coord(score):
        return min(max(minus_inf, score), plus_inf)
        
    framedata = self.mbtree.ablist_by_score[self.play.value]
    status = framedata["status"]
    maxnode = self.selectednode.mb.turn == Marubatsu.CIRCLE
    minimax = self.mbtree.minimax
    
    self.abax.clear()
    self.abax.set_xlim(-4, 23)
    if minimax:
        self.abax.set_ylim(-1.5, 1.5)
    else:
        self.abax.set_ylim(-4.3, 2.3)
    self.abax.axis("off")

    minus_inf = -3 if self.mbtree.shortest_victory else -2
    plus_inf = 4 if self.mbtree.shortest_victory else 2

    # 範囲の色分け
    if not minimax:
        alphaorig = framedata["alphaorig"]
        betaorig = framedata["betaorig"]
        alphaorig_coord = calc_coord(alphaorig)
        betaorig_coord = calc_coord(betaorig)
        color = "lightgray" if maxnode else "aqua"
        rect = patches.Rectangle(xy=(minus_inf, -0.5), width=alphaorig_coord-minus_inf,
                                height=1, fc=color)
        self.abax.add_patch(rect)
        rect = patches.Rectangle(xy=(alphaorig_coord, -0.5), width=betaorig_coord-alphaorig_coord,
                                height=1, fc="yellow")
        self.abax.add_patch(rect)
        color = "aqua" if maxnode else "lightgray"
        rect = patches.Rectangle(xy=(betaorig_coord, -0.5), width=plus_inf-betaorig_coord,
                                height=1, fc=color)
        self.abax.add_patch(rect)
    # 数直線の描画    
    self.abax.plot(range(minus_inf, plus_inf + 1), [0] * (plus_inf + 1 - minus_inf) , "|-k")
    for num in range(minus_inf, plus_inf + 1):
        if num == minus_inf:
            numtext = "" if self.mbtree.init_ab else "-∞"
        elif num == plus_inf:
            numtext = "" if self.mbtree.init_ab else "∞"
        else:
            numtext = num
        self.abax.text(num, -1, numtext, ha="center")        

    # メッセージの表示
    if minimax:
        linenum = 4
        linetop = 1
    else:
        linenum = 9
        linetop = 1.7
    textlist = [""] * linenum
    textcolorlist = ["black"] * linenum

    algorithm = "mm 法" if self.mbtree.minimax else "αβ法"
    use_tt = "〇" if self.mbtree.use_tt else "×"
    shortest_victory = "〇" if self.mbtree.shortest_victory else "×"
    init_ab = "〇" if self.mbtree.init_ab else "×"
    textlist[0] = f"{algorithm}　置換表 {use_tt}　最短 {shortest_victory}"
    if not self.mbtree.minimax:
        textlist[0] += f"　初期値 {init_ab}"
    
    textlist[1] = f"深さ {self.selectednode.mb.move_count} "
    if maxnode:
        textlist[1] += "max node"
    else:
        textlist[1] += "min node"
    
    statusdata = {
        "start": {
            "text": "処理の開始",
            "color": "white"
        },
        "tt": {
            "text": "置換表の処理",
            "color": "honeydew"
        },
        "score": {
            "text": "子ノードの評価値",
            "color": "lightyellow"
        },
        "update": {
            "text": "更新処理",
            "color": "lightcyan"
        },
        "end": {
            "text": "評価値の確定",
            "color": "lavenderblush"
        },
    }
    textlist[2] = statusdata[status]["text"]
    facecolor = statusdata[status]["color"]
    
    arrowprops = { "arrowstyle": "->"}
    leftx = -3
    rightx = 4
    centerx = (leftx + rightx) / 2
    # α 値 と β 値の初期値の表示
    if not minimax:
        if status == "start" or status == "tt":
            self.abax.plot(alphaorig_coord, 0, "or")
            self.abax.annotate(f"α = {alphaorig}", xy=(alphaorig_coord, 0),
                               xytext=(leftx, 1.7), arrowprops=arrowprops, ha="left")        
            self.abax.plot(betaorig_coord, 0, "ob")
            self.abax.annotate(f"β = {betaorig}", xy=(betaorig_coord, 0),
                               xytext=(rightx, 1.7), arrowprops=arrowprops, ha="right")        
        else:
            self.abax.text(leftx, 1.7, f"α = {alphaorig}", ha="left")   
            self.abax.text(rightx, 1.7, f"β = {betaorig}", ha="right")       
    # そのフレームでのノードの評価値の表示
    if status in ["score", "update", "end"]:
        score = framedata["score"]
        score_coord = calc_coord(score)
        text_coord = leftx if maxnode else rightx
        ha = "left" if maxnode else "right"
        self.abax.plot(score_coord, 0, "ok")
        self.abax.annotate(f"score = {score}", xy=(score_coord, 0),
                           xytext=(text_coord, 1), arrowprops=arrowprops, ha=ha)        
    # 子ノードの評価値の表示
    if status == "score":
        childscore = framedata["childscore"]
        childscore_coord = calc_coord(childscore)
        text_coord = rightx if maxnode else leftx
        ha = "right" if maxnode else "left"
        self.abax.plot(childscore_coord, 0, "og")
        self.abax.annotate(f"cscore = {childscore}", xy=(childscore_coord, 0),
                           xytext=(text_coord, 1), arrowprops=arrowprops, ha=ha)        
    # 置換表にデータが登録されていたかどうかの表示
    elif status =="tt":
        if framedata["registered_in_tt"]:
            textlist[3] = "置換表に登録済"
            textcolorlist[3] = "red"
            score = framedata["lower_bound"]
            score_coord = calc_coord(score)
            self.abax.plot(score_coord, 0, "om")
            self.abax.annotate(f"置換表の評価値 = {score}", xy=(score_coord, 0),
                            xytext=(centerx, 1), arrowprops=arrowprops, ha="center")        
        else:
            textlist[3] = "置換表に未登録"
    # ノードの評価値が更新されたかどうかの表示
    elif status == "update":
        if framedata["updated"]:
            textlist[3] = "評価値の更新"
            textcolorlist[3] = "red"
        else:
            textlist[3] = "評価値の更新なし"
    # 置換表に登録したかどうかの表示
    elif status == "end":
        if self.mbtree.use_tt:
            if framedata["registered_in_tt"]:
                textlist[3] = "置換表に登録されていたデータを利用"
            else:
                textlist[3] = "置換表への登録"
                textcolorlist[3] = "red"

    self.abfig.set_facecolor(facecolor)
    for i in range(linenum):
        self.abax.text(5, linetop - i * 0.7, textlist[i], c=textcolorlist[i])

    num_calculated = framedata["num_calculated"]
    num_pruned = framedata["num_pruned"]
    num_total = num_calculated + num_pruned
    num_ratio = num_calculated / num_total if num_total != 0 else 0
    prev_framedata = self.mbtree.ablist_by_score[self.prev_frame]
    prev_num_calculated = prev_framedata["num_calculated"]
    prev_num_pruned = prev_framedata["num_pruned"]
    prev_num_total = prev_num_calculated + prev_num_pruned
    diff_num_calculated = num_calculated - prev_num_calculated
    diff_num_pruned = num_pruned - prev_num_pruned
    diff_num_total = num_total - prev_num_total
    diff_num_ratio = diff_num_calculated / diff_num_total if diff_num_total != 0 else 0

    textlist = [ "計算済", "枝狩り", "合計", "割合" ]
    datalist = [ num_calculated, num_pruned, num_total, f"{num_ratio * 100:.1f}%"]
    diff_datalist = [ f"{diff_num_calculated:+d}", f"{diff_num_pruned:+d}", 
                    f"{diff_num_total:+d}", f"{diff_num_ratio * 100:.1f}%"]
    for i in range(4):
        self.abax.text(15, linetop - i * 0.7, textlist[i])
        self.abax.text(19.5, linetop - i * 0.7, datalist[i], ha="right")
        self.abax.text(22.5, linetop - i * 0.7, diff_datalist[i], ha="right")
        
Mbtree_Anim.update_frameinfo = update_frameinfo
```
</details>

<details><summary><strong>修正箇所</strong></summary>

```diff_python
def update_frameinfo(self):
元と同じなので省略
    arrowprops = { "arrowstyle": "->"}
    leftx = -3
    rightx = 4
    centerx = (leftx + rightx) / 2
    # α 値 と β 値の初期値の表示
    if not minimax:
-       self.abax.plot(alphaorig_coord, 0, "or")
-       self.abax.annotate(f"α = {alphaorig}", xy=(alphaorig_coord, 0),
-                          xytext=(leftx, 1.7), arrowprops=arrowprops, ha="left")        
-       self.abax.plot(betaorig_coord, 0, "ob")
-       self.abax.annotate(f"β = {betaorig}", xy=(betaorig_coord, 0),
-                          xytext=(rightx, 1.7), arrowprops=arrowprops, ha="right")        
+       if status == "start" or status == "tt":
+           self.abax.plot(alphaorig_coord, 0, "or")
+           self.abax.annotate(f"α = {alphaorig}", xy=(alphaorig_coord, 0),
+                              xytext=(leftx, 1.7), arrowprops=arrowprops, ha="left")        
+           self.abax.plot(betaorig_coord, 0, "ob")
+           self.abax.annotate(f"β = {betaorig}", xy=(betaorig_coord, 0),
+                              xytext=(rightx, 1.7), arrowprops=arrowprops, ha="right")        
+       else:
+           self.abax.text(leftx, 1.7, f"α = {alphaorig}", ha="left")   
+           self.abax.text(rightx, 1.7, f"β = {betaorig}", ha="right")       
元と同じなので省略
        
Mbtree_Anim.update_frameinfo = update_frameinfo
```
</details>

実行結果は同じなので省略しますが、下記のプログラムで __置換表付き αβ 法__ の場合で計算したデータに対して Mbtree_Anim を表示すると __"start" のフレームに対して先程と同じ表示__ が行われます。

```python
mbtree.calc_score_for_anim(mbtree.root, minimax=False, use_tt=True, 
                           init_ab=True, shortest_victory=True)
Mbtree_Anim(mbtree, isscore=True)
```

次に __下段の >__ をクリックすると下図のように __"tt" のフレーム__ の状態の数直線の部分が __"start" のフレームと同様の方法で表示__ されることが確認できます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/c2fa1f3f-d423-4c3d-89ee-791d91c46ef8.png" width="500">

もう一度 __下段の >__ をクリックすると下図のように __"start" のフレーム__ の状態が下記のように __意図通りに表示される__ ことが確認できます。表示は省略しますが "update"、"end" のフレームでも下図と同様の表示が行われることを確認して下さい。

* 数直線上に α 値と β 値の初期値の丸が表示されない
* 上部の α 値と β 値の初期値の表示から数直線上に矢印が表示されない

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/1b37b8c2-e0fe-421e-b0ed-781bbf2ac8fe.png" width="500">

なお、現時点では置換表にミニマックス値の範囲が登録されている場合は下図のように表示が重なりますが、__下図に表示されている置換表の評価値__ はミニマックス方の場合の表示なので __αβ 法の場合は正しくありません__。この場合の正しい表示と、表示が重ならないようにする工夫については次回の記事で実装します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/0ff1c6a3-fe36-476f-bf1f-bec94b90047a.png" width="500">

## 範囲の説明の表示

本記事では __範囲の説明の表示__ を右の __枝狩りが行われた数などの下に表示__ することにします。その理由は、__真ん中の下__ には次回の記事で __別のメッセージを表示する__ からです。

また、__それぞれの範囲の説明__ には上から順に __fail low__、__exact value__、__fail high__ を表示することにします。なお、__今回の記事__ では __範囲の説明には常に同じ表示__ を行いますが、次回の記事で `update_ab` での範囲の説明の表示のように、__状況に応じて範囲の説明内容や表示の色を変える__ という工夫を行う予定です。

なお、[以前の記事](https://qiita.com/ysgeso/items/fd421d90cd1f47e38cdb#%E5%A1%97%E3%82%8A%E3%81%A4%E3%81%B6%E3%81%97%E3%81%AE%E8%89%B2%E3%81%AE%E8%AA%AC%E6%98%8E%E3%81%AE%E6%8F%8F%E7%94%BB)で __`update_ab`__ で __範囲の表示を実装する際__ には、__常に一番上__ に α 狩りまたは β 狩りを行う __水色の範囲を表示__ していましたが、__`update_frameinfo`__ では常に上から順に __fail low__、__exact value__、__fail high__ の表示を行うことにしたので __max ノード と min ノード__ で表示する範囲の __色の順番が逆__ になります。

下記はそのように __`update_frameinfo`__ を修正したプログラムです。

* __12 ~ 24 行目__：αβ 法の場合に範囲の説明の表示を行う
* __13 ~ 17 行目__：それぞれの範囲の色を計算して `facecolorlist` に代入する
* __18、19 行目__：それぞれの範囲の説明と色を変数に代入する。現時点では範囲の説明と色は常に同じだが、次回の記事で状況に応じて説明と色を変えるようにする予定である
* __20 ~ 24 行目__：それぞれの範囲の色を表す長方形と、説明文を表示する
```python
 1  def update_frameinfo(self):
元と同じなので省略
 2      textlist = [ "計算済", "枝狩り", "合計", "割合" ]
 3      datalist = [ num_calculated, num_pruned, num_total, f"{num_ratio * 100:.1f}%"]
 4      diff_datalist = [ f"{diff_num_calculated:+d}", f"{diff_num_pruned:+d}", 
 5                      f"{diff_num_total:+d}", f"{diff_num_ratio * 100:.1f}%"]
 6      for i in range(4):
 7          self.abax.text(15, linetop - i * 0.7, textlist[i])
 8          self.abax.text(19.5, linetop - i * 0.7, datalist[i], ha="right")
 9          self.abax.text(22.5, linetop - i * 0.7, diff_datalist[i], ha="right")
10          
11      # 範囲の説明の表示
12      if not minimax:
13          facecolorlist = [
14              "lightgray" if maxnode else "aqua", 
15              "yellow",
16              "aqua" if maxnode else "lightgray", 
17          ]
18          textlist = ["fail low", "exact value", "fail high"]
19          textcolorlist = ["black", "black", "black"]
20          for i in range(3):
21              rect = patches.Rectangle(xy=(15, linetop - 0.1 - (i + 5) * 0.7), 
22                                       width=0.8, height=0.5, fc=facecolorlist[i], ec="k")
23              self.abax.add_patch(rect)
24              self.abax.text(16.2, linetop - (i + 5) * 0.7, textlist[i], c=textcolorlist[i])  
25           
26  Mbtree_Anim.update_frameinfo = update_frameinfo
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```python
def update_frameinfo(self):
    def calc_coord(score):
        return min(max(minus_inf, score), plus_inf)
        
    framedata = self.mbtree.ablist_by_score[self.play.value]
    status = framedata["status"]
    maxnode = self.selectednode.mb.turn == Marubatsu.CIRCLE
    minimax = self.mbtree.minimax
    
    self.abax.clear()
    self.abax.set_xlim(-4, 23)
    if minimax:
        self.abax.set_ylim(-1.5, 1.5)
    else:
        self.abax.set_ylim(-4.3, 2.3)
    self.abax.axis("off")

    minus_inf = -3 if self.mbtree.shortest_victory else -2
    plus_inf = 4 if self.mbtree.shortest_victory else 2

    # 範囲の色分け
    if not minimax:
        alphaorig = framedata["alphaorig"]
        betaorig = framedata["betaorig"]
        alphaorig_coord = calc_coord(alphaorig)
        betaorig_coord = calc_coord(betaorig)
        color = "lightgray" if maxnode else "aqua"
        rect = patches.Rectangle(xy=(minus_inf, -0.5), width=alphaorig_coord-minus_inf,
                                height=1, fc=color)
        self.abax.add_patch(rect)
        rect = patches.Rectangle(xy=(alphaorig_coord, -0.5), width=betaorig_coord-alphaorig_coord,
                                height=1, fc="yellow")
        self.abax.add_patch(rect)
        color = "aqua" if maxnode else "lightgray"
        rect = patches.Rectangle(xy=(betaorig_coord, -0.5), width=plus_inf-betaorig_coord,
                                height=1, fc=color)
        self.abax.add_patch(rect)
    # 数直線の描画    
    self.abax.plot(range(minus_inf, plus_inf + 1), [0] * (plus_inf + 1 - minus_inf) , "|-k")
    for num in range(minus_inf, plus_inf + 1):
        if num == minus_inf:
            numtext = "" if self.mbtree.init_ab else "-∞"
        elif num == plus_inf:
            numtext = "" if self.mbtree.init_ab else "∞"
        else:
            numtext = num
        self.abax.text(num, -1, numtext, ha="center")        

    # メッセージの表示
    if minimax:
        linenum = 4
        linetop = 1
    else:
        linenum = 9
        linetop = 1.7
    textlist = [""] * linenum
    textcolorlist = ["black"] * linenum

    algorithm = "mm 法" if self.mbtree.minimax else "αβ法"
    use_tt = "〇" if self.mbtree.use_tt else "×"
    shortest_victory = "〇" if self.mbtree.shortest_victory else "×"
    init_ab = "〇" if self.mbtree.init_ab else "×"
    textlist[0] = f"{algorithm}　置換表 {use_tt}　最短 {shortest_victory}"
    if not self.mbtree.minimax:
        textlist[0] += f"　初期値 {init_ab}"
    
    textlist[1] = f"深さ {self.selectednode.mb.move_count} "
    if maxnode:
        textlist[1] += "max node"
    else:
        textlist[1] += "min node"
    
    statusdata = {
        "start": {
            "text": "処理の開始",
            "color": "white"
        },
        "tt": {
            "text": "置換表の処理",
            "color": "honeydew"
        },
        "score": {
            "text": "子ノードの評価値",
            "color": "lightyellow"
        },
        "update": {
            "text": "更新処理",
            "color": "lightcyan"
        },
        "end": {
            "text": "評価値の確定",
            "color": "lavenderblush"
        },
    }
    textlist[2] = statusdata[status]["text"]
    facecolor = statusdata[status]["color"]
    
    arrowprops = { "arrowstyle": "->"}
    leftx = -3
    rightx = 4
    centerx = (leftx + rightx) / 2
    # α 値 と β 値の初期値の表示
    if not minimax:
        if status == "start" or status == "tt":
            self.abax.plot(alphaorig_coord, 0, "or")
            self.abax.annotate(f"α = {alphaorig}", xy=(alphaorig_coord, 0),
                           xytext=(leftx, 1.7), arrowprops=arrowprops, ha="left")        
            self.abax.plot(betaorig_coord, 0, "ob")
            self.abax.annotate(f"β = {betaorig}", xy=(betaorig_coord, 0),
                           xytext=(rightx, 1.7), arrowprops=arrowprops, ha="right")        
        else:
            self.abax.text(leftx, 1.7, f"α = {alphaorig}", ha="left")   
            self.abax.text(rightx, 1.7, f"β = {betaorig}", ha="right")       
    # そのフレームでのノードの評価値の表示
    if status in ["score", "update", "end"]:
        score = framedata["score"]
        score_coord = calc_coord(score)
        text_coord = leftx if maxnode else rightx
        ha = "left" if maxnode else "right"
        self.abax.plot(score_coord, 0, "ok")
        self.abax.annotate(f"score = {score}", xy=(score_coord, 0),
                           xytext=(text_coord, 1), arrowprops=arrowprops, ha=ha)        
    # 子ノードの評価値の表示
    if status == "score":
        childscore = framedata["childscore"]
        childscore_coord = calc_coord(childscore)
        text_coord = rightx if maxnode else leftx
        ha = "right" if maxnode else "left"
        self.abax.plot(childscore_coord, 0, "og")
        self.abax.annotate(f"cscore = {childscore}", xy=(childscore_coord, 0),
                           xytext=(text_coord, 1), arrowprops=arrowprops, ha=ha)        
    # 置換表にデータが登録されていたかどうかの表示
    elif status =="tt":
        if framedata["registered_in_tt"]:
            textlist[3] = "置換表に登録済"
            textcolorlist[3] = "red"
            score = framedata["lower_bound"]
            score_coord = calc_coord(score)
            self.abax.plot(score_coord, 0, "om")
            self.abax.annotate(f"置換表の評価値 = {score}", xy=(score_coord, 0),
                            xytext=(centerx, 1), arrowprops=arrowprops, ha="center")        
        else:
            textlist[3] = "置換表に未登録"
    # ノードの評価値が更新されたかどうかの表示
    elif status == "update":
        if framedata["updated"]:
            textlist[3] = "評価値の更新"
            textcolorlist[3] = "red"
        else:
            textlist[3] = "評価値の更新なし"
    # 置換表に登録したかどうかの表示
    elif status == "end":
        if self.mbtree.use_tt:
            if framedata["registered_in_tt"]:
                textlist[3] = "置換表に登録されていたデータを利用"
            else:
                textlist[3] = "置換表への登録"
                textcolorlist[3] = "red"

    self.abfig.set_facecolor(facecolor)
    for i in range(linenum):
        self.abax.text(5, linetop - i * 0.7, textlist[i], c=textcolorlist[i])

    num_calculated = framedata["num_calculated"]
    num_pruned = framedata["num_pruned"]
    num_total = num_calculated + num_pruned
    num_ratio = num_calculated / num_total if num_total != 0 else 0
    prev_framedata = self.mbtree.ablist_by_score[self.prev_frame]
    prev_num_calculated = prev_framedata["num_calculated"]
    prev_num_pruned = prev_framedata["num_pruned"]
    prev_num_total = prev_num_calculated + prev_num_pruned
    diff_num_calculated = num_calculated - prev_num_calculated
    diff_num_pruned = num_pruned - prev_num_pruned
    diff_num_total = num_total - prev_num_total
    diff_num_ratio = diff_num_calculated / diff_num_total if diff_num_total != 0 else 0

    textlist = [ "計算済", "枝狩り", "合計", "割合" ]
    datalist = [ num_calculated, num_pruned, num_total, f"{num_ratio * 100:.1f}%"]
    diff_datalist = [ f"{diff_num_calculated:+d}", f"{diff_num_pruned:+d}", 
                    f"{diff_num_total:+d}", f"{diff_num_ratio * 100:.1f}%"]
    for i in range(4):
        self.abax.text(15, linetop - i * 0.7, textlist[i])
        self.abax.text(19.5, linetop - i * 0.7, datalist[i], ha="right")
        self.abax.text(22.5, linetop - i * 0.7, diff_datalist[i], ha="right")
        
    # 範囲の説明の表示
    if not minimax:
        facecolorlist = [
            "lightgray" if maxnode else "aqua", 
            "yellow",
            "aqua" if maxnode else "lightgray", 
        ]
        textlist = ["fail low", "exact value", "fail high"]
        textcolorlist = ["black", "black", "black"]
        for i in range(3):
            rect = patches.Rectangle(xy=(15, linetop - 0.1 - (i + 5) * 0.7), 
                                     width=0.8, height=0.5, fc=facecolorlist[i], ec="k")
            self.abax.add_patch(rect)
            self.abax.text(16.2, linetop - (i + 5) * 0.7, textlist[i], c=textcolorlist[i])  
         
Mbtree_Anim.update_frameinfo = update_frameinfo
```
</details>

<details><summary><strong>修正箇所</strong></summary>

```diff_python
def update_frameinfo(self):
元と同じなので省略
    textlist = [ "計算済", "枝狩り", "合計", "割合" ]
    datalist = [ num_calculated, num_pruned, num_total, f"{num_ratio * 100:.1f}%"]
    diff_datalist = [ f"{diff_num_calculated:+d}", f"{diff_num_pruned:+d}", 
                    f"{diff_num_total:+d}", f"{diff_num_ratio * 100:.1f}%"]
    for i in range(4):
        self.abax.text(15, linetop - i * 0.7, textlist[i])
        self.abax.text(19.5, linetop - i * 0.7, datalist[i], ha="right")
        self.abax.text(22.5, linetop - i * 0.7, diff_datalist[i], ha="right")
        
    # 範囲の説明の表示
+   if not minimax:
+       facecolorlist = [
+           "lightgray" if maxnode else "aqua", 
+           "yellow",
+           "aqua" if maxnode else "lightgray", 
+       ]
+       textlist = ["fail low", "exact value", "fail high"]
+       textcolorlist = ["black", "black", "black"]
+       for i in range(3):
+           rect = patches.Rectangle(xy=(15, linetop - 0.1 - (i + 5) * 0.7), 
+                                    width=0.8, height=0.5, fc=facecolorlist[i], ec="k")
+           self.abax.add_patch(rect)
+           self.abax.text(16.2, linetop - (i + 5) * 0.7, textlist[i], c=textcolorlist[i])   
         
Mbtree_Anim.update_frameinfo = update_frameinfo
```
</details>

下記のプログラムで __αβ 法__ の場合で計算したデータに対して Mbtree_Anim を表示すると、実行結果のように __右下に範囲の説明が正しく表示される__ ようになったことが確認できます。

```python
mbtree.calc_score_for_anim(mbtree.root, minimax=False, use_tt=True, 
                           init_ab=True, shortest_victory=True)
Mbtree_Anim(mbtree, isscore=True)
```

実行結果

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/1a2366d4-9308-4437-bf86-b2b6236ff308.png" width="500">

また、__上段の > ボタンを 2 回クリック__ して __min ノードを表示__ すると下図のように __min ノードでの範囲の色__ の順番が上図と逆になり、__正しく表示される__ 事が確認できます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/8045767a-8fcb-4e7f-95a8-0e2e1ceb49f9.png" width="500">

# 今回の記事のまとめ

今回の記事では、__αβ 法の視覚化__ のための __Mbtree_Anim の修正__ のうち、__常に表示する内容の表示__ の実装を行いました。次回の記事では残りの表示の実装を行います。

# 本記事で入力したプログラム

| リンク | 説明 |
|:--|:--|
| [marubatsu.ipynb](https://github.com/ysgeso/marubatsu/blob/master/160/marubatsu.ipynb)  | 本記事で入力して実行した JupyterLab のファイル |
| [tree.py](https://github.com/ysgeso/marubatsu/blob/master/160/tree_new.py)  | 本記事で更新した tree_new.py  |

# 次回の記事

https://qiita.com/ysgeso/items/b10991d23a8c084c2bba

[^1]:どのように表示するかについては次回の記事で説明します
[^2]:Axes の表示範囲の y 座標の上部を少し上にずらしたのはそのためです
[^3]:数直線の描画より後で範囲の色分けを行うと、数直線の上に範囲の色分けが表示されてしまうため数直線が表示されなくなってしまうからです