自己写一个程序,实现发牌、比大小判断输赢。
#### 游戏规则:
一付扑克牌,去掉大小王,每个玩家发3张牌,最后比大小,看谁赢。
有以下几种牌:
豹子:三张一样的牌,如3张6.
顺金:又称同花顺,即3张同样花色的顺子, 如红桃 5、6、7
顺子:又称拖拉机,花色不同,但是顺子,如红桃5、方片6、黑桃7,组成的顺子
对子:2张牌一样
单张:单张最大的是A
这几种牌的大小顺序为, **豹子>顺金>顺子>对子>单张**
#### 需程序实现的点:
1. 先生成一付完整的扑克牌
2. 给5个玩家随机发牌
3. 统一开牌,比大小,输出赢家是谁
目录
讲解思路:
首先,需要实现两个类:扑克牌Card和玩家Player,然后确定它们的基本属性。
# 定义一个Card类,由牌面和花色初始化
class Card:
def __init__(self, face="", color=""):
self.face = face # 牌面,字符串类型
self.color = color # 花色,字符串类型
self.name = color + face # 牌的完整名称
self.prior1 = PRIORITY[face] # 牌面对应的优先级
self.prior2 = PRIORITY[color] # 牌花色对应的优先级
对于扑克牌Card类:牌面、花色、完整名称属性应该很好理解, prior1和prior2主要是对应牌面和花色的优先级,这2个属性是方便比较牌Card对象的大小。所有优先级对应如下:
# PRIORITY数组用于比较牌面值,花色,牌型的优先级
# A>K>Q>...>2
# 黑桃>红心>方块>梅花
# 豹子>顺金>顺子>对子>单张
PRIORITY = {'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,
'梅花♣': 1, '方块♦': 2, '红心♥': 3, '黑桃♠': 4,
'单张': 1, '对子': 2, '顺子': 3, '顺金': 4, '豹子': 5
}
# 定义一个Player类,由玩家姓名初始化
class Player:
def __init__(self, name=""):
self.card = list() # 玩家拥有的牌存储在card里面,元素类型为Card
self.name = name # 玩家姓名
self.type = '' # 牌型
self.winner = False # 标志,记录该玩家是否是最后的赢家
self.pair = 0 # 记录第一张对子牌的索引,默认为0
对于玩家Player类:玩家姓名name、牌组card属性应该很好理解, 牌型type指定玩家牌属于顺子,豹子等等,winner属性记录玩家是否是赢家,pair记录对子牌的索引。
这个游戏过程我们分为三个步骤,第一步是初始化;第二步是模拟发牌;第三步是判断输赢。
步骤一:初始化牌组列表及玩家列表
此处定义多个全局变量:(牌面有13种情况,花色有4种情况,牌型有5种情况,玩家有5位)
# 分别定义全局变量:牌面值,花色,牌型,玩家姓名
FACES = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
COLORS = ['黑桃♠', '红心♥', '方块♦', '梅花♣']
TYPE = ['豹子', '顺金', '顺子', '对子', '单张']
NAMES = ['#Player1', '#Player2', '#Player3', '#Player4', '#Player5']
我们需要两个列表来存储52张牌以及5位玩家,列表元素分别是Card对象和Player对象
# 牌组列表初始化为空,玩家组列表初始化为空
CARD_GROUP = list()
PLAYERS = list()
准备工作做好了,接下来填充列表:
# 输入:无
# 输出:无
# 功能:初始化52张的扑克牌列表,即生成一副完整的牌,然后初始化玩家列表
def init():
CARD_GROUP.clear() # 清空牌组列表
for f in FACES: # 遍历牌面值
for c in COLORS: # 遍历花色
CARD_GROUP.append(Card(f, c)) # 添加扑克牌
for name in NAMES: # 初始化玩家列表
PLAYERS.append(Player(name)) # 添加玩家
步骤二:模拟发牌
直接说思路:每次从列表CARD_GROUP随机抽取一张牌发给玩家,此牌再从牌组中移除。
# 输入:无
# 输出:无
# 功能:实现发牌
def deal():
# 因为每人需要发三张牌,所以用三次循环实现
# 模拟随机发牌过程:每次随机抽取一张牌,然后从列表移除
circle = 3
for i in range(circle):
print('第{0}轮发牌:'.format((i + 1)), end='\t')
for p in PLAYERS:
temp = random.choice(CARD_GROUP) # 随机抽牌
CARD_GROUP.remove(temp) # 从牌组移除这张牌
p.card.append(temp) # 将牌加入玩家的card列表里面
time.sleep(0.7) # 体现发牌停顿感,暂停0.7s
print(temp.name, end='\t\t') # 打印牌名称
print()
print()
效果展示(运行过程中会有停顿感以模拟真实发牌):
步骤三:判断输赢
这个过程中,我们要分两个小过程,首先是判断玩家手中的牌型,把他们分类;然后比较同种牌型下的牌组大小。
1. 判断牌型,这个函数作为Player类的成员函数实现:
def judge_type(self): # 判断牌型,这个过程中,若出现对子类型的牌,则更新属性self.pair
self.card.sort(reverse=True) # 对玩家的牌按照优先级从大到小排序
cards = self.card
if cards[0].prior1 == cards[1].prior1 and cards[0].prior1 == cards[2].prior1:
self.type = '豹子'
elif cards[0].prior1-cards[1].prior1 == 1 and cards[1].prior1-cards[2].prior1 == 1:
if cards[0].prior2 == cards[1].prior2 and cards[0].prior2 == cards[2].prior2:
self.type = '顺金'
else:
self.type = '顺子'
elif cards[0].prior1 == cards[1].prior1:
self.type = '对子'
elif cards[1].prior1 == cards[2].prior1: # 排序过后,若出现对子牌,那么它们必定连在一起,所以其实索引0和2不用比较
self.type = '对子'
self.pair = 1
else:
self.type = '单张'
2. 接下来是“重头戏”:怎么比较玩家手中牌的大小呢?
首先,我们知道有个库函数sort(),用于对原列表进行排序,如果指定参数,则使用比较函数指定的比较函数。语法如下:
list.sort( key=None, reverse=False)
仔细分析炸金花游戏的比较过程:首先对于玩家比如说张三手中的牌,需要对这三张牌进行比较,这是第一个排序;然后对于同种牌型的玩家,比如两个人都是对子牌,需要对玩家进行比较,这是第二个排序。像这种对《类对象数组的排序》,有基础的童鞋应该有思路了:实现一个内置函数cmp,然后调用库函数sort就可以了。一开始我以为是重写类的__cmp__()函数,查了半天发现是重写__lt__()函数,其实就是重写<号。这个游戏需要重写两个类的__lt__函数,如下:
def __lt__(self, other): # 此处重写Card类的'<'符号,先比较牌面,再比较花色,当调用sort函数时,其会自动采用该内置函数比较
if self.prior1 == other.prior1:
return self.prior2 <= other.prior2
else:
return self.prior1 < other.prior1
def __lt__(self, other): # 此处重写Player类的'<'符号,先比较牌型,再比较每张牌,当调用sort函数时,其会自动采用该内置函数比较
n = len(self.card)
if PRIORITY[self.type] == PRIORITY[other.type]: # 若牌型相同,则比较牌大小。注意:对子牌型需要先比对,再比单。
for i in range(n):
index = (self.pair + i) % n # 0,1,2或者是1,2,0比较
if self.card[index] != other.card[index]:
return self.card[index] < other.card[index]
# 循环外这种情况是,玩家所有牌全部一样,也返回True。
# 当然,看花色的情况下,第一张牌就可以比较出玩家输赢
return True
else:
return PRIORITY[self.type] < PRIORITY[other.type]
类Player补充的函数:
info函数:用于输出玩家的姓名,牌信息,是否是赢家
def info(self):
print(self.name, end='\t') # 输出玩家姓名
for c in self.card: # 输出牌面
print(c.name, end='\t')
if not self.winner: # 若不是赢家,则只用输出牌型
print(self.type)
else: # 若是赢家,则还要加上庆祝语
print(self.type, '\tWinner! 🎉🎉🎉')
完整代码:
当然具体流程的主函数此处没有展示,完整代码文件可见我的Github:GitHub - yhmain/优快云
或者在优快云上面下载:Python3-card.py
(大家觉得可以的话,可以给个👍嘛~~~)