Pythonでドミニオン戦略解析 〜鍛冶屋ステロ編 その2〜

#はじめに
船着場ステロと言ったな、あれは嘘だ。
昨日書いた記事を見返していたらあまりにも酷いなぁと思ったのでその2です。
~~誰だあんな記事書いたの~~
自己紹介やドミニオンについては前回の記事をお読みください。
[Pythonでドミニオン戦略解析 〜鍛冶屋ステロ編〜][1]
[1]:https://qiita.com/NCT48/items/7b20434f0948246dcbf3

#改めて
使用するのは前回のデータに改良を加えたものです。
出力内容は
・ID
・属州4枚買うまでのターン(Turn)
・各ターンの手札内容(Hund)
__・各ターンカードを購入する前の財宝(Money)__
・各ターン使用しなかった財宝(UselessMoney)
__・デッキシャッフルの回数(Refresh)__
です。
__太字の箇所__が新たに追加したデータです。
今回は、しっかりと全てのデータを解析に生かすことができました。

#改善点
1.コードにミスがあったので、修正しました。
2.データの種類を増やし、より解析の幅を広げました。

#環境
前回と変わらずです。
Windows10
Jupyter Notebook
Python3
NumPy
Pandas
Matplotlib

#本題

```python:Smith.ipynb
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

smith_data = pd.read_csv('./TestProgramSmith.csv', header=0, index_col=0)
smith_data.head()
```

|  | Turn | Hund | Money | UselessMoney | Refresh |
|--:|--:|:--|:--|:--|:--|
| 0 | 13 | 屋銅銅銅銅/屋屋銅銅銅/屋銅銅銅銅/屋屋銀銅銅銅銅鍛/屋銀銅銅銅/屋銀銅銅銅/屋金銀銅銅/屋... | 4/3/4/6/5/5/7/10/6/8/5/10/9 | 0/0/1/0/2/2/1/2/0/0/2/2/1 | 0/0/1/2/2/3/3/4/4/5/5/5/6 |
| 1 | 15 | 屋屋銅銅銅/屋銅銅銅銅/銀銅銅銅銅/屋屋屋銅銅銅銅鍛/金銀銅銅銅/屋金銅銅銅/屋屋金銅銅/銀... | 3/4/6/4/8/6/5/10/3/10/8/4/3/7/12 | 0/0/0/1/2/0/2/2/0/2/0/1/0/1/4 | 0/0/1/2/2/3/3/4/4/5/5/5/6/6/6 |
| 2 | 15 | 屋屋屋銅銅/銅銅銅銅銅/屋銅銅銅銅銅銅鍛/屋屋銅銅銅/屋金銀銅銅銅銅鍛/屋屋銅銅銅/屋銀銀銅... | 2/5/6/3/9/3/6/9/4/7/9/6/7/8/9 | 2/1/0/0/3/0/0/1/1/1/1/0/1/0/1 | 0/0/1/2/3/3/4/4/5/5/6/6/6/7/7 |
| 3 | 16 | 屋銅銅銅銅/屋屋銅銅銅/屋屋銀銅銅/屋銀銅銅銅銅銅鍛/屋屋銀銅銅/屋屋銀銀銅/屋金銅銅銅/屋... | 4/3/4/7/4/5/6/9/5/8/6/5/11/6/5/8 | 0/0/1/1/1/2/0/1/2/0/0/2/3/0/2/0 | 0/0/1/2/2/3/3/4/4/5/5/5/6/6/6/7 |
| 4 | 14 | 屋屋屋銅銅/銅銅銅銅銅/屋屋銅銅銅銅銅鍛/屋屋屋銅銅/屋銀銅銅銅銅銅鍛/屋屋銅銅銅/金銅銅銅... | 2/5/5/2/7/3/7/8/7/8/6/6/11/10 | 2/1/2/2/1/0/1/0/1/0/0/0/3/2 | 0/0/1/2/3/4/4/5/5/6/6/6/7/7 |

このあたりは前回とは変わっていませんが、データ種が増えたので出力は変わってます。

---

```python:Smith.ipynb
smith_data_des = smith_data["Turn"].describe()
print(smith_data_des)
```

```text:smith_data_des
count    50000.000000
mean        14.996700
std          1.716866
min          9.000000
25%         14.000000
50%         15.000000
75%         16.000000
max         23.000000
Name: Turn, dtype: float64
```

普通、細かいデータを語る前に全体のデータを語るべきですよね。
鍛冶屋ステロは平均15.0ターン、標準偏差は1.7です。
15ターンはそこそこ早い方だと思います。
標準偏差は・・・こんなもんですかね？正直わからないです。
(他にも色々データを取ってみてから調べ直します。)

---

```python:Smith.ipynb
Bins = smith_data["Turn"].max() - smith_data["Turn"].min() + 1
Range = (smith_data["Turn"].min()-0.5, smith_data["Turn"].max()+0.5)
```

なんと、自動でグラフの範囲を設定できるようになりました！
~~初めからやっておけって感じです。~~

---

```python:Smith.ipynb
plt.hist(smith_data["Turn"], bins=Bins, range=Range, density=True, rwidth=0.5)
```

![Unknown.png](https://qiita-image-store.s3.amazonaws.com/0/311955/88cfcf8d-fc6c-c80e-5240-fd02db9eb87a.png)

綺麗な正規分布ですねぇ...
戦略に置いて大事な安定性の観点から見ると、良いことですね。

---

```python:Smith.ipynb
#smith_data34 = smith_data[smith_data["Hund"].str.startswith("屋屋銅銅銅/", "屋銅銅銅銅/")]
#smith_data25 = smith_data[smith_data["Hund"].str.startswith("銅銅銅銅銅/", "屋屋屋銅銅/")]
smith_data34 = smith_data[smith_data["Hund"].str.contains("^(?:屋屋銅銅銅/|屋銅銅銅銅/)")]
smith_data25 = smith_data[smith_data["Hund"].str.contains("^(?:屋屋屋銅銅/|銅銅銅銅銅/)")]

smith_data34_des = smith_data34["Turn"].describe()
smith_data25_des = smith_data25["Turn"].describe()

smith_data25_des, smith_data34_des
```

```text:smith_data25_des, smith_data34_des
(count    8358.000000
 mean       15.809285
 std         1.691966
 min        11.000000
 25%        15.000000
 50%        16.000000
 75%        17.000000
 max        22.000000
 Name: Turn, dtype: float64, 
 count    41642.000000
 mean        14.833605
 std          1.674993
 min          9.000000
 25%         14.000000
 50%         15.000000
 75%         16.000000
 max         23.000000
 Name: Turn, dtype: float64)
```

前回の僕は何をしてたんですかね？思い切りデータを拾い損ねてました。お恥ずかしい。
今回はstr.containsを使用して正規表現で拾っています。
countの合計も50000になっています。~~当たり前か。~~

---


```python:Smith.ipynb
plt.hist([smith_data25["Turn"],smith_data34["Turn"]],bins=Bins, range=Range, density=True, rwidth=0.5)
```

![Unknown-1.png](https://qiita-image-store.s3.amazonaws.com/0/311955/5901954e-eec2-aaf4-69d0-a4b0e4e2e2ca.png)

前回とほぼ同じグラフなので言うことはないですかね。
3-4の入りの方が速度が上なのは、デッキ2週目に銀貨がいない(鍛冶屋を買っている)→デッキの金量が低いからではないかと思います。


---

```python:Smith.ipynb
smithTry = smith_data['Turn'].quantile([0,0.33,0.66,1]).tolist()
try_cat = ['速','中','遅']
smith_data['TurnLabel'] = pd.cut(smith_data['Turn'], smithTry, labels=try_cat, include_lowest=True, right=True)
```

(底沈みに関しては前回と同じなので省略します。)
新しく同じ入り方や沈み方の時、速度の差はどこに出るのか調べてみました。
終了ターンを「'速','中','遅'」の3段階(三分位数)に分けてラベルを貼ります。

---

```python:Smith.ipynb
smith_dataF = smith_data[smith_data["TurnLabel"]=="速"]
smith_dataM = smith_data[smith_data["TurnLabel"]=="中"]
smith_dataS = smith_data[smith_data["TurnLabel"]=="遅"]

print(smith_dataF["Hund"].str.count("鍛").describe())
print(smith_dataS["Hund"].str.count("鍛").describe())
```

```text:smith_dataF,smith_dataS
count    19973.000000
mean         4.082561
std          0.835356
min          2.000000
25%          4.000000
50%          4.000000
75%          5.000000
max          7.000000
Name: Hund, dtype: float64
count    9004.000000
mean        4.597290
std         0.856451
min         3.000000
25%         4.000000
50%         5.000000
75%         5.000000
max         8.000000
Name: Hund, dtype: float64
```


「アクションをたくさん打ったら速度が上がるではないか？(効率よくデッキを回せている)」と仮定していたのですが、特に差は出ませんでした。
考えたら「速度が遅い→たくさんターンが回ってくる→たくさんアクションを打てる」ので、差が出なくて当たり前です。

--

```python:Smith.ipynb
smith_data_haya = smith_data[smith_data["Turn"] == 11]
smith_data_haya.head(5)

smith_data_oso = smith_data[smith_data["Turn"] == 19]
smith_data_oso.head(5)
```

|  | Turn | Hund | Money | UselessMoney | Refresh | TurnLabel |
|--:|--:|:--|:--|:--|:--|:--|
| 116 | 11 | 屋銅銅銅銅/屋屋銅銅銅/屋銀銅銅銅銅銅鍛/屋屋銅銅銅/屋金銀銅銅/屋銀銀銅銅銅銅鍛/屋金金銅... | 4/3/7/3/7/8/8/5/9/4/8 | 0/0/1/0/1/0/0/2/1/1/0 | 0/0/1/2/2/3/3/4/4/5/5 | 速 |
| 228 | 11 | 屋銅銅銅銅/屋屋銅銅銅/屋屋銀銅銅銅銅鍛/屋金銅銅銅/屋屋銀銅銅/屋金銅銅銅銅銅鍛/屋屋金銀... | 4/3/6/6/4/8/7/8/8/4/10 | 0/0/0/0/1/0/1/0/0/1/2 | 0/0/1/2/2/3/3/4/4/5/5 | 速 |
| 266 | 11 | 屋銅銅銅銅/屋屋銅銅銅/屋屋銀銅銅銅銅鍛/屋銀銅銅銅/屋屋金銅銅銅銅鍛/屋金銅銅銅銅銅鍛/屋... | 4/3/6/5/7/8/7/8/4/8/8 | 0/0/0/2/1/0/1/0/1/0/0 | 0/0/1/2/2/3/3/4/4/5/5 | 速 |
| 312 | 11 | 屋銅銅銅銅/屋屋銅銅銅/屋銀銅銅銅銅銅鍛/屋屋銅銅銅/屋金銀銅銅/屋屋屋銅銅銅銅鍛/金銀銀銅... | 4/3/7/3/7/4/9/8/6/8/9 | 0/0/1/0/1/1/1/0/0/0/1 | 0/0/1/2/2/3/3/4/4/5/5 | 速 |
| 327 | 11 | 屋銅銅銅銅/屋屋銅銅銅/屋屋銀銅銅銅銅鍛/屋屋屋銅銅銅銅鍛/金銀銅銅銅/屋屋銀銅銅/屋金金銀... | 4/3/6/4/8/4/11/4/8/8/9 | 0/0/0/1/2/1/3/1/0/0/1 | 0/0/1/2/2/3/3/4/4/5/5 | 速 |


|  | Turn | Hund | Money | UselessMoney | Refresh | TurnLabel |
|--:|--:|:--|:--|:--|:--|:--|
| 16 | 19 | 屋屋銅銅銅/屋銅銅銅銅/屋屋銅銅銅銅銅鍛/屋銀銅銅銅/屋屋銀銅銅銅銅鍛/屋銀銅銅銅/屋金銀銅... | 3/4/5/5/6/5/7/6/8/12/5/5/7/11/6/7/7/6/8 | 0/0/2/2/0/2/1/0/0/4/2/2/1/3/0/1/1/0/0 | 0/0/1/2/2/3/3/4/4/5/5/5/6/6/6/7/7/7/7 | 遅 |
| 80 | 19 | 銅銅銅銅銅/屋屋屋銅銅/屋屋屋銅銅銅銅鍛/屋屋銅銅銅/屋屋銀銅銅銅銅鍛/屋銀銅銅銅/金銀銀銅... | 5/2/4/3/6/5/9/5/5/11/7/6/7/10/7/7/10/7/10 | 1/2/1/0/0/2/3/2/2/3/1/0/1/2/1/1/2/1/2 | 0/0/1/2/3/3/4/4/5/5/6/6/6/7/7/7/8/8/8 | 遅 |
| 85 | 19 | 銅銅銅銅銅/屋屋屋銅銅/屋屋銅銅銅/屋屋銅銅銅銅銅鍛/屋銀銅銅銅/屋銀銅銅銅/屋銀銅銅銅銅銅... | 5/2/3/5/5/5/7/6/5/8/5/11/7/5/7/12/5/6/11 | 1/2/0/2/2/2/1/0/2/0/2/3/1/2/1/4/2/0/3 | 0/0/1/2/3/3/4/4/5/5/5/6/6/6/7/7/7/7/8 | 遅 |
| 142 | 19 | 屋銅銅銅銅/屋屋銅銅銅/屋屋銅銅銅銅銅鍛/屋銀銅銅銅/屋屋銀銅銅/銀銀銀銅銅銅銅鍛/屋屋銅銅... | 4/3/5/5/4/10/3/7/9/5/7/9/5/4/15/4/7/7/8 | 0/0/2/2/1/4/0/1/1/2/1/1/2/1/7/1/1/1/0 | 0/0/1/2/2/3/3/4/4/5/5/5/6/6/6/7/7/7/7 | 遅 |
| 171 | 19 | 屋銅銅銅銅/屋屋銅銅銅/屋屋銀銅銅/屋銅銅銅銅/屋屋銅銅銅銅銅鍛/屋銀銀銅銅/屋金銀銅銅/屋... | 4/3/4/4/5/6/7/9/5/5/11/9/7/5/7/5/6/6/15 | 0/0/1/1/2/0/1/1/2/2/3/1/1/2/1/2/0/0/7 | 0/0/1/1/2/2/3/3/4/4/4/5/5/5/6/6/6/6/7 | 遅 |

前述の仮定が外れたので、とりあえずデータを並べてみました。
[速いデータ]はUselessMoneyが少なく、[遅いデータ]はUselessMoneyが多いのが見えます。
余らせてしまったお金は持ちこせず捨て札になってしまいます。
デッキの総金量は一定なので、それ以降思ったように金量が増えず速度の低下を招いているようです。これは当たり前のことかなと。
しかし中には、UselessMoneyが多いのに[速いデータ]や、UselessMoneyが少ないのに[遅いデータ]もあります。

---

UselessMoneyが多いのに早かった327回目と、UselessMoneyが少ないのに遅かった171回目の、MoneyとUselessMoneyとRefreshを取り出してみました。

| |327 | 171 |
|:--|:--|:--|
|Money| 4/3/6/4/8/4/11/4/8/8/9 | 4/3/4/4/5/6/7/9/5/5/11/9/7/5/7/5/6/6/15 |
| UselessMoney |0/0/0/1/2/1/3/1/0/0/1 | 0/0/1/1/2/0/1/1/2/2/3/1/1/2/1/2/0/0/7 |
| Refresh |0/0/1/2/2/3/3/4/4/5/5 | 0/0/1/1/2/2/3/3/4/4/4/5/5/5/6/6/6/6/7

327回目の7ターン目に3金の無駄が出ています。
しかし、その次のターンにRefreshが1増えています。
つまりこの7ターン目と8ターン目の間にデッキシャッフルが発生しています。
デッキシャッフルが発生した場合、無駄に使ってしまったお金は次のデッキに入っていきます。
よってデッキの送金量は減らないんですね。

171回目の後半にMoneyが7となっているターンが何度かあります。
俗に「7金病」と呼ばれていて、「あと1金あれば属州が買えるのに！」と悔しさを滲ませるものです。
今回は戦術が単純化されているのでこの局面でも金貨を買っています。(普通は終盤は1つ下の勝利点である「公領カード(5円,3点)」を買う。)
なので無駄金が1と少なく見えますが、勝利点にすると3点の損失です。

---

```python:Smith.ipynb
smith_data25_haya = smith_data25[smith_data25["Turn"] == 11]
smith_data25_haya.head(5)

smith_data34_oso = smith_data34[smith_data34["Turn"] == 19]
smith_data34_oso.head(5)
```

|  | Turn | Hund | Money | UselessMoney | Refresh |
|--:|--:|:--|:--|:--|:--|
| 1248 | 11 | 銅銅銅銅銅/屋屋屋銅銅/屋屋屋銅銅銅銅鍛/屋銀銅銅銅銅銅鍛/屋屋銀銅銅/屋金銅銅銅銅銅鍛/銀... | 5/2/4/7/4/8/6/8/8/9/8 | 1/2/1/1/1/2/0/0/0/1/0 | 0/0/1/2/3/3/4/4/5/5/6 |
| 4369 | 11 | 屋屋屋銅銅/銅銅銅銅銅/屋屋銅銅銅/屋銅銅銅銅銅銅鍛/屋屋銀銅銅/屋金銅銅銅銅銅鍛/金銀銅銅... | 2/5/3/6/4/8/8/8/1/8/8 | 2/1/0/0/1/2/0/0/1/0/0 | 0/0/1/2/3/3/4/4/5/5/6 |
| 13079 | 11 | 銅銅銅銅銅/屋屋屋銅銅/屋屋銅銅銅/屋銅銅銅銅銅銅鍛/屋屋銀銅銅銅銅鍛/屋金銅銅銅/屋銀銅銅... | 5/2/3/6/6/6/5/8/8/8/8 | 1/2/0/0/0/0/2/0/0/0/0 | 0/0/1/2/3/3/4/4/5/5/6 |
| 16812 | 11 | 屋屋屋銅銅/銅銅銅銅銅/屋銅銅銅銅銅銅鍛/屋屋屋金銅銅銅鍛/屋銅銅銅銅/金金銅銅銅/屋屋属金... | 2/5/6/6/4/9/6/6/8/8/8 | 2/1/0/0/1/1/0/0/0/0/0 | 0/0/1/2/3/3/4/4/5/5/6 |
| 19156 | 11 | 屋屋屋銅銅/銅銅銅銅銅/屋銅銅銅銅銅銅鍛/屋屋金銅銅/屋銅銅銅銅銅銅鍛/屋屋金銀銅/金銀銅銅... | 2/5/6/5/6/6/8/10/9/4/8 | 2/1/0/2/0/0/0/2/1/1/0 | 0/0/1/2/3/3/4/4/5/5/6 |

|  | Turn | Hund | Money | UselessMoney | Refresh |
|--:|--:|:--|:--|:--|:--|
| 16 | 19 | 屋屋銅銅銅/屋銅銅銅銅/屋屋銅銅銅銅銅鍛/屋銀銅銅銅/屋屋銀銅銅銅銅鍛/屋銀銅銅銅/屋金銀銅... | 3/4/5/5/6/5/7/6/8/12/5/5/7/11/6/7/7/6/8 | 0/0/2/2/0/2/1/0/0/4/2/2/1/3/0/1/1/0/0 | 0/0/1/2/2/3/3/4/4/5/5/5/6/6/6/7/7/7/7 |
| 142 | 19 | 屋銅銅銅銅/屋屋銅銅銅/屋屋銅銅銅銅銅鍛/屋銀銅銅銅/屋屋銀銅銅/銀銀銀銅銅銅銅鍛/屋屋銅銅... | 4/3/5/5/4/10/3/7/9/5/7/9/5/4/15/4/7/7/8 | 0/0/2/2/1/4/0/1/1/2/1/1/2/1/7/1/1/1/0 | 0/0/1/2/2/3/3/4/4/5/5/5/6/6/6/7/7/7/7 |
| 171 | 19 | 屋銅銅銅銅/屋屋銅銅銅/屋屋銀銅銅/屋銅銅銅銅/屋屋銅銅銅銅銅鍛/屋銀銀銅銅/屋金銀銅銅/屋... | 4/3/4/4/5/6/7/9/5/5/11/9/7/5/7/5/6/6/15 | 0/0/1/1/2/0/1/1/2/2/3/1/1/2/1/2/0/0/7 | 0/0/1/1/2/2/3/3/4/4/4/5/5/5/6/6/6/6/7 |
| 197 | 19 | 屋銅銅銅銅/屋屋銅銅銅/屋屋銅銅銅/屋屋銀銅銅銅銅鍛/屋銀銅銅銅/屋銀銅銅銅/屋屋銀銅銅/屋... | 4/3/3/6/5/5/4/11/5/6/8/5/9/6/6/10/6/6/11 | 0/0/0/0/2/2/1/5/2/0/0/2/1/0/0/2/0/0/3 | 0/0/1/2/2/3/3/4/4/5/5/5/6/6/6/7/7/7/7 |
| 203 | 19 | 屋屋銅銅銅/屋銅銅銅銅/屋屋銅銅銅銅銅鍛/屋銀銅銅銅/屋銅銅銅銅/屋銀銀銅銅銅銅鍛/屋屋銀銅... | 3/4/5/5/4/8/4/9/5/7/6/9/6/6/11/11/6/6/12 | 0/0/2/2/1/2/1/3/2/1/0/1/0/0/3/3/0/0/4 | 0/0/1/2/2/3/3/4/4/5/5/5/6/6/6/7/7/7/7 |

2-5の入りなのに速い時や、3-4の入りなのに遅い時も同様の傾向が見られます。

#まとめ
前回の~~ヒドい~~記事に自らマサカリを投げていくスタイルの記事となりました。
他の戦略に行く前に、もう少し鍛冶屋ステロを掘り下げられそうなのでまだもう少しだけ続くと思います。

ちなみに最終目標は「ドミニオンAI」を作ることです。
いつか自分の作ったAIと対戦してみたいですね〜。
~~(いつになることやら)~~

今回使用したソースおよびCSVです。
https://github.com/NCT48/Dominion