問(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):
- 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
- 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
-
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]
- 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é)果:
為了查看不同閾值對(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é)果中可以看出閾值越小,勝利的概率越大随闺。