Python开发实战-用python开发一个炸金花小游戏
小职 2021-09-23 来源 :「Seon_Pan」 阅读 804 评论 0

摘要:本文主要介绍了Python开发实战-用python开发一个炸金花小游戏,通过具体的内容展现,希望对大家Python开发的学习有所帮助。

本文主要介绍了Python开发实战-用python开发一个炸金花小游戏,通过具体的内容展现,希望对大家Python开发的学习有所帮助。

Python开发实战-用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。

以下概率截自百度百科:

Python开发实战-用python开发一个炸金花小游戏

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


一、游戏流程实现

1、准备扑克牌

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


suit = ["黑桃", "红心", "方块", "梅花"]

num = [str(i) for i in range(2, 11)] + ["J", "Q", "K", "A"]


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


score_map = {}  # 单张点数映射表

for s in suit:

    count = 2

    for n in num:

        score_map[f"{s}{n}"] = count

        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(1, 6)]


3、发牌

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


def get_pk_lst(pls, pks):

    result = []

    for p in pls:

        pk = sample(pks, 3)

        for _pk in pk:

            pks.remove(_pk)

        result.append({"name": p, "poker": pk})

    return result


pokers = list(score_map.keys())  # 去掉大小王的一幅扑克

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[:2] for 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) == {14, 2, 3}  # 是否连续


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


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


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


if not same_suit and not continuity and check == 3:

    return sum(n_lst), "单张"


其次是对子,非同花,有且仅有两张点数一致。为保证


if not same_suit and check == 2:

    w = [i for i in n_lst if n_lst.count(i) == 2][0]

    single = [i for i in n_lst if i != w][0]

    return w*2*2 + single, "对子"


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


if same_suit and not continuity:

    return sum(n_lst)*9, "金花"


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


if continuity and not same_suit:

    return sum(n_lst)*81, "顺子"


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


if check == 1:

    return sum(n_lst)*666, "豹子"


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


if continuity and same_suit:

    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 万场游戏并对各类牌型进行频率统计,可见与前述排列组合的计算所得概率基本一致。


Counter({'单张': 371856, '对子': 84773, '金花': 24833, '顺子': 16239, '豹子': 1179, '同花顺': 1120})

单张频率:74.37%

对子频率:16.95%

金花频率:4.97%

顺子频率:3.25%

豹子频率:0.24%

同花顺频率:0.22%


2、牌局案例

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


开牌结果------

{'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': '单张'}

赢家是------

p3



开牌结果------

{'name': 'p1', 'poker': ['方块Q', '黑桃5', '黑桃K'], 'score': 30, 'type': '单张'}

{'name': 'p2', 'poker': ['黑桃2', '方块2', '红心10'], 'score': 18, 'type': '对子'}

{'name': 'p3', 'poker': ['梅花2', '黑桃4', '梅花J'], 'score': 17, 'type': '单张'}

{'name': 'p4', 'poker': ['红心K', '梅花7', '红心6'], 'score': 26, 'type': '单张'}

{'name': 'p5', 'poker': ['方块A', '方块6', '红心4'], 'score': 24, 'type': '单张'}

赢家是------

p1



开牌结果------

{'name': 'p1', 'poker': ['黑桃J', '黑桃5', '黑桃4'], 'score': 180, 'type': '金花'}

{'name': 'p2', 'poker': ['梅花7', '红心4', '梅花5'], 'score': 16, 'type': '单张'}

{'name': 'p3', 'poker': ['方块5', '黑桃9', '梅花10'], 'score': 24, 'type': '单张'}

{'name': 'p4', 'poker': ['黑桃Q', '梅花9', '黑桃10'], 'score': 31, 'type': '单张'}

{'name': 'p5', 'poker': ['红心9', '方块9', '红心A'], 'score': 50, 'type': '对子'}

赢家是------

p1



开牌结果------

{'name': 'p1', 'poker': ['方块8', '黑桃10', '方块9'], 'score': 2187, 'type': '顺子'}

{'name': 'p2', 'poker': ['梅花9', '红心Q', '黑桃3'], 'score': 24, 'type': '单张'}

{'name': 'p3', 'poker': ['方块A', '梅花K', '黑桃4'], 'score': 31, 'type': '单张'}

{'name': 'p4', 'poker': ['方块J', '红心J', '红心6'], 'score': 50, 'type': '对子'}

{'name': 'p5', 'poker': ['梅花5', '黑桃K', '方块3'], 'score': 21, 'type': '单张'}

赢家是------

p1



开牌结果------

{'name': 'p1', 'poker': ['黑桃Q', '黑桃8', '梅花6'], 'score': 26, 'type': '单张'}

{'name': 'p2', 'poker': ['红心3', '梅花3', '黑桃3'], 'score': 5994, 'type': '豹子'}

{'name': 'p3', 'poker': ['红心A', '红心6', '方块5'], 'score': 25, 'type': '单张'}

{'name': 'p4', 'poker': ['黑桃4', '梅花A', '方块2'], 'score': 20, 'type': '单张'}

{'name': 'p5', 'poker': ['梅花7', '黑桃6', '梅花8'], 'score': 1701, 'type': '顺子'}

赢家是------

p2



开牌结果------

{'name': 'p1', 'poker': ['黑桃5', '梅花9', '方块9'], 'score': 41, 'type': '对子'}

{'name': 'p2', 'poker': ['黑桃Q', '黑桃2', '红心Q'], 'score': 50, 'type': '对子'}

{'name': 'p3', 'poker': ['红心2', '黑桃7', '红心5'], 'score': 14, 'type': '单张'}

{'name': 'p4', 'poker': ['梅花3', '方块10', '黑桃A'], 'score': 27, 'type': '单张'}

{'name': 'p5', 'poker': ['黑桃9', '黑桃J', '黑桃10'], 'score': 29970, 'type': '同花顺'}

赢家是------

p5


3、完整代码

# @Seon

# 炸金花


from random import sample

from collections import Counter



def get_pk_lst(pls, pks):  # 发牌

    result = []

    for p in pls:

        pk = sample(pks, 3)

        for _pk in pk:

            pks.remove(_pk)

        result.append({"name": p, "poker": pk})

    return result



def calculate(_score_map, pk_lst):  # 返回得分和牌型

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

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

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

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

    if not same_suit and not continuity and check == 3:

        return sum(n_lst), "单张"

    if not same_suit and check == 2:

        w = [i for i in n_lst if n_lst.count(i) == 2][0]

        single = [i for i in n_lst if i != w][0]

        return w*2*2 + single, "对子"

    if same_suit and not continuity:

        return sum(n_lst)*9, "金花"

    if continuity and not same_suit:

        return sum(n_lst)*81, "顺子"

    if check == 1:

        return sum(n_lst)*666, "豹子"

    if continuity and same_suit:

        return sum(n_lst)*999, "同花顺"



def compare(_score_map, pk_grp):  # 比大小

    for p in pk_grp:

        p["score"], p["type"] = calculate(_score_map, p["poker"])

    print("开牌结果------")

    for p in pk_grp:

        print(p)

    print("赢家是------")

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

    print(best)

    return pk_grp



def show(_score_map, _players):   # 开局

    pokers = list(_score_map.keys())

    poker_grp = get_pk_lst(_players, pokers)

    return compare(_score_map, poker_grp)



def start_game(_score_map, _players, freq=1):   # 游戏和统计

    type_lst = []

    for i in range(freq):

        grp = show(_score_map, _players)

        type_lst = type_lst + [t["type"] for t in grp]

    c = Counter(type_lst)

    print(c)

    total = sum(c.values())

    for item in c.items():

        print(f"{item[0]}频率:{item[1]/total:.2%}")



if __name__ == '__main__':

    # 准备扑克牌

    suit = ["黑桃", "红心", "方块", "梅花"]

    num = [str(i) for i in range(2, 11)] + ["J", "Q", "K", "A"]

    score_map = {}  # 单张点数映射表

    for s in suit:

        count = 2

        for n in num:

            score_map[f"{s}{n}"] = count

            count += 1

    # 5个玩家入场

    players = [f"p{i}" for i in range(1, 6)]

    # 开始游戏

    start_game(score_map, players, freq=100000)


这里是水中滑翔者、鱼类按摩师,Seon塞翁,下一篇再见!


补充

前述各牌型算分方式存在疏漏,可能会出现大牌型输给小牌型的情况,需对奖励机制进行调整。已知奖励前的原始得分如下:


最大单张得分:J(11) + K(13) + A(14) = 38

最小对子得分:2 + 2 + 3 = 7

最大对子得分:A(14) + A(14) + K(13) = 41

最小金花得分:2 + 2 + 3 = 7 (同花且含对子时按同花算,或叠加算分亦可)


为保证所有出现对子的牌组,都可以胜过纯单张的牌组,可将对子的奖励倍数设为 6 倍,以便简单地使用 sum(n_lst)*6,而不像前文所述的单独计算对子。 此时最大对子受奖励后的得分为 41 X 6 = 246,而最小金花受奖励后的得分也应高于对子,因此可设为 40 倍。后续牌型的奖励倍数以此类推进行调整。


我是小职,记得找我

✅ 解锁高薪工作

✅ 免费获取基础课程·答疑解惑·职业测评

Python开发实战-用python开发一个炸金花小游戏

本文由 @小职 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程