闲来无事,写一款扎金花小游戏,先练练手,打的时候好赢钱
大年三十我坐庄,大年初一就背井离乡
众所 周知扑克牌可谓是居家旅行、桌面交友的必备道具,今天我们用 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 张牌。
suit = ["黑桃", "红心", "方块", "梅花"] num = [str(i) for i in range(2, 11)] + ["J", "Q", "K", "A"]
为了便于后续算分,先给每一个 单张
赋予相应的点数。
score_map = {} # 单张点数映射表 for s in suit:count = 2for n in num:score_map[f"{s}{n}"] = countcount += 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 resultpokers = 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), "单张"
其次是 对子 ,非同花,有且仅有两张点数一致。得分中对于构成对子的部分给予 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, "金花"
顺子,即点数连续而非同花,给予 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 Counterdef 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 resultdef 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_grpdef 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 = 2for n in num:score_map[f"{s}{n}"] = countcount += 1# 5个玩家入场players = [f"p{i}" for i in range(1, 6)]# 开始游戏start_game(score_map, players, freq=100000)
到这里你学会了吗?学会了给我点个赞哈,完整项目源代码点这里获取
闲来无事,写一款扎金花小游戏,先练练手,打的时候好赢钱相关推荐
- 用JAVA写一款自己的小游戏
用JAVA写一款自己的小游戏 我目前也处于一个学习的阶段,所以代码会有很多的错误,但是经过我几天的瞎调试目前还是可以玩的,但是可玩性还是很差 说一说怎么玩这个游戏把 设计初衷:KoalaChess,顾 ...
- 写一个《扎金花》程序自己玩。
写一个<扎金花>程序自己玩.hongjin2用一年半的时间基本自学完了程序设计专业的主干课程(除了<扎金花>,hongjin2实在看不懂),1995年hongjin2参加了初级 ...
- 【C语言】大一小白的一个C语言扎金花小程序
算是一个课程作业吧,找个地方存一下.小生学疏才浅,若程序中有不当的地方或是可以改进的地方,也欢迎各位大神指点指点,要是有后辈看了觉得有帮助更是小生的荣幸. 扎金花这个游戏就不在此多作介绍了,那么下面就 ...
- Python实现炸金花小游戏
炸金花小游戏 规则: 一付扑克牌,去掉大小王,每个玩家发3张牌,最后比大小,看谁赢. 有以下几种牌: 豹子:三张一样的牌,如3张6. 顺金:又称同花顺,即3张同样花色的顺子, 如红桃 5.6.7 顺子 ...
- 利用random 的randint 方法写一个猜数字的小游戏
学习python第二天,编写的一款数字小游戏 昨天学习完条件语句和while循环,老师留下的作业:利用random 的randint 方法写一个猜数字的小游戏. 第一次在CSDN上记录,小白一枚.希望 ...
- 20款Adobe AIR小游戏
20款Adobe AIR小游戏 什么时候有空去包装一下,跑在PlayBook上,我好成天天玩,嘿嘿 http://paranimage.com/20-adobe-air-mini-games/
- 勇闯掘金小游戏为一款多个小游戏的合集游戏,有五个关卡:找掘金、石头剪刀布、寻找藏宝图、打地鼠、抽奖。基于Vue
游戏简介 勇闯掘金小游戏为一款多个小游戏的合集游戏,共有五个关卡,分别为:找掘金.石头剪刀布.寻找藏宝图.打地鼠.抽奖.每个环节20分,满分100分. 完整代码下载地址:勇闯掘金小游戏 快速体验 ht ...
- android 免费游戏推荐,10款免费Android小游戏推荐
如果你是Android手机用户,而且正在寻找一些小游戏来消磨时间,下面这10款免费小游戏将大有帮助.无论你喜欢哪种游戏,下面至少有一两款会满足你的要求. 1. 愤怒的小鸟 自从数月前登录iPhone/ ...
- 利用Scanner和Random类写的java猜字小游戏
利用Scanner和Random类写的java猜字小游戏 题目:随机生成一个数,然后键盘输入猜的数字,验证输入数和这个随机生成的数是否相等,一共有五次猜测机会,自动提示输入的数大了还是小了,如果五次都 ...
最新文章
- C++中#include<fstream>头文件
- Angular中使用双向数据绑定操作表单的input、checkboc、radio、select、textarea实现简单的人员登记系统实例
- Spring Boot的学习之路(03):基础环境搭建,做好学习前的准备工作
- 成吉思汗:“世界之鞭”还是“人类之王”?
- 张文宏:知道很多网友批评我,但粥还是不能喝
- 我就想加个索引,怎么就这么难?
- Nginx源码分析 - Event事件篇 - Epoll事件模块(19)
- DataGame : AIIA 2019
- JBoss主要版本下载链接一览
- DotNetBar 14.1.X 安装+源码=不错选择
- Layout state should be one of 100 but it is 10起因和解决
- CSR867x — 蓝牙音频发射器方案(支持USB、模拟和SPDIF)
- 8小时学会div+css 视频教程
- Action层, Service层 ,modle层 和 Dao层详解
- Unity 钓鱼玩法的初步实现
- 游戏项目框架(属性名+方法名)
- PM,RD,FE,UE,UI,QA,OP,DBA,BRD,MRD, PRD,FSD等缩写的全称解析
- 关于MBR和GUID分区的问题
- 罗马神话缪斯_缪斯与边缘– Adob​​e Web Designer的工具
- Transformers for 1D signals in Parkinson’s disease detection from gait