眾所周知?撲克牌可謂是居家旅行、桌面交友的必備道具,
今天我們用?Python
?來實現(xiàn)一個類似炸金花的撲克牌小游戲,先來看一下基本的游戲規(guī)則。
炸(詐)金花又叫三張牌,是在全國廣泛流傳的一種民間多人紙牌游戲。游戲使用一副除去大小王的撲克牌,共 4 個花色 52 張牌,各個玩家從中抽取 3 張牌,比較大小。各種牌型的大小順序如下(按照全排列組合中出現(xiàn)的概率越小,牌型分數(shù)獎勵越大):1、同花順:三張同樣花色且點數(shù)連續(xù)的牌,如紅心2、紅心3、紅心4;2、豹子:三張點數(shù)一樣的牌,如 AAA、222;3、順子:三張點數(shù)連續(xù)的牌,如紅心2、黑桃3、方塊4;4、金花:三張同樣花色的牌,如紅心2、紅心5、紅心8;5、對子:兩張點數(shù)一樣的牌,如紅心2、黑桃2;6、單張:2~10 < J < Q < K < A。以下概率截自百度百科:
注:本文所述游戲規(guī)則與實際有所不同,主要基于對不同牌型的比較進行設計
一、游戲流程實現(xiàn)
1、準備撲克牌
開始游戲前,需要先生成一副滿足要求的撲克牌,牌友們都知道,撲克牌有以下四種花色,每種花色有 A、2~10、J、Q、K 等 13 張牌。
suit?=?["黑桃",?"紅心",?"方塊",?"梅花"]
num?=?[str(i)?for?i?in?range(2,?11)]?+?["J",?"Q",?"K",?"A"]
為了便于后續(xù)算分,先給每一個單張
賦予相應的點數(shù)。
score_map?=?{}??#?單張點數(shù)映射表
for?s?in?suit:
????count?=?2
????for?n?in?num:
????????score_map[f"{s}{n}"]?=?count
????????count?+=?1
撲克牌點數(shù)預覽如下:
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 等名稱對玩家進行區(qū)分,我們先邀請 5 個玩家入場。
players?=?[f"p{i}"?for?i?in?range(1,?6)]
3、發(fā)牌
將玩家和撲克牌列表作為參數(shù),傳入發(fā)牌器。發(fā)牌器在撲克牌中進行不放回抽取,為每個玩家隨機抽取 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)??#?發(fā)牌
發(fā)牌預覽如下:
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 張撲克牌轉(zhuǎn)換成對應的點數(shù)。
n_lst?=?list(map(lambda?x:?score_map[x],?pk_lst))??#?點數(shù)映射
接下來截取花色部分的文本,利用集合去重后判斷是否為三張同花。
same_suit?=?len(set([pk[:2]?for?pk?in?pk_lst]))?==?1??#?是否同花色
再對點數(shù)部分進行排序,與依靠點數(shù)的最值生成的順序列表進行比較,判斷是否為連續(xù)的點數(shù)。要注意的是,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}??#?是否連續(xù)
別忘了考慮對子和豹子的檢查方式。
check?=?len(set(n_lst))??#?重復情況
那么正式開始判斷牌型和算分吧!首先是單張,非同花、非順子、三張點數(shù)不一。得分以 3 個單張點數(shù)相加。
if?not?same_suit?and?not?continuity?and?check?==?3:
????return?sum(n_lst),?"單張"
其次是對子,非同花,有且僅有兩張點數(shù)一致。得分中對于構(gòu)成對子的部分給予 2 倍獎勵。
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,?"金花"
順子,即點數(shù)連續(xù)而非同花,給予 81 倍獎勵。
if?continuity?and?not?same_suit:
????return?sum(n_lst)*81,?"順子"
豹子,即三張點數(shù)一致,這不得刷個 666 嘛。
if?check?==?1:
????return?sum(n_lst)*666,?"豹子"
同花順,同花色且點數(shù)連續(xù),絕了,賭神一個技能 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 函數(shù)找出來誰是最棒的,公布名字!
best?=?max(pk_grp,?key=lambda?x:?x["score"])["name"]
贏家是------ p3
好啦,又可以開始下一場愉快的游戲了~
二、統(tǒng)計及源碼
1、牌型統(tǒng)計
進行了 10 萬場游戲并對各類牌型進行頻率統(tǒng)計,可見與前述排列組合的計算所得概率基本一致。另外,搜索公眾號Linux就該這樣學后臺回復“猴子”,獲取一份驚喜禮包。文章來源:http://www.zghlxwxcb.cn/news/detail-696734.html
Counter({'單張':?371856,?'對子':?84773,?'金花':?24833,?'順子':?16239,?'豹子':?1179,?'同花順':?1120})
單張頻率:74.37%
對子頻率:16.95%
金花頻率:4.97%
順子頻率:3.25%
豹子頻率:0.24%
同花順頻率:0.22%
2、牌局案例
各類牌型的局面和結(jié)果如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-696734.html
開牌結(jié)果------
{'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
開牌結(jié)果------
{'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
開牌結(jié)果------
{'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
開牌結(jié)果------
{'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
開牌結(jié)果------
{'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
開牌結(jié)果------
{'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):??#?發(fā)牌
????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))??#?點數(shù)映射
????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}??#?是否連續(xù)
????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("開牌結(jié)果------")
????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):???#?游戲和統(tǒng)計
????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?=?{}??#?單張點數(shù)映射表
????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)
到了這里,關(guān)于用python開發(fā)一個炸金花小游戲的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!