跳到主要内容

用 Python 实现 21 点游戏(二):发牌初始化与回合流程搭建

上一篇我们已经完成了扑克牌(Card)与牌堆(Deck)的基础类。这一篇我们进入游戏主控类 Game 的搭建:先实现「加牌」方法,再完成「发牌初始化」,最后把玩家回合、庄家回合、结算等流程串起来,并运行验证发牌与手牌显示效果。

一、目标​

本次要完成三件事:

  1. 为游戏增加「继续要牌」的方法 hit,可对玩家或庄家的手牌追加一张牌。
  2. 实现发牌初始化 deal_init:玩家与庄家各发两张牌,并显示手牌(庄家保留一张底牌不显示)。
  3. 定义 play 主流程,依次调用发牌、显示手牌、玩家回合、庄家回合、结算等方法,先跑通整体骨架。

二、实现「继续要牌」方法 hit​

hit 用于给指定的一手牌追加一张牌。由于玩家和庄家都可能要牌,所以方法需要接收一个 hand 参数,表示「给谁的手牌加牌」。

在 Game 类中新增:

def hit(self, hand):
# 从牌堆发一张牌,追加到指定手牌中
hand.add_card(self.deck.deal())

这里复用了前面已经写好的两个能力:Hand.add_card() 负责把牌加入手牌,Deck.deal() 负责从牌堆发出一张牌。之所以前面在 Deck 部分写了示例代码并演示过效果,就是为了在这里可以直接拿来用——deal() 返回的正是一个 Card 对象。

三、实现发牌初始化 deal_init​

发牌规则:玩家一张、庄家一张、玩家一张、庄家一张,共四张牌。两张牌无论如何都不会超过 21 点,所以初始化阶段不需要判断爆牌。

def deal_init(self):
# 玩家和庄家各发两张牌
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())

四、显示手牌 show_the_hand​

发完牌后要展示牌面:玩家手牌全部亮出,庄家只显示一张,另一张作为底牌用问号代替。

def show_the_hand(self):
# 显示玩家手牌
print("玩家手牌:")
for card in self.player_hand.cards:
print(card)

# 显示庄家手牌,只亮一张,另一张为底牌
print("庄家手牌:")
print(self.dealer_hand.cards[0], "?")

玩家手牌通过遍历 cards 全部打印;庄家则直接取列表第一张,第二张不显示,用 ? 让玩家去猜。

运行 game.play() 后输出玩家两张手牌与庄家一张明牌加问号底牌

五、搭建 play 主流程​

把各个阶段按顺序串起来。玩家回合、庄家回合、结算先占位(pass),后续再逐步丰富。

def player_turn(self):
pass

def dealer_turn(self):
pass

def game_over(self):
pass

def show_result(self):
pass

def play(self):
self.deal_init() # 各发两张牌
self.show_the_hand() # 展示牌面信息
self.player_turn() # 玩家回合
self.dealer_turn() # 庄家回合
self.game_over() # 游戏结束
self.show_result() # 显示结果

在类内部调用其他方法,直接使用 self.方法名() 即可。整个调用链非常清晰:外部只操作 Game 类,Game 内部通过 self 调度各阶段,各阶段再调用 Card、Deck、Hand 等类相互配合。

六、测试运行​

实例化并启动游戏:

game = Game()
game.play()

运行后可以看到:程序先给玩家发两张、再给庄家发两张,与真实游戏开局一致。输出中玩家手牌完整显示(如红桃 J、黑桃 9),庄家手牌只显示一张,另一张用问号代替——这正是底牌不让你看到的效果。

实例化 Game 并调用 play 后完整开局输出,庄家底牌显示为问号

七、小结​

本篇完成了游戏骨架的关键一步:

  • hit(hand):复用 Hand.add_card() 与 Deck.deal(),实现按手牌追加一张牌;
  • deal_init():玩家、庄家交替各发两张;
  • show_the_hand():玩家全亮、庄家留一张底牌;
  • play():把发牌、显示、玩家回合、庄家回合、结算按顺序串起来。

发牌与手牌显示已经跑通,玩家回合、庄家回合、结算目前还是占位方法。下一篇我们将依次实现这些方法,让游戏真正可以「要牌、停牌、比大小」。

动图规划建议:录制一次 game.play() 从发牌到输出手牌的完整控制台过程,突出庄家底牌以问号隐藏的效果,适合放在「测试运行」小节末尾。

课程推荐

想系统学习这门课程?

你已阅读完本文。需要更完整的知识体系,可以继续了解「Vibe Coding 实战课程」。

查看课程详情