シャンテン数計算プログラム

# ※この記事は2020年に作成しました
#概要
手牌を入力し、その手牌のシャン点数を求めるプログラムを作成した。
七対子、国士無双のシャンテン数は下記サイトの計算方法を参考にした。

- http://ara.moo.jp/mjhmr/shanten.htm

一般手のシャンテン数の計算方法は下記のシャンテン数理論の【メンツ手のシャンテン数の簡単な求め方】を参考にした。

- https://ameblo.jp/010101/entry-11802087952.html

実際にこれで全ての手牌のシャンテン数を正確に数えられるとは限らないと思う。また、面子の取り方、ターツの取り方で色々工夫しなければならない。
今回は面子の取り方を16パターンに分けて取り出して、その後にターツを取り出してからシャンテン数を計算している。
16パターンの面子の取り方は以下になっている。
sが順子を手牌の最初からみて1回だけ取り出していて、kが刻子を手牌の最初から見て1回だけ取り出していることを表す。
また、Sは手牌を最初からみて順子を全て取り出していて、Kは手牌を最初からみて刻子をを全て取り出していることを表す。

|    パターン|
|:-----------------:|
|S→K        |            
|K→S|
| s→k→s→k       |         
| k→s→k→s            |    
| s→k→k→s     |     
| k→s→s→k|
|s→s→s→k|
|s→s→k→s|
|s→k→s→s|
|k→s→s→s|
|k→k→k→s|
|k→k→s→k|
|k→s→k→k|
|s→k→k→k|
|s→s→k→k|
|k→k→s→s|

#作成したプログラム
```python
# coding: UTF-8
#手牌のシャンテン数を調べる（鳴きは無視）
import numpy as np
import copy

def OneCntKotsu(tmp):#刻子があれば1を返しその刻子を抜く,そうでなければ0を返す
        for i in range(34):
                if tmp[0,i] >=3:
                        tmp[0,i] -=3
                        return 1
        return 0

def OneCntShunt(tmp):#順子があれば1を返しその刻子を抜く,そうでなければ0を返す
        for i in range(25):
                if i==7 or i==8 or i==16 or i==17:continue
                if tmp[0,i]>0 and tmp[0,i+1]>0 and tmp[0,i+2]>0:
                        tmp[0,i]-=1
                        tmp[0,i+1]-=1
                        tmp[0,i+2]-=1
                        return 1
        return 0

def CntShunt(tmp):#順子をカウント
        CntS = 0
        for i in range(25):
                if i==7 or i==8 or i==16 or i==17:continue
                while(tmp[0,i]>0 and tmp[0,i+1]>0 and tmp[0,i+2]>0):
                        tmp[0,i]-=1
                        tmp[0,i+1]-=1
                        tmp[0,i+2]-=1
                        CntS += 1
        return CntS              

def CntKotsu(tmp):#刻子をカウント
        CntK = 0
        for i in range(34):
                if tmp[0,i] >=3:
                        tmp[0,i] -=3
                        CntK += 1
        return CntK

def Tartsu(tmp):#ターツの数を返す
        CntT = 0
        for i in range(8):
                if tmp[0,i]==1 and tmp[0,i+1]==1:
                        CntT +=1 
                        tmp[0,i] -= 1#マンズのリャンメンorペンチャン待ち
                if tmp[0,i+9]==1 and tmp[0,i+10]==1:
                        CntT += 1
                        tmp[0,i]-=1#ピンズのリャンメンorペンチャン待ち
                if tmp[0,i+18]==1 and tmp[0,i+19]==1:
                        CntT += 1
                        tmp[0,i]-=1#ピンズのリャンメンorペンチャン待ち
        for i in range(7):
                if tmp[0,i]==1 and tmp[0,i+2]==1:
                        CntT += 1
                        tmp[0,i]-=1#マンズのカンチャン待ち  
                if tmp[0,i+9]==1 and tmp[0,i+11]==1:
                        CntT += 1
                        tmp[0,i]-=1#ピンズのカンチャン待ち
                if tmp[0,i+18]==1 and tmp[0,i+20]==1:
                        CntT += 1
                        tmp[0,i]-=1#ソーズのカンチャン待ち
        return CntT

def kari_shanten(CntS,CntK,CntT,tmp):#順子数と刻子数とターツ数から仮シャンテン数を返す
        kari_shanten = 0
        cntAtama = 0
        for i in range(34):#雀頭を調べている
                if tmp[0,i] == 2:
                        cntAtama += 1
                        tmp[0,i] -= 2
                        CntT += 1

        Ments = CntS + CntK
        MentsTarts = Ments + CntT

        if MentsTarts >= 5:
                MentsTarts = 5
                if cntAtama == 0:MentsTarts = 4
        kari_shanten = 8 - (MentsTarts + Ments)
        return kari_shanten

def Shanten(tehai):#シャンテン数を返す
        
        tmp = np.ndarray((4,34))
        tmp = copy.deepcopy(tehai)
        Shanten_value = 8
        kari = 0
        CntK=CntS=CntT = 0

        CntS = CntShunt(tmp)#順子→刻子の順で面子を調べる
        CntK = CntKotsu(tmp)
        CntT = Tartsu(tmp)
        kari = kari_shanten(CntS,CntK,CntT,tmp)
        if Shanten_value > kari: Shanten_value = kari
        
        tmp = copy.deepcopy(tehai)#刻子→順子の順で面子を調べる
        CntK=CntS=CntT = 0
        CntK = CntKotsu(tmp)
        CntS = CntShunt(tmp)
        CntT = Tartsu(tmp)
        kari = kari_shanten(CntS,CntK,CntT,tmp)
        if Shanten_value > kari: Shanten_value = kari

        tmp = copy.deepcopy(tehai)#ksksの場合
        CntK=CntS=CntT = 0
        for i in range(2):
                CntK += OneCntKotsu(tmp)
                CntS += OneCntShunt(tmp)
        CntT = Tartsu(tmp)
        kari = kari_shanten(CntS,CntK,CntT,tmp)
        if Shanten_value > kari: Shanten_value = kari

        tmp = copy.deepcopy(tehai)#skskの場合
        CntK=CntS=CntT = 0
        for i in range(2):
                CntS += OneCntShunt(tmp)
                CntK += OneCntKotsu(tmp)
        CntT = Tartsu(tmp)
        kari = kari_shanten(CntS,CntK,CntT,tmp)
        if Shanten_value > kari: Shanten_value = kari
        
        tmp = copy.deepcopy(tehai)#skksの場合
        CntK=CntS=CntT = 0
        CntS += OneCntShunt(tmp)
        CntK += OneCntKotsu(tmp)
        CntK += OneCntKotsu(tmp)
        CntS += OneCntShunt(tmp)
        CntT = Tartsu(tmp)
        kari = kari_shanten(CntS,CntK,CntT,tmp)
        if Shanten_value > kari: Shanten_value = kari

        tmp = copy.deepcopy(tehai)#ksskの場合
        CntK=CntS=CntT = 0
        CntK += OneCntKotsu(tmp)
        CntS += OneCntShunt(tmp)
        CntS += OneCntShunt(tmp)
        CntK += OneCntKotsu(tmp)
        CntT = Tartsu(tmp)
        kari = kari_shanten(CntS,CntK,CntT,tmp)
        if Shanten_value > kari: Shanten_value = kari

        tmp = copy.deepcopy(tehai)#ssskの場合
        CntK=CntS=CntT = 0
        for i in range(3):CntS += OneCntShunt(tmp)
        CntK += OneCntKotsu(tmp)
        CntT = Tartsu(tmp)
        kari = kari_shanten(CntS,CntK,CntT,tmp)
        if Shanten_value > kari: Shanten_value = kari

        tmp = copy.deepcopy(tehai)#ssksの場合
        CntK=CntS=CntT = 0
        CntS += OneCntShunt(tmp)
        CntS += OneCntShunt(tmp)
        CntK += OneCntKotsu(tmp)
        CntS += OneCntShunt(tmp)
        CntT = Tartsu(tmp)
        kari = kari_shanten(CntS,CntK,CntT,tmp)
        if Shanten_value > kari: Shanten_value = kari
        
        tmp = copy.deepcopy(tehai)#skssの場合
        CntK=CntS=CntT = 0
        CntS += OneCntShunt(tmp)
        CntK += OneCntKotsu(tmp)
        CntS += OneCntShunt(tmp)
        CntS += OneCntShunt(tmp)
        CntT = Tartsu(tmp)
        kari = kari_shanten(CntS,CntK,CntT,tmp)
        if Shanten_value > kari: Shanten_value = kari
        
        tmp = copy.deepcopy(tehai)#ksssの場合
        CntK=CntS=CntT = 0
        CntK += OneCntKotsu(tmp)
        CntS += OneCntShunt(tmp)
        CntS += OneCntShunt(tmp)
        CntS += OneCntShunt(tmp)
        CntT = Tartsu(tmp)
        kari = kari_shanten(CntS,CntK,CntT,tmp)
        if Shanten_value > kari: Shanten_value = kari
        
        tmp = copy.deepcopy(tehai)#kkksの場合
        CntK=CntS=CntT = 0
        CntK += OneCntKotsu(tmp)
        CntK += OneCntKotsu(tmp)
        CntK += OneCntKotsu(tmp)
        CntS += OneCntShunt(tmp)
        CntT = Tartsu(tmp)
        kari = kari_shanten(CntS,CntK,CntT,tmp)
        if Shanten_value > kari: Shanten_value = kari
        
        tmp = copy.deepcopy(tehai)#kkskの場合
        CntK=CntS=CntT = 0
        CntK += OneCntKotsu(tmp)
        CntK += OneCntKotsu(tmp)
        CntS += OneCntShunt(tmp)
        CntK += OneCntKotsu(tmp)
        CntT = Tartsu(tmp)
        kari = kari_shanten(CntS,CntK,CntT,tmp)
        if Shanten_value > kari: Shanten_value = kari
        
        tmp = copy.deepcopy(tehai)#kskkの場合
        CntK=CntS=CntT = 0
        CntK += OneCntKotsu(tmp)
        CntS += OneCntShunt(tmp)
        CntK += OneCntKotsu(tmp)
        CntK += OneCntKotsu(tmp)
        CntT = Tartsu(tmp)
        kari = kari_shanten(CntS,CntK,CntT,tmp)
        if Shanten_value > kari: Shanten_value = kari
        
        tmp = copy.deepcopy(tehai)#skkkの場合
        CntK=CntS=CntT = 0
        CntS += OneCntShunt(tmp)
        CntK += OneCntKotsu(tmp)
        CntK += OneCntKotsu(tmp)
        CntK += OneCntKotsu(tmp)
        CntT = Tartsu(tmp)
        kari = kari_shanten(CntS,CntK,CntT,tmp)
        if Shanten_value > kari: Shanten_value = kari

        tmp = copy.deepcopy(tehai)#sskkの場合
        CntK=CntS=CntT = 0
        CntS += OneCntShunt(tmp)
        CntS += OneCntShunt(tmp)
        CntK += OneCntKotsu(tmp)
        CntK += OneCntKotsu(tmp)
        CntT = Tartsu(tmp)
        kari = kari_shanten(CntS,CntK,CntT,tmp)
        if Shanten_value > kari: Shanten_value = kari
        
        tmp = copy.deepcopy(tehai)#kkssの場合
        CntK=CntS=CntT = 0
        CntK += OneCntKotsu(tmp)
        CntK += OneCntKotsu(tmp)
        CntS += OneCntShunt(tmp)
        CntS += OneCntShunt(tmp)
        CntT = Tartsu(tmp)
        kari = kari_shanten(CntS,CntK,CntT,tmp)
        if Shanten_value > kari: Shanten_value = kari
        
        tmp = copy.deepcopy(tehai)#国士無双のシャンテン数
        kokushi = 0
        for i in range(34):
                if i==0 or i==8 or i==9 or i==17 or i==18 or i==26 or 27<=i:
                        if tmp[0,i] >=1:
                                tmp[0,i] -= 1
                                kokushi += 1
        for i in range(34):#対子がある場合はkokushiに1を足してbreakする
                if i==0 or i==8 or i==9 or i==17 or i==18 or i==26 or 27<=i:
                        if tmp[0,i] >=1:
                                tmp[0,i] -= 1
                                kokushi += 1
                                break
        kari = 13 - kokushi
        if Shanten_value > kari: Shanten_value = kari
       
        tmp = copy.deepcopy(tehai)#七対子のシャンテン数計算
        toits = 0
        hai = 0
        for i in range(34):
                if tmp[0,i] >= 2:
                        toits += 1
                if tmp[0,i] >= 1:
                        hai += 1
        kari = 6 - toits
        if toits >= hai:
                kari = 6 - toits + (7-hai)
        if Shanten_value > kari: Shanten_value = kari

        return Shanten_value

tehai = np.ndarray((4,34))
tehai[0,]=[2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,] 
#[0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,]
#[0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 2, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,]
#[0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,]
#[0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,]
#[3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 3, 0, 0, 2, 0, 0, 0, 0,] 
#雀頭なし国士無双[1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]

print(Shanten(tehai))
```