Black Jackはプレイヤーが有利説は本当か？

絶対誰かがすでに書いてると思いますが・・・ｗ
pythonでBlackJackのアルゴリズムを組んで結局ディーラーとプレイヤーのどちらが有利なのかを検証してみたいと思います！
##ブラックジャックとは？
プレーヤー・ディーラーで引いたカードの合計値を競うゲームです。
カードの強さは次の通りです
強い　21→20→19→18→17→16以下→22以上　弱い
プレイヤー・ディーラーともに22以上の場合はプレイヤーの負けです。

①掛け金をきめて、ベットする（今回はすべて25＄として行っています。）
②プレイヤー・ディーラーに2枚ずつカードが配られる
（ただし、ディーラーのカードの1枚は裏向けになっている）
③プレイヤーは次の行動ができる
１・ヒット→カードを1枚引く
２・スタンド→カードを引くのをやめ、ディーラーに番を渡す
３・ダブルダウン→掛け金を2倍にする代わりに、必ずあと1回だけひく
４・スプリット→自分のカードがぞろ目だった時に、2枚を分けて別々のゲームとして行う
５・サレンダー→掛け金の半分をディーラーにわたい、ゲームを降りる
④プレイヤーが22を超えなかった場合、ディーラーがカードを引く
（このとき、ディーラーは17以上になるまで引き続ける義務がある）
⑤最終的なカードの強さで勝敗が決まる。
※絵札はすべて10とカウントします。
※Aは1か11の都合の良いほうにカウントできます。
※プレイヤーがはじめから10又は絵札+Aのペアだった場合、ブラックジャックになり、配当が上がります。

詳しいルールは[ウィキペディア](https://ja.wikipedia.org/wiki/%E3%83%96%E3%83%A9%E3%83%83%E3%82%AF%E3%82%B8%E3%83%A3%E3%83%83%E3%82%AF)へどうぞ。

##検証！
なんか上のルールを見る限りは一瞬プレイヤーのほうが有利な感じもしますけど、実際はどうなんでしょうか？　
と気になったので、1万回回してみることにしました。
（スプリット・ダブル・サレンダーなどを実装している時間はありませんでした・・・もしかしたらまた今度やるかもです。）

ヒットする最大値はinputします。
（例えば自分のカードの合計が14だった時にヒットするかどうか　みたいなことを決めます。）
ものすごく雑で安っぽい（？）コードですがそれでもよければどうぞ・・

```python
#設定-----------------------------------------------------------
IP=10000#ゲームを行う回数
Money=0#金額の初期値
MinChip=25#ベッド数
Stop=input()#ヒットする最大値
Stop=int(Stop)
StopA=18#ヒットするソフト（A＝11）最大値
Deck=6#使用するデッキ数
Cmax=6#デッキのシャッフルを何ゲームごとにするか

#ログ-----------------------------------------------------------
MaxM=Money#もっとも儲かていた時
MinM=0#最も損をしていた時
Rate=0    #勝率
BustRate=0 #ディーラーのバストをカウント
BustRateIP=IP#ディーラー・バストレートの母数
#関数-----------------------------------------------------------
import random
import time
def functionA(E1,E2,s):#Aカード対策関数
    if E1[0]=="A":
        if E2[0]=="A":
            return [12,s+2]
        else:
            return [11+int(E2[:-1]),s+1]
    elif E2[0]=="A":
            return [int(E1[:-1])+11,s+1]
        
    else:
        return [int(E1[:-1])+int(E2[:-1]),s]

def functionJQK(E):#JKQカード対策用関数
    if E[0]=="J" or E[0]=="Q" or E[0]=="K":
        return "10z"
    else:
        return E

def functionAA(E,XT,s):#Aカード対策2
    if E[0]=="A":
        return [XT+11,s+1]
    else:
        return [XT+int(E[:-1]),s]



#処理-----------------------------------------------------------
counter=0
Trump=["2c","2s","2h","2d","3c","3s","3h","3d","4c","4s","4h","4d"
        ,"5c","5s","5h","5d","6c","6s","6h","6d","7c","7s","7h","7d"
        ,"8c","8s","8h","8d","9c","9s","9h","9d","10c","10s","10h","10d"
            ,"Jc","Js","Jh","Jd","Qc","Qs","Qh","Qd","Kc","Ks","Kh","Kd"
            ,"Ac","As","Ah","Ad"]
if True:
    for z in range(int(IP)):

        s1=0 #Aカード対策
        s2=0 #Aカード対策
        v=0  #ディーラーBJ対策
        counter+=1
        if counter>Cmax:
            Trump=["2c","2s","2h","2d","3c","3s","3h","3d","4c","4s","4h","4d"
                ,"5c","5s","5h","5d","6c","6s","6h","6d","7c","7s","7h","7d"
                ,"8c","8s","8h","8d","9c","9s","9h","9d","10c","10s","10h","10d"
                   ,"Jc","Js","Jh","Jd","Qc","Qs","Qh","Qd","Kc","Ks","Kh","Kd"
                   ,"Ac","As","Ah","Ad"]
            Trump=Trump*Deck
            counter=0


    #プレイヤー
        #トランプからランダムにひとつ抽出
        R1=random.randint(0,len(Trump)-1)
        PC1=Trump.pop(R1)
        R2=random.randint(0,len(Trump)-1)
        PC2=Trump.pop(R2)
        print(PC1)
        print(PC2)

        #JQK対策
        PC1=functionJQK(PC1)
        PC2=functionJQK(PC2)
        #A対策
        PT=functionA(PC1,PC2,s1)[0]
        s1=functionA(PC1,PC2,s1)[1]



    #ディーラー   
        R3=random.randint(0,len(Trump)-1)
        DC1=Trump.pop(R3)
        R4=random.randint(0,len(Trump)-1)
        DC2=Trump.pop(R4)
        print(DC1,"Dealer")

        #JQK対策
        DC1=functionJQK(DC1)
        DC2=functionJQK(DC2)
        #A対策
        DT=functionA(DC1,DC2,s2)[0]
        s2=functionA(DC1,DC2,s2)[1]


        if DT==21 and PT==21:
            print("Drow")
            v=1
        elif DT==21:
            print(DC2,"Dealer")
            print("you lose!")
            Money=Money-MinChip
            v=1
            Rate+=1
        elif PT==21:
            Money=Money+MinChip*1
            print ("BJ!")
            v=1
            Rate+=1
            BustRateIP-=1

    #hit/stand処理

        while v==0:
            if PT<=Stop or (PT<=StopA and s1>0)  :
                RH=random.randint(0,len(Trump)-1)
                HC=Trump.pop(RH)
                print(HC)
                HC=functionJQK(HC)
                PT=functionAA(HC,PT,s1)[0]
                s1=functionAA(HC,PT,s1)[1]
                if PT>21:
                    if s1>0:
                        PT=PT-10
                        s1=s1-1
                    else :
                        Money=Money-MinChip
                        print("You lose!")
                        BustRateIP-=1
                        break
                
            elif True:
                    print(DC2,"Dealer")
                    while True:
                        if 16<DT<22:
                            print (DT,"DEALER TOTAL")
                            if PT>DT:
                                print("You win!")
                                Money=Money+MinChip
                                Rate+=1
                            elif PT<DT:
                                print("You losee....")
                                Money=Money-MinChip
                            else:
                                print("Draw")
                            break
                        elif 21<DT:
                            if s2>0:
                                s2=s2-1
                                DT=DT-10
                            else:
                                print("Dealer Bust")
                                Money=Money+MinChip
                                Rate+=1
                                BustRate+=1
                                print("You Win!")
                                break
                        RH=random.randint(0,len(Trump)-1)
                        DHC=Trump.pop(RH)
                        print(DHC,"Dealer")
                        DHC=functionJQK(DHC)
                        DT=functionAA(DHC,DT,s2)[0]
                        s2=functionAA(DHC,DT,s2)[1]


                    break
        print(Money)
        print("")
        if MaxM<Money:
            MaxM=Money
        elif MinM>Money:
            MinM=Money
print(Money,"result")
print(MaxM,"maximum")
print(MinM,"minimum")
print(Rate*100/IP,"Rate")
print(BustRate*100/BustRateIP,"Dealer Bust Rate")

```
かなり無駄があるコードな気もしますが・・・・・


##結果
25ドル×1万回で25万ドル（三千万円弱）を賭けた結果がこちらになります。

```
#最大ヒット値12
-21850 result
75 maximum
-21950 minimum
46.9 Rate
28.515454941227688 Dealer Bust Rate
```
```
#最大ヒット値13
-14500 result
25 maximum
-14775 minimum
47.71 Rate
29.3588429563831 Dealer Bust Rate
```
```
#最大ヒット値14
-13325 result
300 maximum
-13500 minimum
47.87 Rate
29.046048613655795 Dealer Bust Rate
```
```
#最大ヒット値15
-16425 result
450 maximum
-16925 minimum
46.91 Rate
28.943196829590487 Dealer Bust Rate
```
```
#最大ヒット値16
-15675 result
275 maximum
-15750 minimum
46.24 Rate
28.960943257184965 Dealer Bust Rate
```

##結論
なんだかんだで結局ヒット・スタンドだけではディーラーに勝つことはできないという結論に落ち着きました。
あとは、勝率を見る限り15くらいでスタンドしておくのが一番だということがわかりました。

とはいっても、勝率47%なら、スプリット・サレンダー・ダブル入れたら勝てそうじゃないか？

##実践
実際のゲームでプレイしてみました。
[crypto-games.net](https://www.crypto-games.net?i=hCHrwnnPg3)
リアルマネーで勝負するのは怖いけど、ノーマネーのゲームもつまらない・・・なんて思ったので、「ノーリスク」ローリターンなビットコインカジノにしました。
（your account → REWARDから、少量のビットコインがタダでもらえます。）

さて、それはさておき、結果は次の通りでした。
![無題.png](https://qiita-image-store.s3.amazonaws.com/0/211349/fbe4006d-087e-0228-1d72-4a1283cd468a.png)

わかりにくいですが、
443回のベッドで0.01BTC(8000円くらい)を賭けて
勝ちが0.005BTC(4500円くらい？)
負けが0.0042BTC(3500円くらい？)　という結果になりました。
(課金してないのに結果として1000円儲かってる・・・　)
予想に反して意外な結果でした。
（その時の感みたいなのが働いたりもしたのかな？ｗ）

##追記１
サレンダー処理を追加しました。
自分がかなり不利な場合（ディーラーのカードが10かAで、プレイヤーの合計が14,15,16の時）に掛け金を半額だけ渡してゲームおります。

```
#最大ヒット値13
-14400.0 result
512.5 maximum
-14437.5 minimum
46.5 Rate
27.460083795719623 Dealer Bust Rate
```
```
#最大ヒット値14
-10362.5 result
12.5 maximum
-10362.5 minimum
47.06 Rate
28.72340425531915 Dealer Bust Rate
```
あれっ　あんまり変わってない？