众所周知扑克牌可谓是居家旅行、桌面交友的必备道具,
今天我们用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
注:本文所述游戏规则与实际有所不同,主要基于对不同牌型的比较进行设计
一、游戏流程实现
1、准备扑克牌
开始游戏前,需要先生成一副满足要求的扑克牌,牌友们都知道,扑克牌有以下四种花色,每种花色有 A、2~10、J、Q、K 等 13 张牌。
suit=["黑桃","红心","方块","梅花"]
num=[str(i)foriinrange(2,11)]+["J","Q","K","A"]
为了便于后续算分,先给每一个单张
赋予相应的点数。
score_map={}#单张点数映射表
forsinsuit:
count=2
forninnum:
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}"foriinrange(1,6)]
3、发牌
将玩家和扑克牌列表作为参数,传入发牌器。发牌器在扑克牌中进行不放回抽取,为每个玩家随机抽取 3 张牌,并记下玩家名称及其对应牌组。
defget_pk_lst(pls,pks):
result=[]
forpinpls:
pk=sample(pks,3)
for_pkinpk:
pks.remove(_pk)
result.append({"name":p,"poker":pk})
returnresult
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(lambdax:score_map[x],pk_lst))#点数映射
接下来截取花色部分的文本,利用集合去重后判断是否为三张同花。
same_suit=len(set([pk[:2]forpkinpk_lst]))==1#是否同花色
再对点数部分进行排序,与依靠点数的最值生成的顺序列表进行比较,判断是否为连续的点数。要注意的是,A23 与 QKA 一样被视作顺子。
continuity=sorted(n_lst)==[iforiinrange(min(n_lst),max(n_lst)+1)]orset(n_lst)=={14,2,3}#是否连续
别忘了考虑对子和豹子的检查方式。
check=len(set(n_lst))#重复情况
那么正式开始判断牌型和算分吧!首先是单张,非同花、非顺子、三张点数不一。得分以 3 个单张点数相加。
ifnotsame_suitandnotcontinuityandcheck==3:
returnsum(n_lst),"单张"
其次是对子,非同花,有且仅有两张点数一致。得分中对于构成对子的部分给予 2 倍奖励。
ifnotsame_suitandcheck==2:
w=[iforiinn_lstifn_lst.count(i)==2][0]
single=[iforiinn_lstifi!=w][0]
returnw*2*2+single,"对子"
金花,即同花而非顺子,给予 9 倍奖励。
ifsame_suitandnotcontinuity:
returnsum(n_lst)*9,"金花"
顺子,即点数连续而非同花,给予 81 倍奖励。
ifcontinuityandnotsame_suit:
returnsum(n_lst)*81,"顺子"
豹子,即三张点数一致,这不得刷个 666 嘛。
ifcheck==1:
returnsum(n_lst)*666,"豹子"
同花顺,同花色且点数连续,绝了,赌神一个技能 999 伤害。
ifcontinuityandsame_suit:
returnsum(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=lambdax:x["score"])["name"]
赢家是—— p3
好啦,又可以开始下一场愉快的游戏了~
二、统计及源码
1、牌型统计
进行了 10 万场游戏并对各类牌型进行频率统计,可见与前述排列组合的计算所得概率基本一致。另外,搜索公众号Linux就该这样学后台回复“猴子”,获取一份惊喜礼包。
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
#炸金花
fromrandomimportsample
fromcollectionsimportCounter
defget_pk_lst(pls,pks):#发牌
result=[]
forpinpls:
pk=sample(pks,3)
for_pkinpk:
pks.remove(_pk)
result.append({"name":p,"poker":pk})
returnresult
defcalculate(_score_map,pk_lst):#返回得分和牌型
n_lst=list(map(lambdax:_score_map[x],pk_lst))#点数映射
same_suit=len(set([pk[:2]forpkinpk_lst]))==1#是否同花色
continuity=sorted(n_lst)==[iforiinrange(min(n_lst),max(n_lst)+1)]orset(n_lst)=={14,2,3}#是否连续
check=len(set(n_lst))#重复情况
ifnotsame_suitandnotcontinuityandcheck==3:
returnsum(n_lst),"单张"
ifnotsame_suitandcheck==2:
w=[iforiinn_lstifn_lst.count(i)==2][0]
single=[iforiinn_lstifi!=w][0]
returnw*2*2+single,"对子"
ifsame_suitandnotcontinuity:
returnsum(n_lst)*9,"金花"
ifcontinuityandnotsame_suit:
returnsum(n_lst)*81,"顺子"
ifcheck==1:
returnsum(n_lst)*666,"豹子"
ifcontinuityandsame_suit:
returnsum(n_lst)*999,"同花顺"
defcompare(_score_map,pk_grp):#比大小
forpinpk_grp:
p["score"],p["type"]=calculate(_score_map,p["poker"])
print("开牌结果------")
forpinpk_grp:
print(p)
print("赢家是------")
best=max(pk_grp,key=lambdax:x["score"])["name"]
print(best)
returnpk_grp
defshow(_score_map,_players):#开局
pokers=list(_score_map.keys())
poker_grp=get_pk_lst(_players,pokers)
returncompare(_score_map,poker_grp)
defstart_game(_score_map,_players,freq=1):#游戏和统计
type_lst=[]
foriinrange(freq):
grp=show(_score_map,_players)
type_lst=type_lst+[t["type"]fortingrp]
c=Counter(type_lst)
print(c)
total=sum(c.values())
foriteminc.items():
print(f"{item[0]}频率:{item[1]/total:.2%}")
if__name__=='__main__':
#准备扑克牌
suit=["黑桃","红心","方块","梅花"]
num=[str(i)fo服务器托管网riinrange(2,11)]+["J","Q","K","A"]
score_map={}#单张点数映射表
forsinsuit:
count=2
forninnum:
score_map[f"{s}{n}"]=count
count+=1
#5个玩家入场
players=[f"p{i}"foriinrange(1,6)]
#开始游戏
start_game(score_map,players,freq=100000)
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net