python实现扑克游戏 - 抽鬼牌 和 21点

poker_games

python实现扑克游戏 : 抽鬼牌 和 21点 - Python Implementation of Poker Games : Drawing Ghost Cards and Blackjack

poker模块

首先,定义一个扑克模块,后面的包括以后的扑克牌游戏,都可以调用这个模块
这个模块可以实现:

  • 卡牌、扑克牌组
  • 发牌、洗牌
  • 玩家摸牌、出牌
    等一些扑克游戏共性的类和方法

定义卡牌的类

属性

  • 花色 - 黑桃、红心、梅花、方块 外加两个特殊的鬼牌
    • 使用Enum将 花色 定义为枚举类型 6种花色与0到5 一一对应
    • 在牌类Card中将所有花色定义成字符串 '♠♥♣♦🤡😈'
  • 面值 - ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'joker', 'joker']

牌类Card 通过__repr__方法使打印返回的是花色值索引的字符 和 面值 索引的字符 如 '♠3'、'🤡joker'

import random
from enum import Enum
# 定义花色枚举
class Suite(Enum):
    """花色(枚举)"""
    # 定义四种花色,分别是黑桃、红心、梅花、方块,外加两种鬼牌
    SPADE, HEART, CLUB, DIAMOND, JOKER, mJOKER = range(6)
# 定义牌类
class Card:
    """牌"""
    def __init__(self, suite, face):
        # 初始化牌的花色和点数
        self.suite = suite
        self.face = face
    def __repr__(self):
        # 定义一个字符串表示牌的花色和点数
        suites = '♠♥♣♦🤡😈'  # 花色字符串,分别对应黑桃、红心、梅花、方块、大鬼、小鬼
        faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'joker', 'joker']  # 点数字符串
        # 返回牌的花色和点数,格式为“花色+点数”
        return f'{suites[self.suite.value]}{faces[self.face]}'

定义扑克的类

属性

  • 扑克牌组 - 可定义鬼牌数量、花色种类,面值范围,牌组数量

方法

  • 洗牌 - 随机打乱牌组
  • 发牌 - 返回牌组索引的一张牌
# 定义扑克类
class Poker:
    """扑克"""
    def __init__(self, joker = 0, card_num = (4,1,14), poker_num = 1):
        self.joker = joker    # 鬼牌数量 0,1,2
        self.card_num = card_num  # (花色数量, 面值开始, 面值结束, 牌组数量)
        self.poker_num = poker_num # 牌组数量
        # 初始化一副扑克牌,默认四种花色和13种点数(从A到K)
        self.cards = [
            Card(suite, face)
            for suite in [i for i in Suite][:self.card_num[0]]  # 遍历花色枚举
            for face in range(self.card_num[1], self.card_num[2])  # 遍历点数,从1(A)到13(K)
        ]
        # 在扑克牌组中加入鬼牌,默认是0
        for i in range(self.joker):
            self.cards.append(
                Card([i for i in Suite][4+i], 14+i)
            )
        # 牌组数量,默认1组牌
        self.cards = self.cards * poker_num

    def shuffle(self):
        """洗牌"""
        # 洗牌前,将current重置为0,表示还没有发过牌
        self.current = 0
        # 使用random模块的shuffle函数打乱扑克牌顺序
        random.shuffle(self.cards)
    def deal(self):
        """发牌"""
        # 发出当前索引的牌,并更新索引
        card = self.cards[self.current]
        self.current += 1
        return card
    @property
    def has_next(self):
        """还有没有牌可以发"""
        # 判断是否还有未发的牌
        return self.current < len(self.cards)

定义玩家类

属性

  • 名字 - 用名字属性来区分玩家
  • 手牌 - 一个列表,存储玩家手牌

方法

  • 摸牌 - 先通过牌组类的发牌方法获取一张牌,再将这这牌加入玩家手牌
  • 洗牌 - 随机打乱手牌
  • 出牌 - 先判断这张牌是否在手牌中,在就移除,不在则不做任何操作

如还需计算点数等方法,可以在具体的游戏程序中继承玩家类,再写计算点数的方法

# 定义玩家类
class Player:
    """玩家"""
    def __init__(self, name):
        # 初始化玩家的名字和手牌列表
        self.name = name
        self.cards = []
    def get_one(self, card, is_hidden = False):
        """摸牌"""
        # 玩家摸到一张牌,将其加入手牌列表
        self.cards.append(card)
        # is_hidden为True,则表示该牌为暗牌
        if is_hidden:
            print(f'{self.name}:获得一张牌')
        else:
            print(f'{self.name}:获得一张{card}')
    def show_cards(self, is_hidden = False):
        # 显示玩家当前的手牌
        # is_hidden为True,则表示该牌为暗牌
        if is_hidden:
            print(f'{self.name}现在的手牌是: '+' ▮ '*len(self.cards))
        else:
            print(f'{self.name}现在的手牌是: {self.cards}')
    def shuffle(self):
        """洗牌"""
        # 使用random模块的shuffle函数打乱扑克牌顺序
        random.shuffle(self.cards)
        print(f'{self.name}洗了自己的手牌')
    def deal(self, card):
        """出牌"""
        if card in self.cards:
            self.cards.remove(card)
            print(f'{self.name}失去了一张牌: {card}')

可以打印检查一下,poker模块是否可以正常工作

if __name__ == '__main__':
    # 第一个参数 鬼牌数量
    # 第二个参数 - 元组 (花色数量, 面值开始, 面值结束, 牌组数量)
    # 如 两种花色 A-K的,外加一个鬼牌的牌组
    p = Poker(1, (2,1,14), 1)
    print(p.cards)

运行结果如下:

抽鬼牌

玩法

  • 先根据玩家数量调整扑克牌组数量 - 要确保所有玩家的手牌加起来除了鬼牌都是成对出现 (如 3个人 可以两种花色 面值1-10 外加一张鬼牌,共21张,每人7张牌)
  • 开局 - 每个玩家分到一份牌
  • 玩家A回合
    • 从玩家B手牌中抽取一张牌,放到自己的手牌中
    • 玩家A可选择自己手牌中两个面值相同的牌打出,出牌次数无限制,也可选择跳过出牌
  • 玩家B回合
    • 从玩家C手牌中抽取一张牌,放到自己的手牌中
    • 玩家B可选择自己手牌中两个面值相同的牌打出,出牌次数无限制,也可选择跳过出牌
  • 玩家C回合
    • 从玩家A手牌中抽取一张牌,放到自己的手牌中
    • 玩家C可选择自己手牌中两个面值相同的牌打出,出牌次数无限制,也可选择跳过出牌
  • 游戏结束条件
    • 当某位玩家出牌后,剩余手牌为空,则该玩家获胜,游戏结束
    • 当某位玩家出牌后,仅剩余一张鬼牌,则该玩家失败,游戏结束

若poker模块中的玩家类无法满足需求,可定义子类继承原玩家类,抽鬼牌需要重写原玩家类的摸牌方法,摸到鬼牌必须显示

import poker
import random

# 定义玩家类
class Player(poker.Player):
    def get_one(self, card, is_hidden = False):
        """摸牌"""
        # 玩家摸到一张牌,将其加入手牌列表
        self.cards.append(card)
        if is_hidden:
            if card.face != 14:
                print(f'{self.name}:获得一张牌')
            else:
                print(f'{self.name}:获得一张{card}')
        else:
            print(f'{self.name}:获得一张{card}')

初始化

先根据玩家数量调整扑克牌组数量 - 要确保所有玩家的手牌加起来除了鬼牌都是成对出现 (如 3个人 可以两种花色 面值1-10 外加一张鬼牌,共21张,每人7张牌)

# 初始化
# 创建一个扑克对象
poker = poker.Poker(1, (2, 1, 11), 1)
# 对扑克进行洗牌
poker.shuffle()
# 创建一个名为“我”的玩家对象
gamer = Player('我')
# 创建一个名为“庄家”的玩家对象
master = Player('庄家')
pc2 = Player('PC')
print(poker.cards)

# 初始发牌
init_card = 7
for i in range(init_card):
    card1 = poker.deal()
    gamer.get_one(card1)
    card2 = poker.deal()
    master.get_one(card2, True)
    card3 = poker.deal()
    pc2.get_one(card3, True)
gamer.show_cards()
master.shuffle()
master.show_cards(True)
pc2.shuffle()
pc2.show_cards(True)
print(' 抽鬼牌 - 游戏开始 '.center(40,'='))

定义抽牌与出牌阶段的规则

# 玩家回合
def get_deal(master, gamer, ):
    print(f' {gamer.name}回合开始 '.center(40,'='))
    gamer.show_cards()
    master.show_cards(True)
    while True:
        num = input(f'请抽{master.name}的牌,输入第几张牌:')
        if num.isdigit() and int(num) -1 in range(len(master.cards)):
            break
        print('输入格式错误!!!')
    num = int(num)
    card = master.cards[num - 1]
    master.deal(card)
    gamer.get_one(card)
    # 丢牌
    while True:
        gamer.show_cards()
        try:
            n, m = input('请丢牌,输入第几张和第几张(如1 2)\n输入 0 0 则跳过出牌:').split()
        except:
            print('输入格式错误!!!')
            continue
        if n.isdigit() and m.isdigit():
            n, m = map(int, (n, m))
            if n != m:
                card1 = gamer.cards[n - 1]
                card2 = gamer.cards[m - 1]
                if card1.face == card2.face:
                    gamer.deal(card1)
                    gamer.deal(card2)
                    if len(gamer.cards) == 0:
                        print('you win')
                        return True, gamer, True
                    if len(gamer.cards) == 1 and gamer.cards[0].face == 14:
                        print('you lost')
                        return True, gamer, False
            elif n == 0 and m == 0:
                print(f'{gamer.name}跳过出牌')
                break
            elif n == m:
                print('无法出同一张牌')
        else:
            print(f'{gamer.name}跳过出牌')
            break
    print(f' {gamer.name}回合结束 '.center(40,'='))
    return False, 0

def deal_PC(pc2, master, ):
    # 庄家回合
    print(f' {master.name}回合开始 '.center(40,'='))
    pc2.show_cards()
    num = random.randint(1, len(pc2.cards))
    card = pc2.cards[num - 1]
    pc2.deal(card)
    master.get_one(card, True)
    pc2.show_cards()
    # if len(master.cards) == 0:
    #     print('winer')
    #     return True, master, True
    # if len(master.cards) == 1 and master.cards[0].face == 14:
    #     print('lost')
    #     return True, master, False
    # 丢牌
    while True:
        master.show_cards(True)
        counts = {}
        for x in [c.face for c in master.cards]:
            counts[x] = counts.get(x, 0) + 1
        key1 = ''
        for i in counts.items():
            if i[1] > 1:
                key1 = i[0]
                break
        if key1:
            n = [c.face for c in master.cards].index(key1)
            card1 = master.cards[n]
            master.deal(card1)
            n = [c.face for c in master.cards].index(key1)
            card1 = master.cards[n]
            master.deal(card1)
            if len(master.cards) == 0:
                print('winer')
                return True, master, True
            if len(master.cards) == 1 and master.cards[0].face == 14:
                print('lost')
                return True, master, False
        else:
            print(f'{master.name}回合结束')
            break
    pc2.show_cards(True)
    master.shuffle()
    master.show_cards(True)
    print(f' {master.name}回合结束 '.center(40,'='))
    return False, 0

游戏进行

通过while True使游戏不断进行
轮流进行不同玩家的抽牌与出牌,直到某玩家出牌后触发结束条件

while True:
    result = get_deal(master,gamer)
    if result[0]:
        print(result[1].name, ' win! ' if result[2] else 'lost')
        break
    result = deal_PC(pc2,master)
    if result[0]:
        print(result[1].name, ' win! ' if result[2] else 'lost')
        break
    result = deal_PC(gamer,pc2)
    if result[0]:
        print(result[1].name, ' win! ' if result[2] else 'lost')
        break
gamer.show_cards()
master.show_cards()
pc2.show_cards()

运行结果如下:

BlackJack - 21点游戏

玩法

  • 定义 一副 4花色 A-K 无鬼牌 的扑克
  • 开局给玩家和庄家各分2张牌,一张明牌,一张暗牌
  • 点数说明:
    • 2 - 10 点数就是它们的面值
    • J Q K 点数都是10
    • A默认面值为11,若手牌有A 且 总点数大于等于21,则A面值变为1
  • 玩家回合:
    • 玩家选择摸牌或停牌
    • 若选li择摸牌,则给玩家发一张牌
      • 若此时玩家总点数大于21,则玩家输,
      • 若不大于21,则可继续选择摸牌或停牌
    • 若选择停牌,则玩家回合结束,进入庄家回合
  • 庄家回合:
    • 庄家选择摸牌或停牌
    • 注:若庄家手牌总点数小于17,则庄家必须摸牌,直到大于等于17
      • 若此时庄家总点数大于21,则庄家输,
      • 若不大于21,则可继续选择摸牌或停牌
    • 若选择停牌,则庄家回合结束,进入结算
  • 结算
    • 计算玩家与庄家手牌总点数
    • 总点数离21越近,分数越高
    • 分数一样,平局,否则分数高者赢

若poker模块中的玩家类无法满足需求,可定义子类继承原玩家类,21点需要再写一个计算点数的方法

import poker
# 定义玩家类
class Player(poker.Player):
    """玩家"""
    def what_face(self):
        # 计算玩家手牌的总点数(考虑A可能作为1或者11)
        li = []
        for i in self.cards:
            if i.face > 10:
                # J, Q, K 都当作10点
                li.append(10)
            elif i.face == 1:
                # A 暂时当作11点
                li.append(11)
            else:
                # 2-10按照实际点数计算
                li.append(i.face)

        result = sum(li)

        # 如果总点数大于等于21,并且包含A(当作11点),尝试将A改为1点,并重新计算
        while result >= 21 and (11 in li):
            li.remove(11)
            li.append(1)
            result = sum(li)

            # 返回玩家手牌的总点数
        return result

初始化

  • 定义 一副 4花色 A-K 无鬼牌 的扑克
  • 开局给玩家和庄家各分2张牌,一张明牌,一张暗牌
# 初始化
# 创建一个扑克对象
poker = poker.Poker()
# 对扑克进行洗牌
poker.shuffle()
# 创建一个名为“我”的玩家对象
gamer = Player('我')
# 创建一个名为“庄家”的玩家对象
master = Player('庄家')

# 开局
def game_start():
    # 打印游戏开始的标题
    print(' BlackJack - 21点游戏 '.center(40,'='))

# 为玩家发牌
def deal_start(player, is_hidden=False):
    """为玩家发牌,并打印发牌信息"""
    # 摸一张明牌
    card1 = poker.deal()
    player.get_one(card1,is_hidden)
    # 摸一张牌,根据is_hidden参数决定是否为暗牌
    card2 = poker.deal()
    player.get_one(card2,is_hidden)

# 开局发牌
game_start()
deal_start(master, is_hidden=True)  # 为庄家发牌,庄家第一张牌为暗牌
deal_start(gamer, is_hidden=False)  # 为玩家发牌,玩家两张牌都为明牌
print()

游戏进行

  • 玩家回合
  • 庄家回合
  • 结算
# 计算并打印玩家和庄家的得分
def score_data(gamer: Player, master: Player, is_over: bool):
    # 计算玩家和庄家的得分(这里只是一个简单的示例,实际规则可能更复杂)
    gamer_score = (21 - abs(gamer.cards_face - 21)) * 100 // 21
    master_score = (21 - abs(master.cards_face - 21)) * 100 // 21
    if not is_over:
        # 判断谁赢了这一局
        if gamer_score > master_score:
            print('玩家赢!')
        elif gamer_score == master_score:
            print('平局!')
        else:
            print('庄家赢!')
    print('\n游戏数据:')
    print(f'{f"玩家 {gamer.cards}":<21} 点数:{gamer.cards_face} 得分:{gamer_score}')
    print(f'{f"庄家 {master.cards}":<21} 点数:{master.cards_face} 得分:{master_score}')

# 玩家回合
print(' 玩家回合 '.center(40,'='))
gamer.show_cards()  # 显示玩家手牌
# 玩家开始摸牌或停牌
while True:
    gamer.cards_face = gamer.what_face()  # 计算玩家手牌的总点数
    master.cards_face = master.what_face()  # 计算庄家手牌的总点数(此处假设庄家的牌也可见,但通常不是)

    if gamer.cards_face > 21:
        # 如果玩家点数超过21,则玩家输
        print('\n玩家点数超过21,玩家输!', gamer.cards, '点数:', gamer.cards_face, )
        score_data(gamer, master, True)  # 传入is_over=True表示游戏结束
        break

        # 询问玩家是否继续摸牌
    is_ok = input('输入1继续 摸牌 ,输入2或其他 停牌 : ')
    if is_ok == '1':
        gamer_card = poker.deal()
        gamer.get_one(gamer_card)
        print(f'{gamer.name}获得一张{gamer_card}')
        gamer.show_cards()  # 更新后显示玩家手牌
    else:
        # 玩家选择停牌,进入庄家回合
        # 庄家回合
        print()
        print(' 庄家回合 '.center(40,'='))

        # 庄家回合的循环,根据庄家的点数来决定是否继续摸牌
        while True:
            # 计算庄家的手牌总点数
            master.cards_face = master.what_face()

            # 如果庄家的点数小于17,则必须继续摸牌
            if master.cards_face < 17:
                # 摸一张牌
                master_card = poker.deal()
                master.get_one(master_card)
                print(f'{master.name}获得一张{master_card}')
                # 如果庄家的点数大于或等于17,则停止摸牌
            else:
                break

                # 结算
        print()

        # 检查庄家的点数是否超过21
        if master.cards_face > 21:
            # 如果庄家点数超过21,则玩家赢
            print('\n庄家点数超过21,玩家赢!', master.cards, '庄家点数:', master.cards_face)
            score_data(gamer, master, True)  # 游戏结束,传入is_over=True
            break

            # 如果庄家点数没有超过21,则进行正常的得分计算
        score_data(gamer, master, False)  # 游戏未结束,传入is_over=False

        # 因为庄家回合结束后,整个游戏流程也结束了,所以这里再次加入break来确保跳出循环
        break

运行结果如下:


热门相关:文娱缔造者   视死如归魏君子   遥望行止   田园晚色:肥妇三嫁良夫   美女总裁之贴身高手