2. 纸牌游戏:
本次作业我们将设计和实现一个扑克牌游戏。游戏规则如下:
游戏开始时,扑克牌在两名玩家中平分(扑克牌总共 52 张,每位玩家分 26张)。
在每个回合中,两个玩家都会展示手中的第一张牌。扑克牌等级较高的玩家将同时获得这两张牌,并添加到自己的牌组中。然后玩家们重新调整手牌。
若(2)中两位玩家的扑克牌等级一样(即打平),玩家们将展示手中的第二张牌。如果不再有平局,扑克牌等级较高的玩家将同时获得这四张牌;如果继续平局,玩家们将展示手中的第三、四、……张牌,直到平局被打破。
游戏持续到一个玩家收集完所有 52 张牌为止。若(3)中平局持续到一方无牌,则还有手牌的玩家获胜;若两个玩家都没有牌了,则打平。
想一想,为了实现上面这个游戏,我们需要什么类和方法?我们知道每张扑克牌都有一个花色和一个等级,如红桃 K。因此,创建一个“扑克牌”(Card)的类, 并包含这两个“属性”是有意义的。此外,在我们的游戏中,“大”的扑克牌是指等级较高的牌;“打平”的牌是指等级相同的牌(不管两张牌的花色)。所以我们需要自定义一些规则来衡量“大”、“小”、“相等”的牌。
每位玩家都有一“手”牌(Hand),是扑克牌的集合,我们也可以将它写成一个类, 可以从一“手”牌中取出牌、或者洗牌。一“手”牌中如果包含所有的 52 张扑克牌, 叫牌堆(Deck)。每位玩家(Player)有一“手”牌和名字。
最后我们需要一个类叫 Game,实现游戏的逻辑。
作业任务:
补充提供的文件 card_game.py 中 Card,Hand,Deck,Player,Game 类的实现(标注“补充代码”处)。测试代码以验证正确性。
【解题思路与补全代码】
1、补全类的比较函数:
首先观察到第28行到第34行中的比较函数全部留空,需要补充,因此,只需在此补全对类的大于小于以及等于函数进行补充即可。
代码补充如下:
首先判断是否也为Card类,如果不为Card类,则抛出格式异常;如果也为Card类,则直接返回卡片中rank的大小即可
2、补充收到多张卡片的函数:
当收到多张卡片时,直接将收到的卡片列表补充在当前player的手牌列表中即可。
3、补充收到单张卡片的函数:
当收到一张卡片时,直接append在当前player的手牌列表中即可。
4、补充给出单张卡片的函数:
首先进行判空,如果为空则直接返回None,若不为空,则将第一个卡片删除,并将删除的卡片返回。
5、补充给出多张卡片的函数:
直接将该玩家的手牌全部清空,并返回即可。
6、补充分配52张牌的代码:
若给牌堆分配52张一整副扑克牌,只需利用循环先遍历花色,再遍历点数并依次加入到牌堆中即可。
7、补充玩家类的初始化构造函数
此处只需对Player的名字以及手牌进行初始化即可。要注意,此处对列表的拷贝,不能采用等号进行浅拷贝,而必须使用copy函数进行深拷贝。
8、补充发牌函数:
根据注释的要求,需要实现两个人的轮流取牌,因此只需让牌堆轮流给两个人一张牌即可,直至牌堆为空。
9、补充每轮游戏代码:
首先不妨考虑两种最后的可能情况,第一种即,最后取完扑克牌后胜负已分,且牌少的输了之后正好没有牌。第二种即,一直平手,到最后一方没有牌,而输掉,因此,需要将情况分开讨论。首先对于每轮游戏,如果两个人都有牌,则可以直接一直循环游戏下去直至一方的牌为0。
为了方便进行牌的转移,不妨设置一中间变量temp
使用temp来完成每次牌的比较与转移。
当两方牌数量一直大于零时,可以一直进入循环,当得到胜者,先将temp中的所有牌给到胜者,并直接返回,如果未得到结果,则进行下一次的比较。
此外,如果在取牌过程中,尽管当前为平局,但一方的牌被取空,则被取空的一方输;若两方都同时被取空,则为平局。
10、开始游戏的代码:
开始游戏时首先开始发牌,发牌后,则使用while循环判断两方的牌的数量并记录下最后一轮的获胜者。最终获胜者即为最后一轮的获胜者,将结果输出即可。此外,每一轮过程中由于需要重新调整两方手牌,需要对手牌进行随机排列。
【测试结果】
1、test_Card()
2、test_Hand()
3、test_Deck()
4、test_Player()
5、test_Game()(即正式运行游戏):
将依次输出中间过程:
非平局情况:
出现平局的情况:
最后结果:
实验结论:
注意append没有返回值:在第一题的编程过程中,我使用了如下代码:
Temp = b.append(1)
本想获取列表b加了元素1后的列表,结果发现不论怎么处理,temp均为空,通过查阅资料,发现这是由于append没有返回值造成的。因此如果想获取表b加了元素1后的列表可以待完成append后再进行操作。
通过仔细观察获得状态机的通用状态转移函数:
在第一题中,需要编写一个适用于三个继承子类的状态转移函数,通过仔细观察,我发现每次处理的次数与传入数据的长度相等。因此,我通过循环完成了传入数据长度的循环,从而完成了转态转移函数的编写。
在第二题的编程过程中,我发现一个问题,不论怎么操作Player1,另一个Player2均有相同的操作,两Player的手牌列表时刻保持一致。通过上网查阅资料,我发现,这是由于我使用的a=b进行赋值产生的问题。即对于列表使用等号,表示的是一种浅拷贝,即只传地址不传值,实际上是共用了一个列表。因此若想解决这个问题,可以采用列表的copy方法,即:
A=b.copy()