Pythonで〇×ゲームのAIを一から作成する　その74　Dropdown による AI を選択するドロップダウンメニューの作成

# 目次と前回の記事

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

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

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

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

https://github.com/ysgeso/marubatsu/blob/master/074/marubatsu.py

https://github.com/ysgeso/marubatsu/blob/master/074/ai.py

## ルールベースの AI の一覧

ルールベースの AI の一覧については、下記の記事を参照して下さい。

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

# 今回の記事の内容

前回の記事で __リセットボタン__ を __実装__ しましたが、__異なる AI__ と __対戦__ を __行う__ 場合は、__キーワード引数 `ai`__ の内容を __変更__ して、__改めて `play`__ メソッドを __実行する必要__ がある点が __面倒__ です。そこで、__`play`__ メソッドを __実行しなくても__、__GUI__ で __AI__ を __選択できる__ ようにします。

# ipywidgets の Dropdown ウィジェットの使い方

__GUI__ で __AI を選択__ できるようにするためには、__複数の項目__ の中から __1 つを選択__ するための __GUI__ が必要になります。__ipywidgets__ には、そのようなウィジェットとして、__ドロップダウンメニュー__ を表す __Dropdown__ や、__ラジオボタン__ を表す __RadioButtons__ などがあります。

## 選択を行うための ipywidgets のウィジェット

__Dropdown__ は、__下図左__ のような、__選択中__ の __項目__ が __表示__ される __ウィジェット__ で、__この上__ で __マウスを押す__ と、__下図右__ のような __ドロップダウンメニュー__ が __表示__ されます。下図は画像なので操作できないので、__実物を見たい方__ は、__下記__ の __リンク先__ を __参照__ して下さい。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/fa9813de-1862-5ce9-7a89-172b27b21f7a.png" width="250">　<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/356eb5a7-f20b-8bb9-fa22-72f56a81bfda.png" width=250">

https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20List.html#dropdown

__RadioButtons__ は、__下図__ のように、__すべての項目__ が __表示__ され、左の __ラジオボタン__ で __項目を選択__ する __ウィジェット__ です。__実物を見たい方__ は、__下記__ の __リンク先__ を __参照__ して下さい。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/b582974f-57a3-666a-64fc-141a5305c7a1.png" width="100">

https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20List.html#radiobuttons

__RadioButtons__ は、__選択__ できる __項目の数__ が __多くなる__ と、__表示範囲__ が __広くなる__ という __欠点__ があります。__これまで__ に __`ai1`__ から __`ai14s`__ まで __多数の AI__ を __作成__ してきたので、本記事では、__Dropdown__ を使って __AI を選択__ することにします。

本記事では扱いませんが、__ipywidgets__ には、他にも __選択を行う__ ための __ウィジェット__ が __多数用意__ されています。興味がある方は、下記のリンク先を参照して下さい。

https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20List.html#selection-widgets

## Dropdown の作成方法

__Dropdown__ は、__ipywidgets__ の __`Dropdown`__ という __関数__ を使って __作成__ します。例えば、__先程__ 紹介した __画像__ の __Dropdown__ は、__下記__ のプログラムで __作成__ し、__ボタン__ の __ウィジェット__ と __同様__ に、__`display`__ を使って __画面に表示__ します。__`Dropdown`__ を呼び出す際に記述する __実引数__ については、__この後で説明__ します。実際に __実行__ し、表示された __Dropdown の上__ で __マウスを押す__ と __メニューが表示__ され、__項目__ を __選択できる__ ことを __確認__ して下さい。

```python
import ipywidgets as widgets

dropdown = widgets.Dropdown(
    options=['1', '2', '3'],
    value='2',
    description='Number:',
)
display(dropdown)
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/fa9813de-1862-5ce9-7a89-172b27b21f7a.png" width="250">

## Dropdown の属性と `Dropdown` の仮引数

__`Dropdown`__ によってよって __作成__ された __オブジェクト__ には、__下記__ のような __属性__ があります。それぞれの __属性の意味__ についてはこの __後で詳しく説明__ します。なお、__`layout`__ と __`style`__ 属性は、__すべて__ の __ウィジェット__ で __共通__ します。

| 属性 | 意味 | データ型 |
|:--|:--|:--|
| __`options`__  | __選択__ できる __項目__ の __リスト__  | list  |
| __`value`__  | __選択中__ の __項目__  | any（何でもよい）  | 
| __`index`__  | __選択中__ の __項目__ の __インデックス__  | int  |
| __`label`__  | __選択中__ の __項目__ の __ラベル__ | str | None |
| __`description`__ | __左に表示__ される __説明__  | str |
| __`disabled`__ | __`True`__ の場合に __操作できなくなる__ | bool |
| __`layout`__ | 作成する __ウィジェット__ の __レイアウト__ の __設定__ | widgets.Layout |
| __`style`__ | 作成する __ウィジェット__ の __スタイル__ の __設定__ | dict |

これらの __属性__ は、__Dropwdown__ の __作成時__ に、__`Dropdown`__ の __実引数__ に、__属性名__ と __同じ名前__ の __キーワード引数__ を __記述__ することで __設定__ することが __できます__。

__具体例__ を挙げます。__下記__ のプログラムは、__先程作成__ した __Dropdown__ と __同じもの__ を __作製__ するプログラムです。__下記__ のプログラムのように、__Dropdown の属性__ は、__Dropdown__ の __作成後__ に、__直接値を代入__ することでも __設定__ することが __できます__。

```python
dropdown2 = widgets.Dropdown()
dropdown2.options = ['1', '2', '3']
dropdown2.value = '2'
dropdown2.description = 'Number:'
display(dropdown2)
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/fa9813de-1862-5ce9-7a89-172b27b21f7a.png" width="250">

また、__Dropdown__ を __`display`__ で __表示した後__ で、__下記__ のプログラムのように、__Dropdown__ の __属性を変更__ すると、__作成した Dropdown__ の __表示内容__ が下記の実行結果のように __変化します__。なお、__`description`__ 属性は、__Dropdown__ の __左に表示__ される __説明__ を表します。

```python
dropdown2.description = '数字:'
```

実行結果（先程表示した Dropdown の表示内容が変化します）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/c0961c21-19b2-6b1a-eeb6-e8d0f7274bfc.png" width="250">

## Dropdown の主要な属性

__Dropdown__ の __主要な属性__ について __説明__ します

### `options`

__Dropdown__ で __選択__ できる __各項目__ を表す __list__ です。__先程の例__ では、__`options=['1', '2', '3']`__ のように、__`'1'`__、__`'2'`__、__`'3'`__ という __3 つの文字列__ が __list__ の __要素__ に __記述__ されたので、__それら__ が __選択できる__ ようになります。

__Dropdown__ では、__選択__ できる __項目__ が __表す値__ と、メニューに __その項目__ を __表示__ する際の __文字列__ を __異なるものにする__ ことが __できます__。例えば、__メニュー__ のそれぞれの __項目__ は __"令和元年"__、__"令和二年"__、__"令和三年"__ のように __日本語で表示__ するが、それらの __項目を選択__ した際には、__2019__、__2020__、__2021__ のような __西暦__ を表す __数値__ が __選択される__ ようにすることができます。このような、__メニュー__ に __表示__ する __文字列__ の事を __ラベル__ と呼びます。

__ラベル__ を __設定__ した __Dropdown__ は、__下記__ のプログラムのように、__それぞれの項目__ を __`(ラベル, 値)`__ という __tuple__ で __記述__ します。__下記__ のプログラムを実行すると、__下図左__ のような __Dropdown__ が表示され、__この上__ で __マウスを押す__ と、__下図右__ のような、__設定__ した __ラベル__ が __表示__ される __ドロップダウンメニュー__ が __表示__ されます。

```python
dropdown3 = widgets.Dropdown(
    options=[("令和元年", 2019), ("令和二年", 2020), ("令和三年", 2021)],
    description="年:", 
)
display(dropdown3)
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/f53f2d88-1816-e99e-ddbf-4f0ee956494a.png" width="250">　<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/41c5f362-079f-9bb5-df0e-7bb307b3d962.png" width="250">

なお、__`dropdown`__ や __`dropdown2`__ のように __ラベル__ を __設定しない__ 場合は、それぞれの __項目__ の __ラベル__ には、__自動的__ に __項目の値__ を __文字列型__ のデータに __変換した値__ が __設定__ されます。

### `value`、`index`、`label`

__Dropdown__ には、__選択中の項目__ を表す、下記の表の __`value`__、__`index`__、__`label`__ という __3 つの属性__ があります。

| 属性名 | 意味 |
|:--|:--|
| __`value`__  | __選択中__ の __項目__ の __値__  |
| __`index`__  | __選択中__ の __項目__ を表す __`options` 属性__ の __要素__ の __インデックス__  |
| __`label`__  | __選択中__ の __項目__ の __ラベル__  |

:::note
__Dropdown__ の __作成時__ に、__`Dropdown`__ に __これら__ の __キーワード引数__ を __記述しなかった場合__ は、__先頭の項目__ が __選択__ された状態の __Dropdown が作成__ されます。
:::

例えば、__先程作成__ した __`dropdown3`__ の __各項目__ の __インデックス__、__ラベル__、__値__ は __以下__ のように __設定__ されています。

| インデックス | ラベル | 値 |
|:-:|:-:|:-:|
| 0  | `"平成元年"`  | `2019`  |
| 1  | `"平成二年"`  | `2020`  |
| 2  | `"平成三年"`  | `2021`  |

__`dropdown3`__ の __メニューの選択__ を __好きな項目__ に __変更した後__ で、__下記__ のプログラムを __実行__ すると、__`index`__、__`label`__、__`value`__ 属性に、__現在選択中__ の __項目__ に __対応__ する __上記の表__ の __値__ が __表示__ されます。__下記__ の __実行結果__ は、__平成三年__ を __選択した際__ のものです。

```python
print(dropdown3.index, dropdown3.label, dropdown3.value)
```

実行結果

```
2 令和三年 2021
```

また、__これらの属性__ の __いずれか__ に __値を代入__ することで、__Dropdown__ の __選択項目__ を __変える__ ことが __できます__。__その際__ に、__残りの 2 つ__ の __属性の値__ は __自動的__ に __新しく選択__ された __項目の値__ に __変更__ されます。

__下記__ は、__`index`__ 属性に __`0`__ を __代入__ するプログラムです。__実行結果__ のように、__Dropdown__ の __選択項目__ の表示が __平成元年__ に変更され、__`label`__ と __`value`__ 属性の値も __変更__ されます。

```python
dropdown3.index = 0
print(dropdown3.index, dropdown3.label, dropdown3.value)
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/f53f2d88-1816-e99e-ddbf-4f0ee956494a.png" width="250">

```
0 令和元年 2019
```

__下記__ は、__`label`__ 属性に __`"令和二年"`__ を __代入__ するプログラムです。__実行結果__ のように、__Dropdown__ の __選択項目__ が __令和二年__ に変更され、__`index`__、__`value`__ 属性の値も __変更__ されます。

```python
dropdown3.label = "平成二年"
print(dropdown3.index, dropdown3.label, dropdown3.value)
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/5cddd852-2b0c-6396-8091-1613b3a07326.png" width="250">

```
1 令和二年 2020
```

__`value`__ 属性に __値を代入__ する場合も __同様__ です。興味がある方は試してみて下さい。

なお、__下記__ のプログラムのように、__対応__ する __項目__ が __存在しない値__ を __代入__ した場合は、__実行結果__ のような __エラーが発生__ します。これは、__3 つの属性__ に __共通する性質__ です。

```python
dropdown3.value = 2000
```

実行結果

```
略
File c:\Users\ys\Anaconda3\envs\marubatsu\Lib\site-packages\ipywidgets\widgets\widget_selection.py:244, in _Selection._validate_value(self, proposal)
    242     return findvalue(self._options_values, value, self.equals) if value is not None else None
    243 except ValueError:
--> 244     raise TraitError('Invalid selection: value not found')

TraitError: Invalid selection: value not found
```

:::note
上記のエラーメッセージは、以下のような意味を持ちます。

* __TraitError__
TraitError は、ipywidget 独自のエラーです。おそらくウィジェットの選択に関するエラーを表しているのではないかと思います
* __invalid syntax__
見つからない（not found）value を選択するという、不正な（invalid）選択（selection）を行おうとした
:::

### `description` 

__Dropdown の左__ に __表示__ される __説明__（description）を表します。

__下記__ のプログラムのように、__この属性__ に __値__ を __代入しない__ 場合は、__Dropdown の 左__ には __何も表示されません__。

```python
dropdown4 = widgets.Dropdown(
    options=['1', '2', '3'],
    value='2',
)
display(dropdown4)
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/f4c24192-6970-64a1-b116-3d27d1f077dd.png" width="250">

### `disabled` 

__下記__ のプログラムのように、__この属性__ に __`True` が代入__ されていた場合は、__Dropdown__ の __メニューの項目__ が __薄い色で表示__ され、__操作ができなく__（disabled）なります。__実際に実行__ して __操作できない__ ことを __確認__ して下さい。なお、__薄い色で表示__ されるのは __項目の部分__ だけで、__左に表示__ される __`description`__ の __説明__ は __薄く表示されません__。

```python
dropdown5 = widgets.Dropdown(
    options=['1', '2', '3'],
    value='2',
    description='Number:',
    disabled=True,
)

display(dropdown5)
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/fbcba84d-7508-6404-ce07-c23f69d83ffe.png" width="250">

なお、__`disabled`__ 属性は、__すべて__ の __操作を行うウィジェット__[^1] に __共通する属性__ で、__それらのウィジェット__ の __作成時__ には、__`disabled`__ 属性の __初期値__ として __`False` が代入__ されます。

### `layout` 

__ウィジェット__ の __横幅__ や __縦幅__ などの __レイアウト__（layout）の __設定__ を表す __属性__ です。__レイアウト__ は、__ipywidgets__ の __`Layout`__ という __関数__ で __作成__ される __オブジェクト__ で __設定__ します。

__`Layout`__ には、__様々な仮引数__ があり、__その中__ で __ウィジェット__ の __サイズ__ に関する __仮引数__ には __下記__ の表のようなものがあります。

| 仮引数 | 意味 |
|:--|:--|
| __`width`__  | 横幅  |
| __`height`__  | 縦幅  |
| __`max_width`__  | 横幅の最大値  |
| __`min_width`__  | 横幅の最小値  |
| __`max_height`__  | 縦幅の最大値  |
| __`min_height`__  | 縦幅の最小値  |

また、__サイズ__ の __値__ は、__以下の方法__ で __記述__ します。

|  | 記述方法 |
|:--|:--|
| __ピクセル単位で設定する__  | __`"100px"`__ のように、__`px` で終わる文字列__ を __記述__ する  |
| __割合で設定する__  | __`"50%"`__ のように、__`%` で終わる文字列__ を __記述__ する  |

__下記__ は、__Dropdown__ の __横幅__ を __100 ピクセル__ と、__表示できる範囲__ の __半分__（50 %）の __サイズ__ で __作成__ するプログラムです。

```python
dropdown6 = widgets.Dropdown(
    options=['1', '2', '3'],
    layout=widgets.Layout(width="100px")
)
display(dropdown6)

dropdown7 = widgets.Dropdown(
    options=['1', '2', '3'],
    layout=widgets.Layout(width="50%")
)
display(dropdown7)
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/56e61654-3531-defd-ce18-92b246eed155.png" width="500">

__`"50%"`__ のように、__%__ で __サイズを指定__ した場合は、__VSCode__ の __ウィンドウの幅__ を __変更__ すると、__ウィジェットの幅__ が __自動的に変更__ されます。実際に試してみて下さい。

__ウィジェット__ の __レイアウトの詳細__ に関しては、__下記のリンク先__ を __参照__ して下さい。

https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Layout.html

### `style`

__ウィジェット__ の __内部__ の __文字の色__ などの __スタイル__（style）の __設定__ を表す __属性__ です。__スタイル__ の __設定__ は、__スタイルの種類__ を表す __キー__ と __その値__ が __設定__ された __dict__ で行います。__設定__ できる __スタイル__ は、__ウィジェット__ の __種類ごと__ に __異なって__ おり、__Dropdown__ の場合は、__下記__ のプログラムのように、__`description_width`__ という __キー__ によって、__左に表示__ する __説明__ の __文字列__ の __表示幅__ を __設定できます__。

__下記__ は 、__`description_width`__ に __`"50px"`__ と __`"100px"`__ を __設定__ した場合で、__実行結果__ のように、__`description`__ で __表示__ した __`"number"`__ の __文字列__ の __表示幅__ が __変化__ します。

```python
dropdown8 = widgets.Dropdown(
    options=['1', '2', '3'],
    description="number",
    style= {
        "description_width": "50px",
    }
)
display(dropdown8)

dropdown9 = widgets.Dropdown(
    options=['1', '2', '3'],
    description="number",
    style= {
        "description_width": "100px",
    }
)
display(dropdown9)
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/358838b4-db57-7b0d-7357-adcad325b13d.png" width="300">

__ウィジェット__ の __スタイルの詳細__ に関しては、__下記__ の __リンク先__ を __参照__ して下さい。

https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Styling.html#the-style-attribute

# AI を選択する Dropdown の作成

__AI__ を __選択__ する __Dropdown を作成__ するためには、__どの AI__ を __Dropdown__ で __選択できる__ ようにするかを __決める必要__ があります。せっかくなので、__ルール 1__ から __ルール 14__ までの __すべての AI__ を __選択できる__ ようにすることにします。ただし、__`ai1`__ と __`ai1s`__ のように、__同じルール__ の __AI__ が __複数存在__ する場合は、__`s` がつく方__ を __選択できる__ ようにします。従って、__`ai1s`__ ~ __`ai14s`__ の __14 種類__ の __AI__ を __選択できる__ ようにします。

## AI の関数の list の作成

__Dropdown__ の __選択項目__ は、__`options` 属性__ に __list の形式__ で __代入する必要__ があるため、__その list__ を __作成__ します。__下記__ は、__AI__ の __関数の一覧__ を表す  __list__ を __作成__ するプログラムです。

```python
import ai

ai_list = [ ai.ai1s, ai.ai2s, ai.ai3s, ai.ai4s, ai.ai5s, ai.ai6s, ai.ai7s,
            ai.ai8s, ai.ai9s, ai.ai10s, ai.ai11s, ai.ai12s, ai.ai13s, ai.ai14s ]
```

:::note
__これまで__ のプログラムで __AI の関数__ を __利用する際__ に __記述__ した __方法__ で、__上記__ のプログラムを __下記__ のプログラムのように __記述__ することも __できます__ が、__記述__ が __かえって長くなる__ ので __上記__ のように __記述__ しました。

```python
from ai import ai1s, ai2s, ai3s, ai4s, ai5s, ai6s, ai7s, \
               ai8s, ai9s, ai10s, ai11s, ai12s, ai13s, ai14s

ai_list = [ ai1s, ai2s, ai3s, ai4s, ai5s, ai6s, ai7s,
            ai8s, ai9s, ai10s, ai11s, ai12s, ai13s, ai14s ]
```
:::

__上記__ のプログラムで __正しい list__ を __作成__ できていますが、__上記__ のプログラムは __記述が大変__ だと思いませんか？__AI__ の __関数の名前__ は __`ai`__ と __`s`__ の __間__ に __1 から 14 まで__ の __整数__ が __ 入る__ という、__規則正しい名前__ になっています。__そのこと__ と、__オブジェクトの中__ から __指定した名前__ を持つ __属性を計算__ する __`getattr`__ という __組み込み関数__ を __利用__ することで、__上記__ のプログラムを __簡潔に記述__ することが __できます__。

## `getattr` を利用した AI の関数の取得方法

__`getattr`__ は、[以前の記事](https://qiita.com/ysgeso/items/085fd15fbd91d5cdf770#%E5%A4%89%E6%95%B0%E3%81%AE%E5%80%A4%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%9F%E8%A6%81%E7%B4%A0%E3%81%AE%E5%8F%82%E7%85%A7)で __説明__ していますが、__これまでに作成__ した __marubatsu.py__ や __ai.py__ では __利用していない__ ので、__改めて説明__ します。

__`getattr`__ は、任意の __オブジェクト__ 対して __利用できる関数__ で、__1 つ目__ の __実引数__ に __オブジェクト__ を、__2 つ目__ の __実引数__ に __属性の名前__ を __記述__ することで、__指定した属性__ の __値__ を __返り値__ として __返す処理__ を行います。

__Python__ では、__インポート__ した __モジュール内__ で __定義__ された __変数__ や __関数など__ を、__`モジュール名.名前`__ のように __記述__ して __参照__ することが __できます__。__`.`__ を使って __記述している__ ことからわかるように、__モジュール__ の __変数__ や __関数__ などは、__インポート__ した __モジュール__ を表す __オブジェクトの属性__ として __管理__ されています。

従って、例えば __`ai.ai1s`__ は、__`ai`__ という __モジュール__ の __`"ai1s"`__ という __名前__ の __属性__ に __代入__ されているので、__下記__ のプログラムのように、__`getattr`__ を __利用__ することで、__`ai.ai1s`__ を __参照__ して、__特定の変数__ に __代入__ することが __できます__。

```python
ai1s = getattr(ai, "ai1s")
```

上記の __`ai1s`__ と __`ai.ai1s`__ には __同じ関数オブジェクト__ が __代入__ されているので、__下記__ のプログラムのように、__ゲーム開始時__ の __局面__ を表す __`mb`__ を __実引数__ に記述して __呼び出すこと__ で、どちらも __同じ着手__ が __選択__ されます。なお、__`ai1s`__ は、「__左上から順__ に __空いているマス__ を __探し__、__最初に見つかったマス__ に __着手__ する」という __AI__ なので、__(0, 0)__ が選択されます。

```python
from marubatsu import Marubatsu

mb = Marubatsu()

print(ai1s(mb))
print(ai.ai1s(mb))
```

実行結果

```
(0, 0)
(0, 0)
```

:::note
__list__ や __dict__ の場合は __`[]`__ を使って __要素の値__ を __参照__ することができますが、__オブジェクト__ の場合は __`[]`__ を使って __属性の値__ を __参照__ することは __できません__。__`getattr`__ は、__list__ や __dict__ における __`[]`__ と __同じ役割__ を果たすものだと __考えると良い__ でしょう。
:::

__`getattr`__ の __詳細__ については、下記のリンク先を参照して下さい。

https://docs.python.org/ja/3/library/functions.html#getattr

## `getattr` を利用した AI の関数の list の計算

先程説明したように、__AI__ の __関数の名前__ は、__`ai`__ と __`s`__ の __間__ に __1 から 14 まで__ の __数字が入る__ という、__規則正しい名前__ になっているので、__`getattr`__ を __利用__ することで、__AI__ の __関数の list__ を __作成__ するプログラムは、__下記__ のように __簡潔に記述__ できます。

* __1 行目__：__`ai_list`__ を __空の list__ で __初期化__ する
* __2 行目__：__1 から 14 ま__ での __整数__ の __繰り返し処理__ を行う
* __3 行目__：__AI__ の __関数の名前__ を __計算__ する
* __4 行目__：__`getattr`__ を使って __AI の関数__ を __取得__ し、__`ai_list`__ の __要素に追加__ する

```python
ai_list = []
for i in range(1, 15):
    ai_name = f"ai{i}s"
    ai_list.append(getattr(ai, ai_name))
```

:::note
少し __わかりづらい__ ので本記事では採用しませんが、__上記__ のプログラムは、__リスト内包表記__ を使って、__下記__ のように __記述__ することも __できます__。

```python
ai_list = [ getattr(ai, f"ai{i}s") for i in range(1, 15) ]
:::

## AI の一覧を表す Dropdown の作成

__AI の一覧__ を表す __Dropdown__ は、__下記__ のプログラムのように、__上記で作成__ した __`ai_list`__ 使って、__`Dropdown`__ の __実引数__ に __`options=ai_list`__ を __記述__ することで __作成__ できます。

```python
dropdown10 = widgets.Dropdown(options=ai_list)
display(dropdown10)
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/e8dec8e9-20ee-0250-15fe-364b6725b334.png" width="250"> 
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/42ed6c55-7e03-20d5-34ef-9ef4ef7b7177.png" width="250">

__上図左__ は __表示__ される __Dropdown__ で、__上図右__ は __Dropdown の上__ で __マウス__ を __押した場合__ に __表示__ される __ドロップダウンメニュー__ の __図__ です。__メニューに表示__ されるのは、__下記__ のプログラムのように、__関数__ を __`print` で表示__ した場合に __表示される内容__ です。

```python
print(ai.ai1s)
```

実行結果（at の後ろに表示される内容は、下記と異なる場合があります）

```
<function ai1s at 0x0000025E8600C860>
```

__上記__ のプログラムでは、__`options`__ に __Dropdown__ で __選択できる項目のみ__ を __要素__ として持つ __list__ を __代入__ しました。__先程__ の __`options`__ の所で __説明__ したように、__そのような場合__ は、__画面に表示__ されるそれぞれの __項目のラベル__ には、__項目__ を __文字列型__ のデータに __変換した値__ が __自動的に設定__ されます。__関数__ を __文字列__ のデータに __変換__ すると、__上記__ のような __表示__ になるので、__先程作成__ した __AI__ の __関数__ を __項目__ として持つ __Dropdown__ の __メニュー__ には、__先程の図__ のような __内容__ が __表示__ されます。

### AI の名前を表すラベルの設定

先程説明したように、__Dropdown__ では、__メニュー__ に __表示__ する __文字列__ を __ラベル__ と呼び、__`options`__ の __要素__ に __下記__ のような __tuple を代入__ することで、__ラベル__ に __項目__ とは __別の文字列__ を __設定__ することが __できます__。

```python
(ラベルを表す文字列, 項目の値)
```

従って、例えば、__`ai1s`__、__`ai2s`__、__`ai3s`__ の __3 つ__ の __AI の名前__ を __ラベル__ にした __Dropdown__ は、__下記__ のプログラムで作成できます。

```python
dropdown11 = widgets.Dropdown(
    options=[("ai1s", ai.ai1s), ("ai2s", ai.ai2s), ("ai3s", ai.ai3s)]
)
display(dropdown11)
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/bd230072-1d50-3c25-a8e2-48d061844a58.png" width="250"> 
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/0e806048-fd8b-79f0-7e24-2ae1c2895f23.png" width="250">

__上図左__ は __表示__ される __Dropdown__ で、__上図右__ は __Dropdown の上__ で __マウスを押した__ 場合に __表示__ される __メニューの図__ です。__メニューに表示__ されるのは __`"ai1s"`__ という __ラベル__ ですが、__このラベル__ を __選択した際__ に __Dropdown__ が __実際__ に __選択する項目__ は、__`ai.ai1s`__ という __関数__ になります。そのことは、__Dropdown__ で __ai1__ を __選択した後__ で、__下記__ のプログラムで __確認__ することが __できます__。先ほど説明したように、__Dropdown__ の __`value` 属性__ には、__選択中__ の __項目の値__ が __代入__ されています。そのため、__ai1s__ を選択すると、__`dropdown11.value`__ には、__`ai.ai1s`__ という __AI の関数__ が代入され、__`dropdown11.value(mb)`__ を __実行__ すると、__`ai1s`__ が __着手を選択__ し、__先程と同様__ に __(0, 0)__ が __選択__ されます。

```python
print(dropdown11.value(mb))
```

実行結果

```
(0, 0)
```

また、__下記__ のプログラムのように、__`dropdown11.value`__ を __表示__ することでも、__`ai.ai1s`__ という __関数__ が __代入されている__ ことを __確認__ できます。


```python
print(dropdown11.value)
```

実行結果（at の後ろに表示される内容は、下記と異なる場合があります）

```
<function ai1s at 0x0000025E8600C860>
```

## ラベルに AI の名前を設定した Dropdown の作成

__上記__ で説明した __方法__ を __利用__ することで、__ラベル__ に __AI の名前__ を __設定__ した __AI の一覧__ を表す __Dropdown__ を、__下記__ のプログラムで __作成__ することが __できます__。


```python
ai_list = []
for i in range(1, 15):
    ai_name = f"ai{i}s"  
    ai_list.append((ai_name, getattr(ai, ai_name)))

dropdown12 = widgets.Dropdown(options=ai_list)
display(dropdown12)
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/bd230072-1d50-3c25-a8e2-48d061844a58.png" width="250">　<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/31feb72e-6598-0753-eafe-4f6cf9057ef6.png" width="250">

__上図左__ は __表示__ される __Dropdown__ で、__上図右__ は __Dropdown の上__ で __マウス__ を __押した場合__ に __表示__ される __ドロップダウンメニュー__ の __図__ で、__意図通り__ の __Dropdown__ が __作成__ されます。

## 〇 と × の AI を選択する Dropdown の作成

次に、__〇__ と __×__ の __それぞれの AI__ を __選択__ する __Dropdown__ を __作成__ します。また、__それぞれ__ の __Dropdown__ が __どちらの手番__ を __担当する__ かを、__Dropdown__ の __`description`__ 属性を使って __区別__ することにします。__下記__ は、__2 つ__ の __Dropdown__ を __作成__ するプログラムです。

```python
dropdown_circle = widgets.Dropdown(
    options=ai_list,
    description="〇",
)
display(dropdown_circle)

dropdown_cross = widgets.Dropdown(
    options=ai_list,
    description="×",
)
display(dropdown_cross)
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/b8ee2134-856f-3abe-1452-ad64e6c0a444.png" width="250">

__実行結果__ からわかるように、__作成__ された __Dropdown__ は、__2 行__ に __分けて表示__ されます。また、__2 つ__ の __Dropdown__ は __左端の位置__ が __微妙に揃わない__ という __問題__ が生じています。

# ipywidgets のウィジェットの配置のレイアウト

__`display`__ を使って __複数__ の __ipywidgets__ を __表示__ すると、__上記__ のように、作成された __ウィジェット__ が __思い通り__ に __表示されない__ という __問題__ が __生じます__。__この問題__ を __解決__ するために、__ipywidgets__ には、作成した __ウィジェット__ の __配置__ の __レイアウト__ を __設定__ するための __`HBox`__ と __`VBox`__ という __ウィジェット__ が __用意__ されています。

__`HBox`__ と __`VBox`__ の __詳細__ については、下記のリンク先を参照して下さい。

https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Layout.html#the-vbox-and-hbox-helpers

:::note
__ipywidgets__ には __`HBox`__ と __`VBox` 以外__ にもウィジェットの __配置__ の __レイアウト__ を __設定__ するための __ウィジェット__ が __用意__ されており、__詳細__ な __レイアウトの設定__ を行うことが __できます__。興味がある方は下記のリンク先を参照して下さい。
:::

https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Layout.html#

## HBox と VBox の使い方

__HBox__ は __ウィジェット__ を __水平__（horizontal）方向に __並べて配置__ するための __ウィジェット__ で、__下記__ のプログラムのように、__ipywidgets__ の __`HBox`__ の __実引数__ に、__横方向__ に __並べて配置__ する __ウィジェット__ を __要素__ として持つ __list を記述__ することで __作成__ します。__作成__ した __HBox__ を __`display` で表示__ することで、__HBox__ に __登録__ した __ウィジェット__ が __実行結果__ のように、__横方向__ に __並べて表示__ されるようになります。

```python
hbox = widgets.HBox([dropdown_circle, dropdown_cross])
display(hbox)
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/0510db42-aaed-e374-3aaf-ac44fcfddf5e.png" width="500">

__VBox__ は __ウィジェット__ を __垂直__（vertical）方向に __並べて配置__ するための __ウィジェット__ で、__下記__ のプログラムのように、__ipywidgets__ の __`VBox`__ の __実引数__ に、__縦方向__ に __並べて配置__ する __ウィジェット__ を __要素__ として持つ __list を記述__ することで __作成__ します。__作成__ した __VBox__ を __`display` で表示__ することで、__VBox__ に __登録__ した __ウィジェット__ が __実行結果__ のように、__縦方向__ に __並べて表示__ されるようになります。

```python
vbox = widgets.VBox([dropdown_circle, dropdown_cross])
display(vbox)
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/4a432fdc-81fe-3e54-7ff3-ea3ec7d4790e.png" width="250">

今回の記事では行いませんが、__`HBox`__ や __`VBox`__ の __実引数__ に記述する __list の要素__ には、__任意__ の __ウィジェット__ を __代入__ することが __できます__。__`HBox`__ や __`VBox`__ も __ウィジェットの一種な__ ので、__下記__ のプログラムのように、__HBox 内__ で __横__ に __2 つ並べた Dropdown__ を __VBox__ で __縦に並べて表示__ するようなことも __できます__。

```python
vbox = widgets.VBox([hbox, hbox])
display(vbox)
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/496f5747-addd-67f0-2517-e7adf98c9f4e.png" width="500">

わかりづらいと思いますので、__上記__ の __実行結果__ の中の __ウィジェット__ が __どのような構成__ で __配置__ されているかを __下図__ で示します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/3e172cc3-3f48-dd82-26b0-b36fb2e31daa.png" width="500">

## `play` メソッドの修正

__AI__ を __選択__ するための __Dropdown__ の __作成__ と __配置方法__ がわかったので、__下記__ のプログラムのように、__`play`__ メソッドを __実行した際__ に、それらの __Dropdown を作成__ し、__HBox__ で __横に並べて配置__ するように __修正__ します。その際に、__リセットボタン__ を __同じ HBox__ の __中に入れる__ ことで、__Dropdown__ の __右に配置__ するようにしてみました。

なお、__`play`__ メソッドの __仮引数__ の __名前__ に __`ai`__ が __存在する__ ので、__ai モジュール__ と __名前__ が __かぶっています__。そのため、__`import ai`__ で __ai モジュール__ を __インポート__ すると、__`play`__ メソッドの __ブロックの中__ で __ai モジュール__ を __利用__ することが __できません__。そこで、__ai モジュール__ を __インポートする際__ に、__`ai_module`__ という __別の名前__ で __インポート__ しました。

* __4 行目__：__ai__ モジュールを __`ai_module`__ という __名前__ で __インポート__ する
* __12 ~ 15 行目__：__Dropdown__ の __`options`__ 属性の __list__ を __作成__ する
* __18 ~ 25 行目__：__〇__ と __×__ の __AI を選択__ する __Dropdown__ を __作成__ する
* __31 行目__：__〇__ と __×__ の __Dropdown__ と __リセットボタン__ を __横に配置__ する __HBox__ を __作成__ する
* __32 行目__：__`display`__ で __HBox を表示__ する

```python
 1  import matplotlib.pyplot as plt
 2  import ipywidgets as widgets
 3  import math
 4  import ai as ai_module
 5
 6  def play(self, ai, params=[{}, {}], verbose=True, seed=None, gui=False, size=3):
 7      # seed が None でない場合は、seed を乱数の種として設定する
 8      if seed is not None:
 9          random.seed(seed)
10
11      # Dropdown の作成の際に必要となる、AI のリストを作成する    
12      ai_list = []
13      for i in range(1, 15):
14          ai_name = f"ai{i}s"  
15          ai_list.append((ai_name, getattr(ai_module, ai_name)))    
16     
17      # 〇 と × の Dropdown を作成する   
18      dropdown_circle = widgets.Dropdown(
19          options=ai_list,
20          description="〇",
21      )
22      dropdown_cross = widgets.Dropdown(
23          options=ai_list,
24          description="×",
25      )       
26    
27      # リセットボタンを作成する
28      button = widgets.Button(description="リセット")
29    
30      # 〇 と × の dropdown と リセットボタンを横に配置した HBox を作成し、表示する
31      hbox = widgets.HBox([dropdown_circle, dropdown_cross, button])
32      display(hbox)
元と同じなので省略
33
34  Marubatsu.play = play
```

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

```python
from marubatsu import Marubatsu
import matplotlib.pyplot as plt
import ipywidgets as widgets
import math
import ai as ai_module

def play(self, ai, params=[{}, {}], verbose=True, seed=None, gui=False, size=3):
    # seed が None でない場合は、seed を乱数の種として設定する
    if seed is not None:
        random.seed(seed)

    # Dropdown の作成の際に必要となる、AI のリストを作成する    
    ai_list = []
    for i in range(1, 15):
        ai_name = f"ai{i}s"  
        ai_list.append((ai_name, getattr(ai_module, ai_name)))    
     
    # 〇 と × の Dropdown を作成する   
    dropdown_circle = widgets.Dropdown(
        options=ai_list,
        description="〇",
    )
    dropdown_cross = widgets.Dropdown(
        options=ai_list,
        description="×",
    )       
    
    # リセットボタンを作成する
    button = widgets.Button(description="リセット")
    
    # 〇 と × の dropdown と リセットボタンを横に配置した HBox を作成し、表示する
    hbox = widgets.HBox([dropdown_circle, dropdown_cross, button])
    display(hbox)
    
    # リセットボタンのイベントハンドラを定義する
    def on_button_clicked(b):
        self.restart()
        self.play_loop(ai=ai, ax=ax, params=params, verbose=verbose, gui=gui)           
         
    # イベントハンドラをリセットボタンに結びつける
    button.on_click(on_button_clicked)

    # gui が True の場合に、ゲーム盤を描画する画像を作成し、イベントハンドラに結びつける
    if gui:
        fig, ax = plt.subplots(figsize=[size, size])
        fig.canvas.toolbar_visible = False
        fig.canvas.header_visible = False
        fig.canvas.footer_visible = False
        fig.canvas.resizable = False          
        
        # ローカル関数としてイベントハンドラを定義する
        def on_mouse_down(event):
            # Axes の上でマウスを押していた場合のみ処理を行う
            if event.inaxes and self.status == Marubatsu.PLAYING:
                x = math.floor(event.xdata)
                y = math.floor(event.ydata)
                self.move(x, y)                
                self.draw_board(ax)

                # 次の手番の処理を行うメソッドを呼び出す
                self.play_loop(ai=ai, ax=ax, params=params, verbose=verbose, gui=gui)
                   
        # fig の画像にマウスを押した際のイベントハンドラを結び付ける
        fig.canvas.mpl_connect("button_press_event", on_mouse_down)     

    self.restart()
    return self.play_loop(ai=ai, ax=ax, params=params, verbose=verbose, gui=gui)
    
Marubatsu.play = play
```
</details>

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

```diff_python
from marubatsu import Marubatsu
import matplotlib.pyplot as plt
import ipywidgets as widgets
import math
import ai as ai_module

def play(self, ai, params=[{}, {}], verbose=True, seed=None, gui=False, size=3):
    # seed が None でない場合は、seed を乱数の種として設定する
    if seed is not None:
        random.seed(seed)

    # Dropdown の作成の際に必要となる、AI のリストを作成する    
+   ai_list = []
+   for i in range(1, 15):
+       ai_name = f"ai{i}s"  
+       ai_list.append((ai_name, getattr(ai_module, ai_name)))    
     
    # 〇 と × の Dropdown を作成する   
+   dropdown_circle = widgets.Dropdown(
+       options=ai_list,
+       description="〇",
+   )
+   dropdown_cross = widgets.Dropdown(
+       options=ai_list,
+       description="×",
+   )       
    
    # リセットボタンを作成する
    button = widgets.Button(description="リセット")
    
    # 〇 と × の dropdown と リセットボタンを横に配置した HBox を作成し、表示する
+   hbox = widgets.HBox([dropdown_circle, dropdown_cross, button])
+   display(hbox)
元と同じなので省略
    
Marubatsu.play = play
```
</details>

__上記__ の __修正後__ に、__下記__ のプログラムで __`play`__ メソッドを __実行__ すると、__実行結果__ のように __2 つ__ の __Dropdown__ と __リセットボタン__ が __横一列に配置__ されて __表示__ されるようになります。

```python
%matplotlib widget

mb = Marubatsu()
mb.play(ai=[None, None], gui=True);
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/53b2809e-0f6e-b830-fba2-aef1409c0589.png" width="500">

## レイアウトとスタイルの調整

__実行結果__ から、__Dropdown__ の「__説明__ の __幅__ が __長すぎる__」、「__項目__ の __横幅__ が __長すぎる__」、__リセットボタン__ の「__横幅__ が __長すぎる__」の __3 点__ が __気になる__ ので、先程説明した __ウィジェット__ の __`layout`__ と __`style`__ 属性を使って __Dropdown__ と __ボタン__ の __表示を調整__ します。

* __6、12、19 行目__：__Dropdown__ と __ボタン__ の __横幅__ を __100 ピクセル__ に __設定__ する
* __7、13 行目__：__Dropdown__ の __左の説明__ の __横幅__ を __20 ピクセル__ に __設定__ する

```python
 1  def play(self, ai, params=[{}, {}], verbose=True, seed=None, gui=False, size=3):
元と同じなので省略
 2      # 〇 と × の Dropdown を作成する   
 3      dropdown_circle = widgets.Dropdown(
 4          options=ai_list,
 5          description="〇",
 6          layout=widgets.Layout(width="100px"),
 7          style={"description_width": "20px"},
 8      )
 9      dropdown_cross = widgets.Dropdown(
10          options=ai_list,
11          description="×",
12          layout=widgets.Layout(width="100px"),
13          style={"description_width": "20px"},
14      )       
15   
16      # リセットボタンを作成する
17      button = widgets.Button(
18          description="リセット",
19          layout=widgets.Layout(width="100px"),
20      )
元と同じなので省略   
21    
22  Marubatsu.play = play
```

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

```python
def play(self, ai, params=[{}, {}], verbose=True, seed=None, gui=False, size=3):
    # seed が None でない場合は、seed を乱数の種として設定する
    if seed is not None:
        random.seed(seed)

    # Dropdown の作成の際に必要となる、AI のリストを作成する    
    ai_list = []
    for i in range(1, 15):
        ai_name = f"ai{i}s"  
        ai_list.append((ai_name, getattr(ai_module, ai_name)))    
     
    # 〇 と × の Dropdown を作成する   
    dropdown_circle = widgets.Dropdown(
        options=ai_list,
        description="〇",
        layout=widgets.Layout(width="100px"),
        style={"description_width": "20px"},
    )
    dropdown_cross = widgets.Dropdown(
        options=ai_list,
        description="×",
        layout=widgets.Layout(width="100px"),
        style={"description_width": "20px"},
    )       
    
    # リセットボタンを作成する
    button = widgets.Button(
        description="リセット",
        layout=widgets.Layout(width="100px"),
    )
    
    # 〇 と × の dropdown と リセットボタンを横に配置した HBox を作成し、表示する
    hbox = widgets.HBox([dropdown_circle, dropdown_cross, button])
    display(hbox)
    
    # リセットボタンのイベントハンドラを定義する
    def on_button_clicked(b):
        self.restart()
        self.play_loop(ai=ai, ax=ax, params=params, verbose=verbose, gui=gui)           
         
    # イベントハンドラをリセットボタンに結びつける
    button.on_click(on_button_clicked)

    # gui が True の場合に、ゲーム盤を描画する画像を作成し、イベントハンドラに結びつける
    if gui:
        fig, ax = plt.subplots(figsize=[size, size])
        fig.canvas.toolbar_visible = False
        fig.canvas.header_visible = False
        fig.canvas.footer_visible = False
        fig.canvas.resizable = False          
        
        # ローカル関数としてイベントハンドラを定義する
        def on_mouse_down(event):
            # Axes の上でマウスを押していた場合のみ処理を行う
            if event.inaxes and self.status == Marubatsu.PLAYING:
                x = math.floor(event.xdata)
                y = math.floor(event.ydata)
                self.move(x, y)                
                self.draw_board(ax)

                # 次の手番の処理を行うメソッドを呼び出す
                self.play_loop(ai=ai, ax=ax, params=params, verbose=verbose, gui=gui)
                   
        # fig の画像にマウスを押した際のイベントハンドラを結び付ける
        fig.canvas.mpl_connect("button_press_event", on_mouse_down)     

    self.restart()
    return self.play_loop(ai=ai, ax=ax, params=params, verbose=verbose, gui=gui)
    
Marubatsu.play = play
```
</details>

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

```diff_python
def play(self, ai, params=[{}, {}], verbose=True, seed=None, gui=False, size=3):
元と同じなので省略
    # 〇 と × の Dropdown を作成する   
    dropdown_circle = widgets.Dropdown(
        options=ai_list,
        description="〇",
+       layout=widgets.Layout(width="100px"),
+       style={"description_width": "20px"},
    )
    dropdown_cross = widgets.Dropdown(
        options=ai_list,
        description="×",
+       layout=widgets.Layout(width="100px"),
+       style={"description_width": "20px"},
    )       
    
    # リセットボタンを作成する
    button = widgets.Button(
        description="リセット",
+       layout=widgets.Layout(width="100px"),
    )
元と同じなので省略   
    
Marubatsu.play = play
```
</details>

__上記__ の __修正後__ に、__下記__ のプログラムで __`play`__ メソッドを __実行__ すると、__実行結果__ のような __表示__ が行われます。__上記__ のプログラムは __試行錯誤__ の __結果__、__ちょうど良い__ と __筆者が感じた設定__ です。人によってデザインの感覚は異なると思いますので、__もっと良い設定__ があれば、__自由に変更__ して下さい。

```python
mb.play(ai=[None, None], gui=True);
```

実行結果（下図は、画像なので操作することはできません）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/a603f024-00e1-a74e-25b9-5c18d8fb274e.png" width="250">

# 今回の記事のまとめ

今回の記事では、__ipywidgets__ の __Dropdown__ の __使い方__ を説明し、__〇×ゲーム__ の __ゲーム盤の上__ に __AI__ を __選択するため__ の __Dropdown__ を __表示__ しました。

ただし、今回のプログラムでは、__Dropdown__ を __表示しただけ__ なので、Dropdown で __AI を選択__ しても __AI が変更される__ ことは __ありません__。その処理は次回の記事で実装します。

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

以下のリンクから、本記事で入力して実行した JupyterLab のファイルを見ることができます。

https://github.com/ysgeso/marubatsu/blob/master/074/marubatsu.ipynb

以下のリンクは、今回の記事で更新した marubatsu.py です。

https://github.com/ysgeso/marubatsu/blob/master/074/marubatsu_new.py

# 次回の記事

https://qiita.com/ysgeso/items/64de676735836b963712

[^1]:__文字の表示__ を __行うだけ__ で、__操作を行わない__ ような __ウィジェット__ が __存在します__