python蒙特卡洛模拟_基于Python的21点游戏蒙特卡洛模拟
問題重述:
大多數(shù)賭場使用6副牌或8副牌玩這種游戲,以防止“數(shù)牌點”,在你的模擬中使用兩副牌(共104張)。只有2位參與者,你和莊家。游戲開始時每人得到兩張牌,對于牌面為2~10的牌,點數(shù)和面數(shù)相同;對于為人臉(J、Q、K)的牌,點數(shù)為10;牌面為A的牌,點數(shù)為1或者11.游戲的目的是得到總數(shù)盡量接近21點的牌,不得超過(超過稱“爆了”),并使你得到的總點數(shù)多于莊家。
如果開始兩張牌的總點數(shù)恰為21(A-10或A-人臉),稱為21點,自動成為勝者(若你和莊家都得到21點,則為平局,你的賭注仍在臺上)。靠21點贏時,付給你3賠2,即1.5賠1(1元賭注贏1.5元,且1元賭注仍保留)。
如果你和莊家都未得到21點,你想要多少張牌就可以取多少張牌,一次一張,使總數(shù)盡量接近21點,如果你超過了21點,就輸了,游戲結(jié)束。一旦你對牌的點數(shù)滿意,你就“打住”,然后莊家按照下列規(guī)則取牌:
當(dāng)莊家牌的點數(shù)為17、18、19、20和21時,就打住。若莊家牌的點數(shù)小于或等于16,必然取牌。莊家總把A的點數(shù)記為11,除非這樣使他或她爆了(這時A的點數(shù)記為1)。例如,莊家的A-6組合是17點,不是7點(莊家沒有選擇權(quán)),且莊家必須打住在17點上。而若莊家有A-4組合(15點),又拿了一張K,那么新的總點數(shù)是15,因為A回到點數(shù)1(使之不超過21點) ,莊家還要再取牌。
如果莊家超過21點,你就贏了(贏賭注的錢,每1元賭注贏1元)。如果莊家的總點數(shù)超過你,你將輸?shù)羧抠€注。如果莊家和你的總點數(shù)相同,為平局(你不輸也不贏)。
賭場中這個游戲的刺激之處在于,莊家開始的兩張牌一張明、一張暗,所以你不知道莊家牌地總點數(shù),必須根據(jù)那張明牌賭一把。在這個項目模擬中你不用考慮這種情況,你需要做的是:用兩副牌做12次游戲,你有無限的賭資,每次下賭2元。兩副牌玩過一次之后,用兩副新牌繼續(xù)玩,這時記錄你的得分,然后下一幅牌從0開始,輸出是12個結(jié)果,你可以用平均數(shù)決定你的總成績。
函數(shù)說明與實現(xiàn):
get_the_card( a )
輸入一個列表,返回一個整數(shù)。
這個函數(shù)可以隨機(jī)地從總撲克牌列表(列表名為:desktop)中獲取一張撲克牌,并將其從撲克牌列表desktop中刪除(因為同一張撲克牌不能取兩次)。返回新獲取到的撲克牌的值。
def get_the_card(a):
'''
從桌面上獲取一張撲克牌,并將其從撲克牌列表中刪除。
輸入一個列表,返回隨機(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 )
輸入一個列表,返回一個列表。
這個函數(shù)輸入玩家手中的撲克牌列表。(列表名為:player_cards 和 banker_cards),返回玩家手中牌所代表分值的所有可能(列表),不同的可能是由于A牌既可以看成是1也可以看成是11造成的。例如玩家手中的牌是 [1, 1, 1] 那么所有可能為 [3, 13, 23, 33]。
def count_points(a):
'''
這個函數(shù)用于計算玩家手中的牌點數(shù)的所有可能
輸入:輸入一個列表
輸出:輸出一個列表
'''
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)
輸入一個列表,一個整數(shù),返回一個整數(shù)。這個函數(shù)調(diào)用了上面的函數(shù)。
如果點數(shù)超過21點則返回-2,如果點數(shù)剛好是21,返回-1,如果點數(shù)介于[0, 19]之間則直接返回點數(shù)。
這個函數(shù)輸入的是玩家手中撲克牌的列表和一個閾值,返回玩家手中牌在閾值的條件下,最具有優(yōu)勢的點數(shù)。正如上面函數(shù)說明中敘述的那樣,相同的牌可能代表不同的點數(shù),閾值代表了玩家主觀的判斷,例如:玩家認(rèn)為17點已經(jīng)很大了不需要再抽牌了,也就是閾值取17,那么如果他手中的牌為 [1, 7], 那么這個玩家就不會繼續(xù)抽牌了,如果閾值取為 19 那么這個玩家就會認(rèn)為 [1, 7] 代表了18 那么他就可以繼續(xù)抽牌期望點數(shù)超過19。
下圖為主函數(shù)的流程圖:
主函數(shù)流程圖
def whats_the_point (cards, k):
'''
這個函數(shù)可以輸出**玩家**最有可能的點數(shù)
輸入:points:點數(shù)可能列表
k: 閾值
輸出:返回最有可能的分?jǐn)?shù)(整數(shù))
'''
points = count_points(cards)
if(points[-1]
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ù),輸入一個整數(shù),返回一個整數(shù)(包括 -3, 3, 2,-2, 0)
這個函數(shù)調(diào)用了前三個函數(shù),根據(jù)輸入的閾值,返回一次游戲所得點數(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ā)牌時玩家手中的牌是', player_cards) # 檢查點
# print('第一次發(fā)牌時莊家手中的牌是', banker_cards) # 檢查點
banker_points = whats_the_point(banker_cards,k)
player_points = whats_the_point(player_cards,k)
# print('第一次發(fā)牌時玩家手中的牌是', player_cards) # 檢查點
# print('第一次發(fā)牌時莊家手中的牌是', banker_cards) # 檢查點
# print('第一次發(fā)牌時玩家的點數(shù)是', player_points) # 檢查點
# print('第一次發(fā)牌時莊家的點數(shù)是', banker_points) # 檢查點
# 判斷是否獲勝
if (banker_points == 21):
return -3
if (player_points == 21):
return 3
if(banker_points == 21 or(player_points == 21)):
return points
# 如果沒有獲勝則繼續(xù)抽牌直到點數(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) # 檢查點
if(whats_the_point(player_cards,k)>=k):
# print('此時玩家手中的牌是', player_cards) # 檢查點
# print('此時玩家手中的點數(shù)是', banker_cards) # 檢查點
# print('此時玩家手中的點數(shù)是', whats_the_point(player_cards,k)) # 檢查點
# print('此時莊家手中的點數(shù)是', whats_the_point(banker_cards,k)) # 檢查點
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)
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
運行結(jié)果:
下表即為程序運行 12 次的運行結(jié)果:
運行結(jié)果
為了查看不同閾值對結(jié)果的影響設(shè)計了下面的程序:
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]
我們對0到22的閾值分別取了1000次實驗結(jié)果,實驗結(jié)果如下圖所示,x軸為閾值,y軸為1000次模擬實驗結(jié)果的總和。從結(jié)果中可以看出閾值越小,勝利的概率越大。
不同閾值下的結(jié)果
總結(jié)
以上是生活随笔為你收集整理的python蒙特卡洛模拟_基于Python的21点游戏蒙特卡洛模拟的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 用if判断一个数是不是整数
- 下一篇: mysql8支持myISAM_mysql