ブラックジャックのAの処理をした話(python)


アソビ大全のブラックジャックにハマったので、
親の合計値の分布をヒストグラムにだそうとしました。

その時 Aの処理でつまづきましたが、
以下のようにして対処しました。

結論

Aを基本11として扱う代わりに、
『猶予』のパラメータを設定する。
Aを引いたときはパラメーターを1増やし
21を超えたときにパラメーターが1以上であれば
合計から10を減らして復帰する

ブラックジャック関数の基本

Aを単なる1 として扱う場合を先に考えましょう。

まずはモジュールをインストール

import numpy as np
import matplotlib.pyplot as plt 
import numpy.random as random
import pandas as pd 
%matplotlib inline

カードを引く関数 deal
合計が17になるまでカードを引き結果を返す関数 dealer
カードの束を表す normal_deck

def deal(x,deck):
    x += random.choice(deck)
    return x
def dealer(x,deck):
    while x < 17 :
        x = deal(x,deck)
    return x
normal_deck = [1,2,3,4,5,6,7,8,9,10,10,10,10]*4

これで dealer(親の合計,使うカード)で
最終的な親のカードの合計値が出てきます。
試しに10万回ほど回してデータをヒストグラムに表しましょう。
これは親の合計値が10の状態からカードを引いていく例です。

ln =[]
for i in range(100000):
    ln.append(dealer(10,normal_deck))

bins= [17,18,19,20,21,22,23,24,25,26,27]
plt.hist(ln, bins=bins) 

結果

縦軸は相対度数にしたり色々できます。

Aを扱うバージョン

Aが出た時は11として計算、
ただし21を超えたとき、
- Aが出ていた場合は-10をして計算続行
- Aが出ていなかった場合は計算終了

とすれば上手く行きました。

関数を書いていきます。

def deal_(deck):
    x = random.choice(deck)
    return x

前のdealは合計値を返していましたが、
今回は引いたカードの値をそのまま返します。

def dealer2(x,deck):

    # 設定パート
    if x == 1:
        para = 1 #猶予設定
        x = 11
    else:
        para = 0
    # 設定パート終

    while x < 17 : #---------------------------
    # hit card part
        y = deal_(deck)
        #print(y)

        if y != 1: # not hit A case
            if x + y <=21:
                x += y
            elif para >= 1:
                para += -1
                x += y - 10 # x+yから10をひく
            else:
                x += y

        else: # hit A case
            para += 1
            if x  <= 10:
                x +=11
            elif para >=1:
                para += -1
                x += 1
            else:
                print('error')
                x += 1
   #--------------------------------------------------
    return x

説明

設定パート

1を基本11として扱うために
アップカード(事前に見えるカード)が1の場合、
合計値を11にし、パラメーター(para)を1にします。
アップカードが1でない場合もパラメーター(para)を0にします。

カード引きパート

引いたカードが1(Aの意味)かで変えます。

  • 引いたカードが1の場合、
    para に1を加え、値に11を足します。

  • 引いたカードが1以外の場合、
    前バージョンと基本同じです。

両方の場合で、21を超えた場合に備え猶予のアクションをしてもらいます。

猶予のアクション

paraが1以上の場合、つまりAを1としてまだ使ってない場合に、
11として使っていたAを1に変換、つまり合計値を -10 します。

Aがない、もしくは1に全部変換した場合は、paraが0になっているので、
そのまま合計値を返します。

ヒストグラフ描写

新たな関数を用い、ヒストグラムを書きましょう。
これは親の合計値が13の状態から始める例です。

ln = []
bins = [17,18,19,20,21,22,23,24,25,26,27]
for i in range(100000):
     ln.append(dealer2(13, normal_deck))

plt.hist(ln, normed=True, bins=bins, align='left', rwidth=0.9)

返り値

array([0.09648, 0.09533, 0.0955 , 0.09558, 0.09561, 0.09664, 0.3286 ,
        0.03688, 0.0326 , 0.02678]),
 array([17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]),
 <a list of 10 Patch objects>)

各々の値を知りたいのでヒストグラムのデータを取り出します。
親の初期合計値がx、試行回数n回のヒストグラムらを返すコードです。

def check(x,n):
    ln = []
    bins = [17,18,19,20,21,22,23,24,25,26,27]

    for i in range(n):
        ln.append(dealer2(x, normal_deck))

    data = plt.hist(ln, normed=True, bins=bins, align='left', rwidth=0.9, cumulative=False)

    sum = 0
    for i in range(0,5):
        sum += data[0][i]
    print('バーストにならない確率は {:.2f} %'.format(sum*100))
    for i in range(0,10):
        print('{}になる確率は {:.2f} %'.format(data[1][i],data[0][i]*100))
check(2,10000)

返り値

バーストにならない確率は 64.64 %
17になる確率は 13.44 %
18になる確率は 13.28 %
19になる確率は 13.03 %
20になる確率は 12.86 %
21になる確率は 12.03 %
22になる確率は 15.13 %
23になる確率は 6.38 %
24になる確率は 5.50 %
25になる確率は 4.57 %
26になる確率は 3.78 %

これでおしまい

終わりに

親の初期状態でAが入ってるかも設定しようと思えばできるでしょうが、
実用性がなさそうなので省きました。

カードを引くたび、もしくは既にでてきた場合、
そのカードが出ないようにもしたいですね。