• 用python开发一个炸金花小游戏


    众所周知扑克牌可谓是居家旅行、桌面交友的必备道具,

    今天我们用 Python 来实现一个类似炸金花的扑克牌小游戏,先来看一下基本的游戏规则

    炸(诈)金花又叫三张牌,是在全国广泛流传的一种民间多人纸牌游戏。游戏使用一副除去大小王的扑克牌,共 4 个花色 52 张牌,各个玩家从中抽取 3 张牌,比较大小。各种牌型的大小顺序如下(按照全排列组合中出现的概率越小,牌型分数奖励越大):1、同花顺:三张同样花色且点数连续的牌,如红心2、红心3、红心4;2、豹子:三张点数一样的牌,如 AAA、222;3、顺子:三张点数连续的牌,如红心2、黑桃3、方块4;4、金花:三张同样花色的牌,如红心2、红心5、红心8;5、对子:两张点数一样的牌,如红心2、黑桃2;6、单张:2~10 < J < Q < K < A。以下概率截自百度百科:

    图片

    注:本文所述游戏规则与实际有所不同,主要基于对不同牌型的比较进行设计

    一、游戏流程实现

    图片

    1、准备扑克牌

    开始游戏前,需要先生成一副满足要求的扑克牌,牌友们都知道,扑克牌有以下四种花色,每种花色有 A、2~10、J、Q、K 等 13 张牌。

    1. suit = ["黑桃""红心""方块""梅花"]
    2. num = [str(i) for i in range(211)] + ["J""Q""K""A"]

    为了便于后续算分,先给每一个单张赋予相应的点数。

    1. score_map = {}  # 单张点数映射表
    2. for s in suit:
    3.     count = 2
    4.     for n in num:
    5.         score_map[f"{s}{n}"= count
    6.         count += 1

    扑克牌点数预览如下:

    score_map  =  {'黑桃2': 2, '黑桃3': 3, '黑桃4': 4, '黑桃5': 5, '黑桃6': 6, '黑桃7': 7, '黑桃8': 8, '黑桃9': 9,  '黑桃10': 10, '黑桃J': 11, '黑桃Q': 12, '黑桃K': 13, '黑桃A': 14, '红心2': 2, ... }

    2、玩家入场

    以 p1、p2 等名称对玩家进行区分,我们先邀请 5 个玩家入场。

    players = [f"p{i}" for i in range(16)]
    

    3、发牌

    将玩家和扑克牌列表作为参数,传入发牌器。发牌器在扑克牌中进行不放回抽取,为每个玩家随机抽取 3 张牌,并记下玩家名称及其对应牌组。

    1. def get_pk_lst(pls, pks):
    2.     result = []
    3.     for p in pls:
    4.         pk = sample(pks, 3)
    5.         for _pk in pk:
    6.             pks.remove(_pk)
    7.         result.append({"name": p, "poker": pk})
    8.     return result
    9. pokers = list(score_map.keys())  # 去掉大小王的一幅扑克
    10. poker_grp = get_pk_lst(players, pokers)  # 发牌

    发牌预览如下:

    result = [{'name': 'p1', 'poker': ['方块5', '梅花3', '方块A']}, {'name': 'p2', 'poker': ['黑桃4', '方块8', '黑桃J']}, {'name': 'p3', 'poker': ['红心10', '红心K', '方块7']}, {'name': 'p4', 'poker': ['方块4', '梅花6', '方块J']}, {'name': 'p5', 'poker': ['红心5', '梅花10', '黑桃A']}]

    4、判断牌型及算分

    在算分之前先按之前的映射字典,将 pk_lst 里的 3 张扑克牌转换成对应的点数。

    n_lst = list(map(lambda x: score_map[x], pk_lst))  # 点数映射
    

    接下来截取花色部分的文本,利用集合去重后判断是否为三张同花。

    same_suit = len(set([pk[:2for pk in pk_lst])) == 1  # 是否同花色
    

    再对点数部分进行排序,与依靠点数的最值生成的顺序列表进行比较,判断是否为连续的点数。要注意的是,A23 与 QKA 一样被视作顺子。

    continuity = sorted(n_lst) == [i for i in range(min(n_lst), max(n_lst) + 1)] or set(n_lst) == {1423}  # 是否连续
    

    别忘了考虑对子和豹子的检查方式。

    check = len(set(n_lst))  # 重复情况
    

    那么正式开始判断牌型和算分吧!首先是单张,非同花、非顺子、三张点数不一。得分以 3 个单张点数相加。

    1. if not same_suit and not continuity and check == 3:
    2.     return sum(n_lst), "单张"

    其次是对子,非同花,有且仅有两张点数一致。得分中对于构成对子的部分给予 2 倍奖励。

    1. if not same_suit and check == 2:
    2.     w = [i for i in n_lst if n_lst.count(i) == 2][0]
    3.     single = [i for i in n_lst if i != w][0]
    4.     return w*2*2 + single, "对子"

    金花,即同花而非顺子,给予 9 倍奖励。

    1. if same_suit and not continuity:
    2.     return sum(n_lst)*9"金花"

    顺子,即点数连续而非同花,给予 81 倍奖励。

    1. if continuity and not same_suit:
    2.     return sum(n_lst)*81"顺子"

    豹子,即三张点数一致,这不得刷个 666 嘛。

    1. if check == 1:
    2.     return sum(n_lst)*666"豹子"

    同花顺,同花色且点数连续,绝了,赌神一个技能 999 伤害。

    1. if continuity and same_suit:
    2.     return sum(n_lst)*999"同花顺"

    5、决出胜负

    一组玩家、抽牌、算分、牌型记录如下:

    pk_grp = [{'name': 'p1', 'poker': ['方块5', '梅花3', '方块A'], 'score': 22, 'type': '单张'}, {'name': 'p2', 'poker': ['黑桃4', '方块8', '黑桃J'], 'score': 23, 'type': '单张'}, {'name': 'p3', 'poker': ['红心10', '红心K', '方块7'], 'score': 30, 'type': '单张'}, {'name': 'p4', 'poker': ['方块4', '梅花6', '方块J'], 'score': 21, 'type': '单张'}, {'name': 'p5', 'poker': ['红心5', '梅花10', '黑桃A'], 'score': 29, 'type': '单张'}]

    利用 max 函数找出来谁是最棒的,公布名字!

    best = max(pk_grp, key=lambda x: x["score"])["name"]
    

    赢家是------ p3

    好啦,又可以开始下一场愉快的游戏了~

    二、统计及源码

    图片

    1、牌型统计

    进行了 10 万场游戏并对各类牌型进行频率统计,可见与前述排列组合的计算所得概率基本一致。另外,搜索公众号Linux就该这样学后台回复“猴子”,获取一份惊喜礼包。

    1. Counter({'单张'371856'对子'84773'金花'24833'顺子'16239'豹子'1179'同花顺'1120})
    2. 单张频率:74.37%
    3. 对子频率:16.95%
    4. 金花频率:4.97%
    5. 顺子频率:3.25%
    6. 豹子频率:0.24%
    7. 同花顺频率:0.22%

    2、牌局案例

    各类牌型的局面和结果如下:

    1. 开牌结果------
    2. {'name''p1''poker': ['方块5''梅花3''方块A'], 'score'22'type''单张'}
    3. {'name''p2''poker': ['黑桃4''方块8''黑桃J'], 'score'23'type''单张'}
    4. {'name''p3''poker': ['红心10''红心K''方块7'], 'score'30'type''单张'}
    5. {'name''p4''poker': ['方块4''梅花6''方块J'], 'score'21'type''单张'}
    6. {'name''p5''poker': ['红心5''梅花10''黑桃A'], 'score'29'type''单张'}
    7. 赢家是------
    8. p3
    9. 开牌结果------
    10. {'name''p1''poker': ['方块Q''黑桃5''黑桃K'], 'score'30'type''单张'}
    11. {'name''p2''poker': ['黑桃2''方块2''红心10'], 'score'18'type''对子'}
    12. {'name''p3''poker': ['梅花2''黑桃4''梅花J'], 'score'17'type''单张'}
    13. {'name''p4''poker': ['红心K''梅花7''红心6'], 'score'26'type''单张'}
    14. {'name''p5''poker': ['方块A''方块6''红心4'], 'score'24'type''单张'}
    15. 赢家是------
    16. p1
    17. 开牌结果------
    18. {'name''p1''poker': ['黑桃J''黑桃5''黑桃4'], 'score'180'type''金花'}
    19. {'name''p2''poker': ['梅花7''红心4''梅花5'], 'score'16'type''单张'}
    20. {'name''p3''poker': ['方块5''黑桃9''梅花10'], 'score'24'type''单张'}
    21. {'name''p4''poker': ['黑桃Q''梅花9''黑桃10'], 'score'31'type''单张'}
    22. {'name''p5''poker': ['红心9''方块9''红心A'], 'score'50'type''对子'}
    23. 赢家是------
    24. p1
    25. 开牌结果------
    26. {'name''p1''poker': ['方块8''黑桃10''方块9'], 'score'2187'type''顺子'}
    27. {'name''p2''poker': ['梅花9''红心Q''黑桃3'], 'score'24'type''单张'}
    28. {'name''p3''poker': ['方块A''梅花K''黑桃4'], 'score'31'type''单张'}
    29. {'name''p4''poker': ['方块J''红心J''红心6'], 'score'50'type''对子'}
    30. {'name''p5''poker': ['梅花5''黑桃K''方块3'], 'score'21'type''单张'}
    31. 赢家是------
    32. p1
    33. 开牌结果------
    34. {'name''p1''poker': ['黑桃Q''黑桃8''梅花6'], 'score'26'type''单张'}
    35. {'name''p2''poker': ['红心3''梅花3''黑桃3'], 'score'5994'type''豹子'}
    36. {'name''p3''poker': ['红心A''红心6''方块5'], 'score'25'type''单张'}
    37. {'name''p4''poker': ['黑桃4''梅花A''方块2'], 'score'20'type''单张'}
    38. {'name''p5''poker': ['梅花7''黑桃6''梅花8'], 'score'1701'type''顺子'}
    39. 赢家是------
    40. p2
    41. 开牌结果------
    42. {'name''p1''poker': ['黑桃5''梅花9''方块9'], 'score'41'type''对子'}
    43. {'name''p2''poker': ['黑桃Q''黑桃2''红心Q'], 'score'50'type''对子'}
    44. {'name''p3''poker': ['红心2''黑桃7''红心5'], 'score'14'type''单张'}
    45. {'name''p4''poker': ['梅花3''方块10''黑桃A'], 'score'27'type''单张'}
    46. {'name''p5''poker': ['黑桃9''黑桃J''黑桃10'], 'score'29970'type''同花顺'}
    47. 赢家是------
    48. p5

    3、完整代码

    1. # @Seon
    2. # 炸金花
    3. from random import sample
    4. from collections import Counter
    5. def get_pk_lst(pls, pks):  # 发牌
    6.     result = []
    7.     for p in pls:
    8.         pk = sample(pks, 3)
    9.         for _pk in pk:
    10.             pks.remove(_pk)
    11.         result.append({"name": p, "poker": pk})
    12.     return result
    13. def calculate(_score_map, pk_lst):  # 返回得分和牌型
    14.     n_lst = list(map(lambda x: _score_map[x], pk_lst))  # 点数映射
    15.     same_suit = len(set([pk[:2for pk in pk_lst])) == 1  # 是否同花色
    16.     continuity = sorted(n_lst) == [i for i in range(min(n_lst), max(n_lst) + 1)] or set(n_lst) == {1423}  # 是否连续
    17.     check = len(set(n_lst))  # 重复情况
    18.     if not same_suit and not continuity and check == 3:
    19.         return sum(n_lst), "单张"
    20.     if not same_suit and check == 2:
    21.         w = [i for i in n_lst if n_lst.count(i) == 2][0]
    22.         single = [i for i in n_lst if i != w][0]
    23.         return w*2*2 + single, "对子"
    24.     if same_suit and not continuity:
    25.         return sum(n_lst)*9"金花"
    26.     if continuity and not same_suit:
    27.         return sum(n_lst)*81"顺子"
    28.     if check == 1:
    29.         return sum(n_lst)*666"豹子"
    30.     if continuity and same_suit:
    31.         return sum(n_lst)*999"同花顺"
    32. def compare(_score_map, pk_grp):  # 比大小
    33.     for p in pk_grp:
    34.         p["score"], p["type"= calculate(_score_map, p["poker"])
    35.     print("开牌结果------")
    36.     for p in pk_grp:
    37.         print(p)
    38.     print("赢家是------")
    39.     best = max(pk_grp, key=lambda x: x["score"])["name"]
    40.     print(best)
    41.     return pk_grp
    42. def show(_score_map, _players):   # 开局
    43.     pokers = list(_score_map.keys())
    44.     poker_grp = get_pk_lst(_players, pokers)
    45.     return compare(_score_map, poker_grp)
    46. def start_game(_score_map, _players, freq=1):   # 游戏和统计
    47.     type_lst = []
    48.     for i in range(freq):
    49.         grp = show(_score_map, _players)
    50.         type_lst = type_lst + [t["type"for t in grp]
    51.     c = Counter(type_lst)
    52.     print(c)
    53.     total = sum(c.values())
    54.     for item in c.items():
    55.         print(f"{item[0]}频率:{item[1]/total:.2%}")
    56. if __name__ == '__main__':
    57.     # 准备扑克牌
    58.     suit = ["黑桃""红心""方块""梅花"]
    59.     num = [str(i) for i in range(211)] + ["J""Q""K""A"]
    60.     score_map = {}  # 单张点数映射表
    61.     for s in suit:
    62.         count = 2
    63.         for n in num:
    64.             score_map[f"{s}{n}"= count
    65.             count += 1
    66.     # 5个玩家入场
    67.     players = [f"p{i}" for i in range(16)]
    68.     # 开始游戏
    69.     start_game(score_map, players, freq=100000)
     
    

  • 相关阅读:
    java计算机毕业设计高校科研信息管理系统MyBatis+系统+LW文档+源码+调试部署
    解决谷歌学术bib信息不全的问题
    SQL注入攻击分为几类?如何防御?
    如何通过更好的文档管理减轻小型企业的压力
    Node.js | 内置模块 http | fs | path 综合小案例:分离HTML文件
    Json转换工具类
    LeetCode 54 螺旋矩阵
    生死时速,分秒必争
    centos编译升级cmake,痛苦的Linux小白
    Azure - 自动化机器学习AutoML Azure使用详解
  • 原文地址:https://blog.csdn.net/liuxing__jacker/article/details/132692958