基于Python的21点游戏蒙特卡洛模拟

问题重述:

大多数赌场使用6副牌或8副牌玩这种游戏,以防止“数牌点”,在你的模拟中使用两副牌(共104张)。只有2位参与者,你和庄家。游戏开始时每人得到两张牌,对于牌面为2~10的牌,点数和面数相同;对于为人脸(J、Q、K)的牌,点数为10;牌面为A的牌,点数为1或者11.游戏的目的是得到总数尽量接近21点的牌,不得超过(超过称“爆了”),并使你得到的总点数多于庄家。             
如果开始两张牌的总点数恰为21(A-10或A-人脸),称为21点,自动成为胜者(若你和庄家都得到21点,则为平局,你的赌注仍在台上)。靠21点赢时,付给你3赔2,即1.5赔1(1元赌注赢1.5元,且1元赌注仍保留)。
如果你和庄家都未得到21点,你想要多少张牌就可以取多少张牌,一次一张,使总数尽量接近21点,如果你超过了21点,就输了,游戏结束。一旦你对牌的点数满意,你就“打住”,然后庄家按照下列规则取牌:
当庄家牌的点数为17、18、19、20和21时,就打住。若庄家牌的点数小于或等于16,必然取牌。庄家总把A的点数记为11,除非这样使他或她爆了(这时A的点数记为1)。例如,庄家的A-6组合是17点,不是7点(庄家没有选择权),且庄家必须打住在17点上。而若庄家有A-4组合(15点),又拿了一张K,那么新的总点数是15,因为A回到点数1(使之不超过21点) ,庄家还要再取牌。
如果庄家超过21点,你就赢了(赢赌注的钱,每1元赌注赢1元)。如果庄家的总点数超过你,你将输掉全部赌注。如果庄家和你的总点数相同,为平局(你不输也不赢)。
赌场中这个游戏的刺激之处在于,庄家开始的两张牌一张明、一张暗,所以你不知道庄家牌地总点数,必须根据那张明牌赌一把。在这个项目模拟中你不用考虑这种情况,你需要做的是:用两副牌做12次游戏,你有无限的赌资,每次下赌2元。两副牌玩过一次之后,用两副新牌继续玩,这时记录你的得分,然后下一幅牌从0开始,输出是12个结果,你可以用平均数决定你的总成绩。

函数说明与实现:

  1. get_the_card( a )
    输入一个列表,返回一个整数。
    这个函数可以随机地从总扑克牌列表(列表名为:desktop)中获取一张扑克牌,并将其从扑克牌列表desktop中删除(因为同一张扑克牌不能取两次)。返回新获取到的扑克牌的值。
def get_the_card(a):
    '''
    从桌面上获取一张扑克牌,并将其从扑克牌列表中删除。
    输入一个列表,返回随机获取的扑克牌值。
    '''
    n = len(a)
    if n == 0:
        sys.exit(1)
    rand_num = np.random.randint(0, n)  # 从现有的扑克牌中随机抽取一张
    num = a.pop(rand_num)               # 弹出选中的那一张
    return num
  1. count_points( a )
    输入一个列表,返回一个列表。
    这个函数输入玩家手中的扑克牌列表。(列表名为:player_cards 和 banker_cards),返回玩家手中牌所代表分值的所有可能(列表),不同的可能是由于A牌既可以看成是1也可以看成是11造成的。例如玩家手中的牌是 [1, 1, 1] 那么所有可能为 [3, 13, 23, 33]。
def count_points(a):
    '''
    这个函数用于计算玩家手中的牌点数的所有可能
    输入:输入一个列表
    输出:输出一个列表
    '''
    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)
    输入一个列表,一个整数,返回一个整数。这个函数调用了上面的函数。
    如果点数超过21点则返回-2,如果点数刚好是21,返回-1,如果点数介于[0, 19]之间则直接返回点数。
    这个函数输入的是玩家手中扑克牌的列表和一个阈值,返回玩家手中牌在阈值的条件下,最具有优势的点数。正如上面函数说明中叙述的那样,相同的牌可能代表不同的点数,阈值代表了玩家主观的判断,例如:玩家认为17点已经很大了不需要再抽牌了,也就是阈值取17,那么如果他手中的牌为 [1, 7], 那么这个玩家就不会继续抽牌了,如果阈值取为 19 那么这个玩家就会认为 [1, 7] 代表了18 那么他就可以继续抽牌期望点数超过19。
    下图为主函数的流程图:


    主函数流程图
def whats_the_point (cards, k):
    '''
    这个函数可以输出**玩家**最有可能的点数
    输入:points:点数可能列表
        k: 阈值
    输出:返回最有可能的分数(整数)
    '''
    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)
    主函数,输入一个整数,返回一个整数(包括 -3, 3, 2,-2, 0)
    这个函数调用了前三个函数,根据输入的阈值,返回一次游戏所得点数。
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('第一次发牌时玩家手中的牌是', player_cards)     # 检查点
#     print('第一次发牌时庄家手中的牌是', banker_cards)     # 检查点
    banker_points = whats_the_point(banker_cards,k)
    player_points = whats_the_point(player_cards,k)
#     print('第一次发牌时玩家手中的牌是', player_cards)     # 检查点
#     print('第一次发牌时庄家手中的牌是', banker_cards)     # 检查点
#     print('第一次发牌时玩家的点数是', player_points)     # 检查点
#     print('第一次发牌时庄家的点数是', banker_points)     # 检查点
    # 判断是否获胜
    if (banker_points == 21):
        return -3
    if (player_points == 21):
        return 3
    if(banker_points == 21 or(player_points == 21)):
        return points
    # 如果没有获胜则继续抽牌直到点数达到了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('此时玩家手中的点数是', banker_cards)     # 检查点
#             print('此时玩家手中的点数是', whats_the_point(player_cards,k))     # 检查点
#             print('此时庄家手中的点数是', 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)<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

运行结果:

下表即为程序运行 12 次的运行结果:


运行结果

为了查看不同阈值对结果的影响设计了下面的程序:

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次实验结果,实验结果如下图所示,x轴为阈值,y轴为1000次模拟实验结果的总和。从结果中可以看出阈值越小,胜利的概率越大。


不同阈值下的结果
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容