【麻雀】天和の確率を実験的に求めた


## 0. 概要
Python自学のため、麻雀に関するライブラリ(mahjong)を使ったシミュレーションを行いました。
ライブラリに関する詳細はこちらの記事を参照ください。
https://qiita.com/FJyusk56/items/8189bcca3849532d095f

シャンテン数計算ライブラリは、入力の14枚に対してシャンテン数が返ってきます。
和了の場合、リターンは-1です。

毎回ランダムな14枚に対するシャンテン数を計算し、-1が返ってきた回数をログします。

## 1. 先行調査

天和の確率は約33万分の1として知られており、和了形÷全組み合わせから求められます。
和了形は4面子+1雀頭型、七対子型、国士型の3パターンに分類されます。

詳細はこちらを参照ください。
[天和 (麻雀)Wikipedia](https://ja.wikipedia.org/wiki/%E5%A4%A9%E5%92%8C_(%E9%BA%BB%E9%9B%80))

また、天和の確率を全組み合わせから計算されたサイトがあります。今回はこちらで紹介されている計算結果を理論値とみなします。
http://www10.plala.or.jp/rascalhp/mjmath.htm#6
https://bibo-blog.com/tenho-probability/

また以下のサイトでは、1000億回のシミュレーションを行った結果が紹介されていました。
詳細はこちらを参照ください。Probabilityの項目になります。
https://riichi.wiki/Tenhou_and_chiihou

時間的にリソースの都合上、1000億回の施行は難しかったため、今回の試行回数は100億回としました。

## 2.アルゴリズム
アルゴリズムは以下の通りです。
1. 配牌14枚の付与
2. シャンテン数の計算
3. シャンテン数=-1が出現したら、その牌姿と役をロギング
4. 1~3を100億回繰り返す
5. csvにログを出力


## 3.検証結果

100億回のシミュレーションに基づく、和了および不和了の発生回数をまとめた表になります。

![result.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763805/4d9657ae-2c38-08a7-7164-3e0a3f23e92b.png)

今回の100億回の繰り返し計算では、天和確率は約0.00035%(約29万回に1回)となりました。
試行回数を増やすことで限りなく理論値に近づくものと思われます。(大数の法則)


## 4.コード

```python
from mahjong.shanten import Shanten
from mahjong.tile import TilesConverter
from mahjong.hand_calculating.hand import HandCalculator
from mahjong.hand_calculating.hand_config import HandConfig
from mahjong.constants import EAST, SOUTH, WEST, NORTH
import random
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import os
from time import time
from tqdm import tqdm
import datetime

print('abspath:     ', os.path.abspath(__file__))
print('abs dirname: ', os.path.dirname(os.path.abspath(__file__)))
dt_now = datetime.datetime.now()
timestring = dt_now.strftime("%Y%m%d%H%M%S")

cur_dir = os.path.dirname(os.path.abspath(__file__))

def main():

    roopcount = 0 
    tsumocount = 0
    tsumoroopcount = 0
    cols = ['tenho_count','single_roop','total_roop','yaku','tiles','win_tile']
    list_temp=[]
    shanten = Shanten()#Shanten(シャンテン計算クラス)のインスタンスを生成
    calculator = HandCalculator()
    allpai = list(range(134))
    print("計算開始")
    start_time = time()
  
    for i in tqdm(range(1000000000)):
        roopcount += 1
        tsumoroopcount += 1    
        a = initialHandby134tiles(allpai)
        result = shanten.calculate_shanten(TilesConverter.to_34_array(a[0]))
       
        if result == -1:
            tsumocount += 1
            tiles = a[0]
            win_tile = a[1]
            result_calc = calculator.estimate_hand_value(tiles,win_tile[0],config=HandConfig(is_tsumo=True,player_wind=EAST,round_wind=EAST))
            list_temp.append([tsumocount,tsumoroopcount,roopcount,result_calc.yaku,a[0],a[1]])
            tsumoroopcount = 0
    elapsed_time = time() - start_time

    print("かかった時間[s]",elapsed_time)
    df_c = pd.DataFrame(list_temp, columns=cols)
    df_c.to_csv(cur_dir+'/'+"hoge_output_data_"+timestring+".csv",index=False)
    print("計算終了")

    
def initialHandby134tiles(mylist1):
    
    haipai = random.sample(mylist1, 14)
    lasttsumohai = random.sample(haipai,1)
    return haipai,lasttsumohai

if __name__ == "__main__":
    main()

```