首页 » 与孩子一起学编程 » 与孩子一起学编程全文在线阅读

《与孩子一起学编程》23.3 创建一副牌

关灯直达底部

游戏中经常使用的另一种随机事件是抽牌。这是随机的,因为会洗牌,所以你不知道下一张是什么牌。每次洗牌时,顺序都不同。

至于掷骰子和扔硬币,我们说每次扔出都有相同的概率,因为硬币(或骰子)没有记忆。不过纸牌就不同了。从一副牌中抽牌时,剩下的牌越来越少(大多数游戏中都是如此)。这会改变抽出剩余各张牌的概率。

例如,开始时是一整副牌,抽出红桃 4 的机会是 1/52,或者大约 2%。这是因为一副牌里有 52 张牌,而只有一张红桃 4。如果继续抽牌(还没有抽到红桃 4),整副牌只剩下一半时,得到红桃 4 的机会就是 1/26,或者大约 4%。剩下最后一张牌时,如果还没有抽到红桃 4,说明抽出红桃 4 的机会就是 1/1,或者 100%。可以肯定下一个肯定会抽到红桃 4,因为只剩下这一张牌了。

为什么要告诉你所有这些呢?我只是想说明:如果要建立一个利用一副纸牌实现的计算机游戏,就需要在整个过程中跟踪已经从这副牌中取走了哪些牌。要做到这一点有一个很好的方法,就是利用列表。开始时列表中包含一副牌中的所有 52 张牌,我们使用 random.choice 函数随机地从这个列表中选牌。每选出一张牌,可以使用 remove 把它从列表(这副牌)中删除。

洗牌

在一个真正的纸牌游戏中,我们要洗牌,也就是说要把纸牌杂乱地混在一起,让它们有一种随机的顺序。这样一来,我们可以只取最上面的一张牌,这张牌是随机的。不过利用 random.choice 函数,总能从列表中随机选择。我们不必取“最上面”的牌,所以“洗牌”没有意义。这就像把牌摊开,说“选一张牌,随便哪张都行”。在一个纸牌游戏中,如果每个人都这么做,这会很耗费时间,不过在计算机程序中这非常容易。

纸牌对象

我们要使用一个列表作为“一副牌”。不过这些牌本身怎么表示?如何存储每张牌呢?是存储为字符串还是整数?我们需要知道每张牌的哪些方面?

在纸牌游戏中,我们通常需要知道一张牌的 3 个方面。

 
  • 花色——方块、红桃、梅花或黑桃。

  • 点数——A、2、3,…10、J、Q、K。

  • 分值——用数字编号的牌(2 到 10),通常分值就等于牌的点数。对于 J、Q 和 K,分值通常是 10,A 的分值可能是 1、11 或者另外某个值,这要依具体游戏而定。

 点数  分值  A  1 或11  2  2  3  4  4  4  5  5  6  6  7  7  8  8  9  9  10  10  J  10  Q  10  K  10 

所以我们要跟踪这 3 个方面,而且需要用某种容器把它们汇集在一起。利用列表可以做到,不过我们还必须记住每一项分别是什么。另一种办法是建立一个包含右面属性的“牌”:

card.suitcard.rankcard.value

下面就采用这种做法。我们还会增加另外两个属性 suit_idrank_id

 
  • suit_id 表示花色,是一个从 1 到 4 的数,其中 1 = 方块,2 = 红桃,3 = 梅花,4 = 黑桃。

  • rank_id 是从 1 到 13 的数,其中

    1 = A

    2 = 2

    3 = 3

    10 = 10

    11 = J

    12 = Q

    13 = K

增加这两个属性的原因是,这样我们可以很容易地使用一个嵌套 for 循环建立一副 52 张牌。可以用一个内循环对应点数(1 到 13),另外用一个外循环对应花色(1 到 4)。纸牌对象的__init__ 方法根据 suit_idrank_id 创建其他属性 ( 花色、点数和分值)。这样还可以很容易地比较两张牌的点数,看哪一张牌的点数更大。

还应当另外增加两个属性,来方便在程序中使用这个纸牌对象。程序需要打印纸牌时,它可能希望打印类似“4H”或“4 of Hearts”(红桃 4)。对于花牌,可能打印成“JD”或“Jack of Diamonds”(方块 J)。我们将增加属性 short_namelong_name,这样程序很容易就可以打印纸牌的不同描述(包括短名和长名)。

下面为纸牌建立一个类。见代码清单 23-4。

代码清单 23-4 Card 类

代码清单 23-4 不是一个完整的程序。这只是 Card 类的类定义。因为这个类可以在不同程序中反复使用,可能应该把它建立为一个模块。把代码清单 23-4 的代码保存为 cards.py。

代码中的错误检查确保 rank_idsuit_id 在正常范围内,而且是整数。否则,在程序中显示纸牌时你就会看到诸如“7 of SuitError”或“RankError of Clubs”之类的错误结果。

现在需要建立纸牌的一些实例——实际上,我们完全可以建立一整副牌!要测试我们的 Card 类,下面建立一个程序,创建一副 52 张的牌,然后随机选 5 张并显示它们的属性。代码清单 23-5 提供了相应代码。

代码清单 23-5 建立一副牌

内循环处理一种花色中的每张牌,外循环处理每种花色(13 张牌 × 4 种花色 = 52 张牌)。然后代码从这副牌中选出 5 张,放在手中(形成一手牌)。另外还要从这副牌中删除选出的这些牌。

如果运行代码清单 23-5 中的代码,应该能得到类似下面的结果:

7D = 7 of Diamonds  Value: 79H = 9 of Hearts  Value: 9KH = King of Hearts  Value: 106S = 6 of Spades  Value: 6KC = King of Clubs  Value: 10

再运行这个代码,会得到 5 张不同的牌。不论你运行多少次,都不会有同一张牌在手中出现两次的情况。

现在我们可以建立一副牌,并且可以从中随机地抽牌,增加到自己手中。听起来已经万事俱备,可以建立一个纸牌游戏了!在下一节,我们会建立一个纸牌游戏,这样你就可以与计算机玩这个游戏了。