21点游戏python代码简单实现(含流程图和功能设计)

21点游戏python简单实现

文章目录

  • 21点游戏python简单实现
    • 规则简述
    • 游戏流程图设计
    • 功能设计
    • 代码实现
    • 总结
      • OOA(分析)和OOD(设计)
      • 代码部分
      • 可改进部分

规则简述

  1. 玩家共两个角色:电脑和人类,电脑是庄家

  2. 游戏开始时,先给人类和电脑每个玩家分别发两张牌作为底牌,庄家底牌只漏一张

  3. 判断双方底牌是否直接为21点,如果其中一方为21点则直接判胜利,并在总分上加一分。如果双方都是21点,那就是平局,不得分。

  4. 当初始牌面上,没有直接出现21点,人类玩家根据自己的牌面大小决定是否继续要牌。如果要牌,那就在牌堆中抽一张,然后再次判断胜负。如果人类牌面的总点数超过了21点,那就直接判输

  5. 如果人类玩家停止要牌了,并且没有因为超过21点而被判输的情况下,则电脑要牌。电脑要牌这里,可以自己设计一个规则:
    5.1 比如电脑一直要牌,直到比人类玩家大才停止要牌。
    5.2 根据牌堆中剩余牌的数量,计算赢的概率,然后设置一个阈值,超过阈值就要,低于就不要。
    5.3 …

  6. 循环4和5的步骤

  7. 完成一轮游戏的时候,可由人类玩家决定,是否继续玩下一轮

  8. 牌堆中剩余的牌数不够玩一轮游戏的时候,游戏自动结束。

  9. 计算规则: 2. 3. 4. 5. 6. 7. 8. 9. 10分别是正常的点数,J. Q. K都是10点

  10. A比较特殊,首先把A当做1来计算,牌面总分数如果小于21,那么再把A当做11再计算一次,如果这个时候仍然小于21,那么A就当11算,如果这个时候牌面总分数大于了21,那么A就当1算。

游戏流程图设计

21点游戏python代码简单实现(含流程图和功能设计)_第1张图片

功能设计

class poke

class dealer

  • field:
    • cards: 每一个荷官对应多副牌
  • function:
    • give_one_card(): 发牌

class Player

  • field:
    • name: 昵称
    • score: 总比分
    • points:计数器
    • cards_in_hand->list: 手牌
  • function:
    • who_win(player): 判断未提前结束下与其他玩家的胜负
    • now_count(): 计算当前牌数字
    • get(*cards): 将荷官发的牌加入手牌

代码实现

# usr/bin/env python3;
#-*-coding: utf-8-*
import random
from sys import exit

class Poke:
    '''
    Poke类用来初始化一个牌堆
    '''
    def __init__(self):
        self.cards = [[face, suite] for face in "♠♥♦♣" for suite in [1,2,3,4,5,6,7,8,9,10,'J','Q','K']]
        random.shuffle(self.cards)


class Dealer:
    '''
    Dealer类初始化一个荷官
    主要用来实现取牌和发牌的作用
    '''
    def __init__(self):
        self.cards = Poke().cards

    def give_one_card(self):
        '''
        给玩家发牌
        return: list
        '''
        if not self.cards:
            # 重新取一副牌并洗牌
            self.cards.extend(Poke().cards)
        return self.cards.pop()


class Player:
    def __init__(self, name):
        '''
        初始化实例属性
        '''
        self.name = name
        self.score = 0
        self.points = 0
        self.cards_in_hand = []

    def init(self):
        '''
        重置计数器和牌列表
        '''
        self.cards_in_hand = []
        self.points = 0

    def now_count(self):
        '''
        更新计数器
        '''
        point = 0 
        for face, suite in self.cards_in_hand:
            if suite in ['J', 'Q', 'K']:
                suite = 10
            point += suite
        # 判断是否有A,如果有A再判断是否大于11,如果是的话A当做1,否的话当做11
        for card in self.cards_in_hand:
            if card[1] == 1 and point + 10 < 21:
                self.points = point + 10
            else:
                self.points = point

    def is_win(self, player):
        '''
        未提前结束回合时,判断玩家输赢
        param: player 进行比较的玩家
        '''
        s1 = self.points
        s2 = player.points
        if s1 > s2:
            print(f"玩家{self.name}点数为{s1}, 电脑{player.name}点数为{s2}, 玩家{self.name}赢了!")
            self.score += 1
        elif s1 == s2:
            print(f"玩家{self.name}点数为{s1}, 电脑{player.name}点数为{s2}, 平局!")
        else:
            print(f"玩家{self.name}点数为{s1}, 电脑{player.name}点数为{s2}, 电脑{player.name}赢了!")
            player.score += 1            

    def get(self, *cards):
        '''
        玩家取荷官发的牌,并更新计数器
        param: *cards 一个或多个list类型表示的牌
        '''
        for card in cards:
            self.cards_in_hand.append(card)
        self.now_count() # 重置分数


def main(dealer: Dealer, computer: Player, human: Player):
    '''
    游戏控制主函数
    '''
    # 回合数
    count = 0
    try:
        while True:
            count += 1
            print(f"第{count}轮比赛开始:")
            # 设置提前结束标志
            flag = False
            # 新回合初始化计数器和牌
            human.init()
            computer.init()
            # 准备发牌,两张牌给human, 两张牌给电脑{computer.name}
            human.get(dealer.give_one_card(), dealer.give_one_card())
            computer.get(dealer.give_one_card(), dealer.give_one_card())
            print(f"玩家{human.name}手中的牌是{human.cards_in_hand[-2]}, {human.cards_in_hand[-1]}")
            print(f"电脑{computer.name}手中的牌是{computer.cards_in_hand[-2]}, ?")
            # 判断是否21点
            if human.points == 21 == computer.points:
                print("玩家{human.name}和电脑{computer.name}都为21点,平局!")
            elif human.points == 21:
                print("玩家{human.name}的点数为21点,恭喜玩家{human.name}赢了!")
                human.score += 1
            else:
                # 玩家要牌
                while True:
                    if_next_card = input("是否继续要牌:(Y/N)")
                    if if_next_card in ['N', 'n']:
                       break                   
                    elif if_next_card in ['Y', 'y']:
                        human.get(dealer.give_one_card())
                        print(f"玩家{human.name}得到一张{human.cards_in_hand[-1]}, 玩家{human.name}手中的牌是{human.cards_in_hand}")
                        # 判断玩家是否超过21点,如果是提前结束标志设置为True
                        if human.points > 21:
                            print(f"玩家{human.name}的点数{human.points}超过了21点,玩家{human.name}输了!")
                            computer.score += 1
                            flag = True
                            break
                # 电脑要牌
                if not flag:
                    # 电脑要牌逻辑,只要小于玩家分数就要牌
                    while computer.points < human.points:
                        computer.get(dealer.give_one_card())
                        print(f"电脑{computer.name}得到一张{computer.cards_in_hand[-1]}, 电脑{computer.name}手中的牌是{computer.cards_in_hand}")
                    # 先判断电脑是否大于21点,如果大于21点提前结束
                    if computer.points > 21:
                        print(f"电脑{computer.name}的点数为{computer.points}超过21点,恭喜玩家{human.name}赢了!")
                        human.score += 1
                    else:
                        # 没有提前结束也就是都小于21点的情况下,判断大小输赢
                        human.is_win(computer)
            print("-" * 30)
            # 是否进行下一局
            if_play_again = input("是否进行下一局:(Y/N)")
            # 如果继续,先打印总比分,再重新开始
            if if_play_again in ['Y', 'y']:
                print(f"玩家{human.name},电脑{computer.name}总比分为{human.score}:{computer.score}")
            # 如果停止,打印总比分并判处胜者后退出
            elif if_play_again in ['N', 'n']:
                print(f"玩家{human.name},电脑{computer.name}总比分为{human.score}:{computer.score}")
                if human.score > computer.score:
                    print(f"{human.name}胜出!")
                elif human.score < computer.score:
                    print(f"{computer.name}胜出!")
                else:
                    print("战况激烈,你们打平了!")
                print("游戏结束")
                exit(0)
            else:
                print("输入有误,请重新输入:")
    except Exception:
        print("有bug,游戏结束!")

if __name__ == '__main__':
    computer = Player('Robot')
    human = Player('Lihaoer')
    dealer = Dealer()
    main(dealer, computer, human)

总结

OOA(分析)和OOD(设计)

  1. 制作流程图时,主流程尽量保持在一个方向上,每遇到选择分支,就在垂直方向上继续分析,尽量保证各个分支的同一层次和主流程保持平行,这样的话如果有提前结束的分支,也非常清楚。
  2. 流程图不要过于复杂,过于复杂的话很容易混乱也不利于再次阅读,并且不要着急去考虑方法。
  3. 本例中需要抽象的类、属性和方法都不多。对于多类多方法的情况,考虑增加类图;多对象的交互行为考虑增加顺序图;而对于像本例这样的游戏属于单对象跨用例行为,可以考虑状态机图。

代码部分

  1. python的类属性和实例属性。与Java有static静态来表示属性的归属不同,python的实例属性定义在__init()__中,类属性仍然直接表示在类中。如果实例属性需要定期重置,可能需要重新定义一个init函数。
  2. 类之间的关系有至少三种,包括关联,泛化和依赖,如果类之间互相有对象的参数调用,需要先搞清楚类之间的关系,否则类方法可能有重复等不合理的地方。
  3. 提前结束的代码逻辑一般是由多层并列循环的跳出来实现的,但具体实现提出设计到goto的思想,一般代码中是通过设置提前结束标志来实现。本例中至少两处需用到,一是是否重复获取输入标志,一是是否提前结束本局标志。
  4. random类和sys.exit()方法
  5. python支持多重判断a==b==c
  6. 两种输出格式化字符串方法。
    print(f"%format", args)
    print("%format".format(args))

可改进部分

  1. 改进系统输入的代码部分,抛出异常或抓获checked exception,或是直接改进bug。
  2. 游戏逻辑可改进,包括电脑要牌逻辑,以及游戏规则等。
  3. 类属性和方法的访问安全问题。

你可能感兴趣的:(python)