基于Python的21點(diǎn)游戲蒙特卡洛模擬

問(wèn)題重述:

大多數(shù)賭場(chǎng)使用6副牌或8副牌玩這種游戲幻碱,以防止“數(shù)牌點(diǎn)”,在你的模擬中使用兩副牌(共104張)细溅。只有2位參與者褥傍,你和莊家。游戲開(kāi)始時(shí)每人得到兩張牌喇聊,對(duì)于牌面為2~10的牌恍风,點(diǎn)數(shù)和面數(shù)相同;對(duì)于為人臉(J誓篱、Q朋贬、K)的牌,點(diǎn)數(shù)為10窜骄;牌面為A的牌锦募,點(diǎn)數(shù)為1或者11.游戲的目的是得到總數(shù)盡量接近21點(diǎn)的牌,不得超過(guò)(超過(guò)稱(chēng)“爆了”)邻遏,并使你得到的總點(diǎn)數(shù)多于莊家糠亩。?????????????
如果開(kāi)始兩張牌的總點(diǎn)數(shù)恰為21(A-10或A-人臉)虐骑,稱(chēng)為21點(diǎn),自動(dòng)成為勝者(若你和莊家都得到21點(diǎn)赎线,則為平局廷没,你的賭注仍在臺(tái)上)〈沽龋靠21點(diǎn)贏(yíng)時(shí)颠黎,付給你3賠2,即1.5賠1(1元賭注贏(yíng)1.5元矫废,且1元賭注仍保留)盏缤。
如果你和莊家都未得到21點(diǎn)砰蠢,你想要多少?gòu)埮凭涂梢匀《嗌購(gòu)埮票推耍淮我粡垼箍倲?shù)盡量接近21點(diǎn)台舱,如果你超過(guò)了21點(diǎn)律杠,就輸了,游戲結(jié)束竞惋。一旦你對(duì)牌的點(diǎn)數(shù)滿(mǎn)意柜去,你就“打住”,然后莊家按照下列規(guī)則取牌:
當(dāng)莊家牌的點(diǎn)數(shù)為17拆宛、18嗓奢、19、20和21時(shí)浑厚,就打住股耽。若莊家牌的點(diǎn)數(shù)小于或等于16,必然取牌钳幅。莊家總把A的點(diǎn)數(shù)記為11物蝙,除非這樣使他或她爆了(這時(shí)A的點(diǎn)數(shù)記為1)。例如敢艰,莊家的A-6組合是17點(diǎn)诬乞,不是7點(diǎn)(莊家沒(méi)有選擇權(quán)),且莊家必須打住在17點(diǎn)上钠导。而若莊家有A-4組合(15點(diǎn))震嫉,又拿了一張K,那么新的總點(diǎn)數(shù)是15牡属,因?yàn)锳回到點(diǎn)數(shù)1(使之不超過(guò)21點(diǎn)) 票堵,莊家還要再取牌。
如果莊家超過(guò)21點(diǎn)湃望,你就贏(yíng)了(贏(yíng)賭注的錢(qián)换衬,每1元賭注贏(yíng)1元)痰驱。如果莊家的總點(diǎn)數(shù)超過(guò)你,你將輸?shù)羧抠€注瞳浦。如果莊家和你的總點(diǎn)數(shù)相同担映,為平局(你不輸也不贏(yíng))。
賭場(chǎng)中這個(gè)游戲的刺激之處在于叫潦,莊家開(kāi)始的兩張牌一張明蝇完、一張暗,所以你不知道莊家牌地總點(diǎn)數(shù)矗蕊,必須根據(jù)那張明牌賭一把短蜕。在這個(gè)項(xiàng)目模擬中你不用考慮這種情況,你需要做的是:用兩副牌做12次游戲傻咖,你有無(wú)限的賭資朋魔,每次下賭2元。兩副牌玩過(guò)一次之后卿操,用兩副新牌繼續(xù)玩警检,這時(shí)記錄你的得分,然后下一幅牌從0開(kāi)始害淤,輸出是12個(gè)結(jié)果扇雕,你可以用平均數(shù)決定你的總成績(jī)。

函數(shù)說(shuō)明與實(shí)現(xiàn):

  1. get_the_card( a )
    輸入一個(gè)列表窥摄,返回一個(gè)整數(shù)镶奉。
    這個(gè)函數(shù)可以隨機(jī)地從總撲克牌列表(列表名為:desktop)中獲取一張撲克牌,并將其從撲克牌列表desktop中刪除(因?yàn)橥粡垞淇伺撇荒苋纱危┱阜拧7祷匦芦@取到的撲克牌的值哨苛。
def get_the_card(a):
    '''
    從桌面上獲取一張撲克牌,并將其從撲克牌列表中刪除莹菱。
    輸入一個(gè)列表移国,返回隨機(jī)獲取的撲克牌值。
    '''
    n = len(a)
    if n == 0:
        sys.exit(1)
    rand_num = np.random.randint(0, n)  # 從現(xiàn)有的撲克牌中隨機(jī)抽取一張
    num = a.pop(rand_num)               # 彈出選中的那一張
    return num
  1. count_points( a )
    輸入一個(gè)列表道伟,返回一個(gè)列表迹缀。
    這個(gè)函數(shù)輸入玩家手中的撲克牌列表。(列表名為:player_cards 和 banker_cards),返回玩家手中牌所代表分值的所有可能(列表)蜜徽,不同的可能是由于A(yíng)牌既可以看成是1也可以看成是11造成的祝懂。例如玩家手中的牌是 [1, 1, 1] 那么所有可能為 [3, 13, 23, 33]。
def count_points(a):
    '''
    這個(gè)函數(shù)用于計(jì)算玩家手中的牌點(diǎn)數(shù)的所有可能
    輸入:輸入一個(gè)列表
    輸出:輸出一個(gè)列表
    '''
    i = 0
    sum_points = list([sum(a)])
    while(1 in a):
        a.remove(1)
        i = i+1
        sum_points = sum_points + [sum_points[-1] + 10]
    for k in range(i):
        a.append(1)
    return sum_points
  1. whats_the_point (cards, k)
    輸入一個(gè)列表拘鞋,一個(gè)整數(shù)砚蓬,返回一個(gè)整數(shù)。這個(gè)函數(shù)調(diào)用了上面的函數(shù)盆色。
    如果點(diǎn)數(shù)超過(guò)21點(diǎn)則返回-2灰蛙,如果點(diǎn)數(shù)剛好是21祟剔,返回-1,如果點(diǎn)數(shù)介于[0, 19]之間則直接返回點(diǎn)數(shù)摩梧。
    這個(gè)函數(shù)輸入的是玩家手中撲克牌的列表和一個(gè)閾值物延,返回玩家手中牌在閾值的條件下,最具有優(yōu)勢(shì)的點(diǎn)數(shù)仅父。正如上面函數(shù)說(shuō)明中敘述的那樣叛薯,相同的牌可能代表不同的點(diǎn)數(shù),閾值代表了玩家主觀(guān)的判斷笙纤,例如:玩家認(rèn)為17點(diǎn)已經(jīng)很大了不需要再抽牌了耗溜,也就是閾值取17,那么如果他手中的牌為 [1, 7], 那么這個(gè)玩家就不會(huì)繼續(xù)抽牌了省容,如果閾值取為 19 那么這個(gè)玩家就會(huì)認(rèn)為 [1, 7] 代表了18 那么他就可以繼續(xù)抽牌期望點(diǎn)數(shù)超過(guò)19抖拴。
    下圖為主函數(shù)的流程圖:


    主函數(shù)流程圖
def whats_the_point (cards, k):
    '''
    這個(gè)函數(shù)可以輸出**玩家**最有可能的點(diǎn)數(shù)
    輸入:points:點(diǎn)數(shù)可能列表
        k: 閾值
    輸出:返回最有可能的分?jǐn)?shù)(整數(shù))
    '''
    points = count_points(cards)
    if(points[-1]<k):
        return points[-1]
    elif(points[-1]>21):
        if len(points)==1:
            return points[-1]
        points.pop(-1)
        return whats_the_point (points, k)
    elif(points[-1]>=k and points[-1]<=21):
        return points[-1]
  1. black_jack_game(k)
    主函數(shù),輸入一個(gè)整數(shù)蓉冈,返回一個(gè)整數(shù)(包括 -3, 3, 2城舞,-2, 0)
    這個(gè)函數(shù)調(diào)用了前三個(gè)函數(shù)轩触,根據(jù)輸入的閾值寞酿,返回一次游戲所得點(diǎn)數(shù)。
def black_jack_game(k):
    # 初始化全部撲克牌脱柱,保存在列表中
    desktop = [10]*32
    for i in [1,2,3,4,5,6,7,8,9]:
        desktop = desktop + [i]*8

    # 初始的兩張牌    
    banker_cards = list([get_the_card(desktop),get_the_card(desktop)])
    player_cards = list([get_the_card(desktop),get_the_card(desktop)])
#     print('第一次發(fā)牌時(shí)玩家手中的牌是', player_cards)     # 檢查點(diǎn)
#     print('第一次發(fā)牌時(shí)莊家手中的牌是', banker_cards)     # 檢查點(diǎn)
    banker_points = whats_the_point(banker_cards,k)
    player_points = whats_the_point(player_cards,k)
#     print('第一次發(fā)牌時(shí)玩家手中的牌是', player_cards)     # 檢查點(diǎn)
#     print('第一次發(fā)牌時(shí)莊家手中的牌是', banker_cards)     # 檢查點(diǎn)
#     print('第一次發(fā)牌時(shí)玩家的點(diǎn)數(shù)是', player_points)     # 檢查點(diǎn)
#     print('第一次發(fā)牌時(shí)莊家的點(diǎn)數(shù)是', banker_points)     # 檢查點(diǎn)
    # 判斷是否獲勝
    if (banker_points == 21):
        return -3
    if (player_points == 21):
        return 3
    if(banker_points == 21 or(player_points == 21)):
        return points
    # 如果沒(méi)有獲勝則繼續(xù)抽牌直到點(diǎn)數(shù)達(dá)到了k值
    while True:
        banker_cards = banker_cards + list([get_the_card(desktop)])
        if(whats_the_point(banker_cards,k)>=k):
            break
    while True:
        player_cards = player_cards + list([get_the_card(desktop)])
#        print(player_cards)     # 檢查點(diǎn)
        if(whats_the_point(player_cards,k)>=k):
#             print('此時(shí)玩家手中的牌是', player_cards)     # 檢查點(diǎn)
#             print('此時(shí)玩家手中的點(diǎn)數(shù)是', banker_cards)     # 檢查點(diǎn)
#             print('此時(shí)玩家手中的點(diǎn)數(shù)是', whats_the_point(player_cards,k))     # 檢查點(diǎn)
#             print('此時(shí)莊家手中的點(diǎn)數(shù)是', whats_the_point(banker_cards,k))     # 檢查點(diǎn)
            break
    if whats_the_point(player_cards,k)>21:
        return -2
    if whats_the_point(banker_cards,k)>21:
        return 2
    if whats_the_point(player_cards,k)<whats_the_point(banker_cards,k):
        return -2
    elif whats_the_point(player_cards,k)>whats_the_point(banker_cards,k):
        return 2
    elif whats_the_point(player_cards,k)==whats_the_point(banker_cards,k):
        return 0

運(yùn)行結(jié)果:

下表即為程序運(yùn)行 12 次的運(yùn)行結(jié)果:


運(yùn)行結(jié)果

為了查看不同閾值對(duì)結(jié)果的影響設(shè)計(jì)了下面的程序:

point_list = []
sumPoint = 0
for k in range(1,22):
    for i in range(0,1000):
        sumPoint = sumPoint + black_jack_game(k)
    point_list = point_list + [sumPoint]

我們對(duì)0到22的閾值分別取了1000次實(shí)驗(yàn)結(jié)果伐弹,實(shí)驗(yàn)結(jié)果如下圖所示,x軸為閾值榨为,y軸為1000次模擬實(shí)驗(yàn)結(jié)果的總和惨好。從結(jié)果中可以看出閾值越小,勝利的概率越大随闺。


不同閾值下的結(jié)果
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末日川,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子矩乐,更是在濱河造成了極大的恐慌龄句,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件散罕,死亡現(xiàn)場(chǎng)離奇詭異分歇,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)欧漱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)职抡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人误甚,你說(shuō)我怎么就攤上這事缚甩∑拙唬” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵擅威,是天一觀(guān)的道長(zhǎng)岳遥。 經(jīng)常有香客問(wèn)我,道長(zhǎng)裕寨,這世上最難降的妖魔是什么浩蓉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮宾袜,結(jié)果婚禮上捻艳,老公的妹妹穿的比我還像新娘。我一直安慰自己庆猫,他們只是感情好认轨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著月培,像睡著了一般嘁字。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上杉畜,一...
    開(kāi)封第一講書(shū)人閱讀 51,590評(píng)論 1 305
  • 那天纪蜒,我揣著相機(jī)與錄音,去河邊找鬼此叠。 笑死纯续,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的灭袁。 我是一名探鬼主播猬错,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼茸歧!你這毒婦竟也來(lái)了倦炒?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤软瞎,失蹤者是張志新(化名)和其女友劉穎逢唤,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體铜涉,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡智玻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了芙代。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吊奢。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出页滚,到底是詐尸還是另有隱情召边,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布裹驰,位于F島的核電站隧熙,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏幻林。R本人自食惡果不足惜贞盯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沪饺。 院中可真熱鬧躏敢,春花似錦、人聲如沸整葡。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)遭居。三九已至啼器,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間俱萍,已是汗流浹背端壳。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鼠次,地道東北人更哄。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像腥寇,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子觅捆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容