Python实现21点游戏:面向对象编程实战
1. 为什么用Python实现Blackjack是个绝佳练习Blackjack21点作为赌场经典游戏规则简单却蕴含丰富策略恰好能全面覆盖面向对象编程的核心概念。我在教学过程中发现用Python实现这个游戏能同时锻炼以下关键能力类设计能力需要抽象出玩家、庄家、牌组、游戏规则等多个实体继承与多态不同角色如普通玩家与AI玩家可共享基础行为封装思想隐藏牌组洗牌、分数计算等实现细节异常处理处理玩家爆牌、无效输入等边界情况class Card: def __init__(self, suit, value): self.suit suit # 花色 self.value value # 牌面值 def get_numeric_value(self): if self.value in [J, Q, K]: return 10 elif self.value A: return 11 # 暂按11计算后续可调整 else: return int(self.value)关键提示Ace牌的特殊处理1或11是游戏策略的核心难点建议先统一按11计算后续在Hand类中实现动态调整2. 核心类结构设计与实现2.1 扑克牌的面向对象建模一套标准的Blackjack游戏需要以下核心类Card类表示单张扑克牌属性花色(suit)、面值(value)方法获取牌面数值(get_numeric_value)Deck类管理整副牌属性牌堆(cards)方法初始化牌组(initialize)、洗牌(shuffle)、发牌(deal)Hand类表示玩家手中的牌属性牌列表(cards)、是否庄家(is_dealer)方法加牌(add_card)、计算分数(calculate_score)、是否爆牌(is_busted)class Deck: suits [♥, ♦, ♣, ♠] values [A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K] def __init__(self): self.cards [] self.initialize() def initialize(self): self.cards [Card(suit, value) for suit in self.suits for value in self.values] def shuffle(self): random.shuffle(self.cards) def deal(self): return self.cards.pop() if self.cards else None2.2 游戏流程的状态管理游戏主循环需要处理多个状态转换graph TD A[开始游戏] -- B[初始发牌] B -- C[玩家回合] C -- D{玩家选择} D --|要牌| E[发牌并检查] D --|停牌| F[庄家回合] E -- G{是否爆牌} G --|是| H[游戏结束] G --|否| C F -- I[庄家抽牌逻辑] I -- J{庄家是否继续} J --|是| I J --|否| K[结算胜负]注意实际代码中应避免使用全局状态变量建议用Game类封装游戏状态3. 核心算法实现细节3.1 分数计算的特殊处理Blackjack的分数计算有三大特殊规则Ace可计为1或11两张初始牌为A10/J/Q/K称为Blackjack庄家必须抽牌到17点以上class Hand: def calculate_score(self): score sum(card.get_numeric_value() for card in self.cards) ace_count sum(1 for card in self.cards if card.value A) while score 21 and ace_count: score - 10 # 将A从11转为1 ace_count - 1 return score def is_blackjack(self): return len(self.cards) 2 and self.calculate_score() 213.2 庄家自动决策逻辑庄家的行为由固定规则决定class Dealer(Player): def make_decision(self, deck): while self.hand.calculate_score() 17: self.hit(deck) if self.hand.is_busted(): break4. 完整游戏实现与扩展建议4.1 主游戏循环示例class BlackjackGame: def __init__(self): self.deck Deck() self.player Player() self.dealer Dealer() def start(self): self.deck.shuffle() # 初始发牌 self.player.hand.add_card(self.deck.deal()) self.dealer.hand.add_card(self.deck.deal()) self.player.hand.add_card(self.deck.deal()) self.dealer.hand.add_card(self.deck.deal()) # 玩家回合 while not self.player.hand.is_busted(): action input(要牌(h)或停牌(s)? ) if action.lower() h: self.player.hit(self.deck) print(f当前分数: {self.player.hand.calculate_score()}) else: break # 庄家回合 if not self.player.hand.is_busted(): self.dealer.make_decision(self.deck) # 结算 self.show_result()4.2 常见问题与调试技巧牌组耗尽问题在deal()方法中添加检查if not self.cards: self.initialize(); self.shuffle()输入验证while True: try: bet int(input(请输入下注金额: )) if 0 bet self.chips: break except ValueError: pass测试建议固定随机种子测试特定牌序random.seed(42)单元测试重点Ace计分逻辑、爆牌判断、Blackjack检测5. 项目扩展方向完成基础版本后可以考虑多玩家支持用列表管理多个Player实例下注系统添加Chips类管理筹码AI玩家实现基本策略表硬17点停牌等GUI界面使用Pygame或Tkinter网络对战通过socket实现多人游戏class AIPlayer(Player): BASIC_STRATEGY { (5,2): H, (5,3): H, ..., (20,10): S } def make_decision(self, dealer_up_card): player_score self.hand.calculate_score() dealer_value dealer_up_card.get_numeric_value() return self.BASIC_STRATEGY.get((player_score, dealer_value), S)我在实际开发中发现处理Ace牌的动态计分时最容易出现逻辑错误。建议单独编写测试用例验证以下场景手牌A5A应计为17手牌AAAA7应计为21手牌10ABlackjack与A10的区别处理