コンピュータとオセロ対戦18　～乱数検証～

https://qiita.com/tt_and_tk/items/068f2afde6db637e189f

[前回](https://qiita.com/tt_and_tk/items/9928f1848112785563ce)

# 今回の目標
[前回](https://qiita.com/tt_and_tk/items/9928f1848112785563ce)、評価値について調べると書きましたが、その前に乱数によって有利不利が生まれるかどうかについて調べたいと思います。
# ここから本編
今回、srand関数の引数を0～999まで変化させ、それぞれで100試合ずつランダム同士の対戦を行い、それぞれの勝率を調べました。
ここで勝率とは、黒側が買った回数をいうこととします。引き分けあるいは白が勝った数はカウントしません。
## 実行ファイル
ヘッダファイルとソースファイルは今までと全く同じです。
オブジェクト指向便利。
やっていることとしては、上述したことがすべてなので追加説明はありません。

```run_rand_rand.cpp
#include "osero_genetic.h"

const int child = 100;
const int entire = 1000;

int main(void){
    int win, win_sum;
    int i, j;
    osero_genetic * run;
    FILE * fp = fopen("data_rand_rand.csv", "w");

    fprintf(fp, "srand_num,win_per\n");

    for (i = 0; i < entire; i++){
        win_sum = 0;

        for (j = 0; j < child; j++){
            run = new osero_genetic(
                INT(PLAY_WAY::random),
                INT(PLAY_WAY::random)
            );
            run -> mode = 1;
            run -> player = 1, run -> computer = 0;
            run -> srand_num = i;
            win = INT(run -> play());

            if (win) win_sum++;
            delete run;
        }

        fprintf(fp, "%d,%d\n", i, win_sum);
    }

    fclose(fp);

    return 0;
}
```
## 実行結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/52dbdd8a-087f-f95b-7f58-9af08d27fd64.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/ce25d780-64a5-5a41-c986-ace92fbd140b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/f5bc5666-2b7a-4b4a-1b52-e40333f640d3.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/84b88140-e6a5-e740-23e7-5b9e492d9562.png)

## run_1hand_rand
続いて、1hand VS randomで試合を行いました。
評価値はすべて1で試してみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/b8aaa79f-88a7-9b13-e52a-ede4e59c868a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/af1a8d66-fb48-1228-3388-a07f9d40c6ea.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/8280cb29-acbf-3be3-e138-1d3a19ee383e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/f6818c44-c69e-9038-830f-6ec0cb50f7a8.png)

## run_2hand_rand
さらに2hand VS randomで試合を行いました。
こちらも、評価値はすべて1で試してみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/f9400b03-aa61-7df7-cb5e-aa0b9b1578cc.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/81e45b7f-e9dc-aeff-e70f-858b23a71e64.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/85933d81-8185-cb0c-307c-6bb8d34243cc.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/554c797a-a68b-8aba-636a-c68f68477fb7.png)

## run_rand_1hand

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/936669fb-c8ac-071b-c742-6578898636b8.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/6d1d4eba-a88b-24ff-8b5b-a6f8b4120694.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/c8373b3c-8522-6171-4aa2-e255ba5dce91.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/fd3e1f74-2db8-55f8-20dc-fa902deb9c96.png)

## run_rand_2hand

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/4c225def-7443-8e8b-03a6-70303c5a4eda.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/e273fe6c-80d5-e86c-9aec-bbe725878cb9.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/527d6bcb-7d61-2f7c-7917-e81d8cf7dced.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/bc8fd1a4-3411-040f-7b95-9d8418f59280.png)

## 結局、どれが最適なのか
結果を見ると、どの試合も600付近で勝率が下がっているように見えます。しかしほとんどのグラフが波打っており、人間の目でははっきりと「これが最適！」とは言えません。

ということでまず、以下のプログラムを用いてすべてのデータをまとめてみました。
やっていることとしては、上の、それぞれの思考方法ごとに作ったmother_numを用いたグラフを一つにまとめているだけです。

```data.py
import pandas as pd
import glob
import matplotlib.pyplot as plt
import numpy as np

pd.set_option("display.max_columns", 6)

########  all data ########

i = 0

for filename in glob.glob("data*.csv"):
    win_per = filename + "_win_per"
    if i == 0:
        df = pd.read_csv(filename)
        df.columns = ["srand_num", win_per]
        i = 1
    else:
        df_ele = pd.read_csv(filename)
        df_ele.columns = ["srand_num", win_per]
        df.loc[:, win_per] = df_ele[win_per]

df.to_csv("csvdata.csv")

########  part data  ########

mother_num = 10
df_custom = []
for_num = int((max(df["srand_num"]) + 1) / mother_num)
srand_num = []
mean = []

data = open("data/data.txt", "w")

for i in range(for_num):
    # get data
    num = i * mother_num
    df_custom.append(df[num:num + mother_num])
    
    # plot data
    srand_num.append(num)
    mean_ele = df_custom[i].mean()
    mean.append(list(mean_ele[1:]))
    
    # statistics data
#     df.to_csv("data/%4d_%4d.csv" % (num, num + mother_num))
    data.write("%4d_%4d:\n" % (num, num + mother_num))
    data.write(str(df_custom[i].describe()) + "\n\n")

data.close()

# plot

mean = np.array(mean)
mean = mean.T
fig = plt.figure()
i = 0
for filename in glob.glob("data*.csv"):
    plt.plot(srand_num, mean[i], label=filename)
    i += 1
plt.legend()
plt.title("mother_num=%d" % mother_num)
plt.xlabel("srand_num")
plt.ylabel("mean win per")
# plt.show()
plt.savefig("data/fig%d" % mother_num)
plt.clf()
plt.close()
```

結果がこちら。

![fig10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/9949ade0-e695-274b-6bd1-7caca1522d3d.png)

![fig25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/c70a1781-da9c-7b37-3494-c80fa29e74d3.png)

![fig50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/7a43d48c-2731-c82b-3f10-0fac928fa79b.png)

やっぱりよく分からないので、以下のプログラムを追加し各手法ごとに正規化したグラフを作ってみました。なお正規化のプログラムは[こちらの記事](https://deepage.net/features/numpy-normalize.html)の丸写しです。

```data.py
# normalized plot

fig = plt.figure()
i = 0
for filename in glob.glob("data*.csv"):
    arr_mean = mean[i].mean()
    arr_std = np.std(mean[i])
    mean[i] = (mean[i] - arr_mean) / arr_std
    plt.plot(srand_num, mean[i], label=filename)
    i += 1
plt.legend()
plt.title("mother_num=%d, normalized" % mother_num)
plt.xlabel("srand_num")
plt.ylabel("mean win per")
# plt.show()
plt.savefig("data/fig%d, normalized" % mother_num)
plt.clf()
plt.close()
```

結果はこちら。

![fig10, normalized.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/66f8f6eb-bf3b-6ad9-aeb9-686c1fed9cc6.png)

![fig25, normalized.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/21e52dac-7d44-ae1b-77d6-b514da29a6e3.png)

![fig50, normalized.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/065fd702-177f-9dc0-c158-256ab2c1ab1a.png)

判断が難しいですが、mother_num=50, normalizedのグラフでもっとも0に近い値の多かった90～100がsrand関数の引数として最適だと思われます。

なお、本題ではないですが、データをよく見たらその勝利数は100か0かでした。よく考えれば、srand関数に与える引数が一定ですから当然のことです。今更ですがchild変数は1でもよかったですね。

# フルバージョン
rand_test内にあります。

https://github.com/TTaisei/osero/tree/main/sita

# 次回は
まさか乱数だけで一つの記事が書けてしまうとは思いませんでした。
次回は[前回](https://qiita.com/tt_and_tk/items/9928f1848112785563ce)も書いた通り、前回求めた評価値の正しさについて検証したいと思います。
その後学習方法の改善を行います。
正しい評価値が算出されれば下積み編は終了し、機械学習にリベンジしたいと思います。

[次回](https://qiita.com/tt_and_tk/items/d0a192d1b4d3ef9c4fb5)

https://qiita.com/tt_and_tk/items/068f2afde6db637e189f