问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

日拱一卒,伯克利太强了,手把手带你写一个植物大战僵尸

发布网友 发布时间:2024-09-26 07:00

我来回答

1个回答

热心网友 时间:2024-10-07 00:50

大家好,日拱一卒,我是梁唐。本文始发于公众号:Coder梁

今天我们继续来肝伯克利的CS61A,这一次我们做的是这门课的第二个大作业。

这个项目非常有意思,让我们做一个类似植物大战僵尸的小游戏。只不过这里改成了蚂蚁大战蜜蜂,蜜蜂一波一波来袭,我们要建造各种功能的蚂蚁抵御蜜蜂的进攻。

完全做好之后的运行效果是这样的:

课程链接

项目原始文档

Github

这一次的项目难度不算高,主要讲解的是关于Python当中面向对象的部分,教我们学会设计类,以及使用类的继承。

核心概念

Colony:游戏发生的场景,由多个通路(tunnel)组合而成,每一个通道是由一个一个网格(place)组成的

Places:网格,每一个网格连接另外的网格组成通路,玩家可以在网格上放置一只蚂蚁,然而每个网格可以有多只蜜蜂

The hive:蜂巢,蜜蜂的老家,蜜蜂从hive中出来,来到colony中

Ants:蚂蚁,玩家在游戏当中用来操控对付蜜蜂的士兵。每一种蚂蚁在每一个回合可以采取一个特定的行动(action),生产蚂蚁需要消耗一定的食物(food)。最基础的两种蚂蚁是**HarvesterAnt**(收割蚂蚁),它每个回合可以采集1点食物,和**ThrowerAnt**(投掷蚂蚁),每个回合每个回合可以朝蜜蜂丢一片树叶

Bees:蜜蜂,游戏当中我们需要对付的敌人。每一个回合当中,如果没有蚂蚁拦路的话,蜜蜂会前进一步。否则会攻击拦路的蚂蚁,当有一只蜜蜂到达tunnel末端时游戏结束

Queen Ant:蚁后,每局游戏只有一只蚁后。蚁后也有攻击能力,除此之外它还能鼓舞士气,提升其他蚂蚁的攻击力。如果蜜蜂击杀了蚁后,同样游戏结束

运行游戏

有两种运行游戏的方式,一种是基于文本命令行的形式,用于开发测试:

python3?ants.py

我们也可以通过游戏界面运行游戏,用于最终演示:

python3?gui.py?-d?easy?--food?10

-d 控制难度 test/easy/medium/hard/insane

-w 地图是否有水

--food 开局食物数量

Phase 1 gameplay

在第一个阶段,实现基本的蚂蚁(HavesterAnt和ThrowerAnt)。

阶段结束时,可以运行最基础的游戏版本

Problem 0

阅读代码回答一下几个问题:

insect类中的armor属性的作用是?在游戏当中它会改变吗?如果会改变,触发条件是?

Ant类所有的属性有哪些?

Ant类的armor属性是类属性还是实例属性?为什么

Ant部分子类的damage属性是类属性还是实例属性,为什么

Ant和Bee在是从哪一个类中继承来的?

Ant和Bee类的实例有什么共同点?

在任意给定时间,一个Place可以出现多少insect?

我们可以以命令行交互的形式回答这些问题(原问题是英文的),下载项目代码之后,输入一下命令即可:

python3?ok?-q?00?-u

题目和选项都会以命令行文本的形式展示,通过输入选项的形式来答题。

老师为每道题都设置了测试样例,但是需要我们先通过问题才能使用这些样例。解锁测试样例和进行测试不会进行身份验证,即使我们不是伯克利的学生也能享用,非常非常人性化。

Problem 1

首先添加食物的开销,以及开发HarvesterAnt。

目前生产蚂蚁不需要任何消耗,因此游戏完全没有难度。你会注意到Ant这个类中的属性food_cost设置成了0。在下列的子类当中覆盖这个属性,将它设置成正确的值。

ClassFood CostArmor HarvesterAnt21 ThrowerAnt31

现在生产蚂蚁需要花费了,因此我们需要HarvesterAnt来收集食物。开发HarvesterAnt类,使得它每回合在行动时可以添加colony.food。

在开始开发之前,先回答问题进行测试,测试通过可以解锁测试样例:

python3?ok?-q?01?-u

当完成实现之后,进行测试:

python3?ok?-q?01答案

action函数会在每个回合被调用,被调用时将colony.food增加即可

class?HarvesterAnt(Ant):????"""HarvesterAnt?proces?1?additional?food?per?turn?for?the?colony."""????name?=?'Harvester'????implemented?=?True????food_cost?=?2????def?action(self,?colony):????????"""Proce?1?additional?food?for?the?COLONY.????????colony?--?The?AntColony,?used?to?access?game?state?information.????????"""????????#?BEGIN?Problem?1????????"***?YOUR?CODE?HERE?***"????????colony.food?+=?1????????#?END?Problem?1Problem 2

完善Place类的构造函数,使得它能够追踪入口(entrance)。

目前,一个place仅仅会记录出口(exit),我们希望place也能记录它的入口。每一个place只会有一个入口,有了入口之后,Ant就可以知道面前有哪些蜜蜂了。

然而,只是简单地将一个entrance传进构造函数是有问题的。因为我们在当前place创建之前就需要这两个属性,会导致循环依赖。为了解决这个问题,我们采用如下方法:

新创建的Place的entrance为None

如果Place拥有一个exit,那么将exit的入口设置成Place

提示:

在__init__函数中,self会绑定当前对象

如果觉得困惑,可以先画出两个Place。在GUI当中,一个Place的入口是它的右边的Place,出口是它的左边

在开始编码之前,先回答问题,确保已经理解了

python3?ok?-q?02?-u

完成之后,使用下列命令来测试:

python3?ok?-q?02答案

exit是外界传入的,当exit不为空时,将exit.entrance设置成self即当前Place即可。

class?Place(object):????"""A?Place?holds?insects?and?has?an?exit?to?another?Place."""????def?__init__(self,?name,?exit=None):????????"""Create?a?Place?with?the?given?NAME?and?EXIT.????????name?--?A?string;?the?name?of?this?Place.????????exit?--?The?Place?reached?by?exiting?this?Place?(may?be?None).????????"""????????self.name?=?name????????self.exit?=?exit????????self.bees?=?[]????????#?A?list?of?Bees????????self.ant?=?None???????#?An?Ant????????self.entrance?=?None??#?A?Place????????#?Phase?1:?Add?an?entrance?to?the?exit????????#?BEGIN?Problem?2????????"***?YOUR?CODE?HERE?***"????????if?self.exit?is?not?None:????????????self.exit.entrance?=?self????????#?END?Problem?2Problem 3

下面我们实现ThrowerAnt类,用来攻击蜜蜂。

首先,它必须知道它需要攻击哪一只蜜蜂。最初版本的代码nearest_bee方法,只会攻击和它在同一个格子里的蜜蜂。你的任务是修改它,让ThrowerAnt会对距离它最近的蜜蜂(蜂巢中的除外)使用throw_at方法。

nearest_bee方法随机返回距离它最近的格子当中的一只蜜蜂:

从当前ThrowerAnt在的Place开始遍历

对于每一个Place,如果它上面有蜜蜂, 返回任意一只。如果没有,检查它前一个Place(entrance)

如果没有蜜蜂可以攻击,返回None

提示:

random_or_none`函数可以随机返回序列中一个元素,如果序列为空时返回`None

开始开发之前先回答问题,确保已经充分理解

python3?gui.py?-d?easy?--food?100

开发完成之后,进行测试:

python3?gui.py?-d?easy?--food?101

测试通过之后,可以模拟一下效果:

python3?gui.py?-d?easy?--food?102

效果如下:

答案

通过while循环来遍历place,直到place上有蜜蜂,随机返回一只即可。

python3?gui.py?-d?easy?--food?103Phase 2 Ants Attack

现在你已经开发好了两个最基本的蚂蚁类型,可以进行最基础的游戏了。

在这个阶段当中,你将会开发更多的拥有不同技能的蚂蚁类型。当你完成了Ant子类的开发之后,你需要将其中implemented属性改成True才能在GUI中使用它。你可以在每完成一种新的蚂蚁之后进行游戏测试。

这个阶段开发完成之后,你可以尝试使用命令:python3 gui.py -d easy当前的蚂蚁类型来对决一大波蜜蜂。你也可以使用-d normal, -d hard或者-d insane来尝试更高的难度。如果你觉得很难获胜,也许你需要开发更多类型的蚂蚁。

Problem 4

ThrowerAnt是一个很好的进攻单位,但如果价格便宜一点就更好了。开发ThrowerAnt的两个子类,它们拥有更低的造价,但在投掷距离上会有*:

LongThrower只能throw_at距离大于等于5的蜜蜂。对于距离小于等于4的蜜蜂*为力,当多只蜜蜂出现,也只会攻击距离满足条件的蜜蜂

ShortThrower只能throw_at距离小于等于3的蜜蜂。

以上两种蜜蜂都不能攻击距离刚好是4的蜜蜂,只放一只攻击蜜蜂是无法获胜的

ClassFood CostArmor ShortThrower21 LongThrower21

一个比较好的实现方式是,让它们继承ThrowerAnt类的nearest_bee方法。因为它们选择攻击目标的逻辑是一样的,除了ShortThrower和LongThrower拥有最大和最小距离的*。

所以你需要修改ThrowerAnt中的nearest_bee方法来使用min_range和max_range属性,只返回在射程中的蜜蜂

原始ThrowerAnt没有射程的*,所以你需要为它添加对应的min_range和max_range属性,使得它不会影响ThrowerAnt,接着给子类LongThrower和ShortThrower设置合适的范围和食物开销。

提示

float('inf')返回代表无穷大的浮点数,和其他任何数字相比都要更大

别忘了将LongThrower和ShortThower的implemented属性改成True

在你开始开发之前,先完成测试:

python3?gui.py?-d?easy?--food?104

完成之后,进行测试:

python3?gui.py?-d?easy?--food?105答案

意思是说我们修改父类ThrowerAnt而不改子类,让父类拥有无限的射程保证父类的功能不变,又可以支持子类射程的要求。

给子类设置合适的射程,使得不需要重复实现nearest_bee方法,只需要修改配置就可以实现功能

python3?gui.py?-d?easy?--food?106Problem 5

实现FireAnt类,FireAnt类拥有一个特别的方法rece_armor:当FireAnt的护甲减为0或更低时:它会攻击相同格子里的所有蜜蜂,将它们的护甲减去它的damage(默认是3)。

ClassFood CostArmor FireAnt51

提示:

攻击蜜蜂会导致蜜蜂死去被移出场地,如果一遍遍历一个list,一遍移除list中的内容,可能会导致出错。

所以Python 官方教程中建议,如果你需要一遍遍历一遍修改一个序列(比如复制/删除选中的元素等),最好先将序列拷贝。你可以调用list构造函数或者使用切片诸如a[:]来完成拷贝

当你开发完成之后,将implemnted设置成True

测试之前,先答题,确保理解正确

python3?gui.py?-d?easy?--food?107

开发之后,进行测试:

python3?gui.py?-d?easy?--food?108答案

护甲减到0以下就攻击同一格中所有的蜜蜂,由于攻击蜜蜂可能导致蜜蜂死亡,place.bees发生变化。所以我们需要先拷贝序列,再对拷贝中的蜜蜂进行攻击。

python3?gui.py?-d?easy?--food?109

可以用已有的蚂蚁玩一两局游戏了,FireAnt的伤害很高,灵活使用获胜并不难

python3?gui.py?-d?easy?--food?102Problem 6

实现HungryAnt,它可以选择和它同一格的蜜蜂进行吞食。吞食完一只蜜蜂之后,需要经过3轮消化才能再次吞食。

ClassFood CostArmor HungryAnt41

给HungryAnt类添加一个time_to_digest的类属性,它表示HungryAnt在吞吃之后消化需要的回合数。同样,给HungryAnt添加一个实例属性digesting标记还需要消化的回合数(默认是0,因为初始时还没有吞吃)。

实现action函数,检查是否在消化,如果是,减少digesting值,否则随机选择同一格的蜜蜂进行吞吃(将蜜蜂的护甲置为0,重置digesting)

编码之前先答题,确保理解正确

python3?ok?-q?00?-u1

实现之后,进行测试:

python3?ok?-q?00?-u2答案

注意类属性和实例属性的区别即可

python3?ok?-q?00?-u3Problem 7

开发忍者蚂蚁(NinjaAnt),它会攻击所有经过的蜜蜂,并且不会被叮。

ClassFood CostArmor NinjaAnt51

NinjaAnt不会阻碍蜜蜂的前进,要实现这一点,需要在Ant类中加上一个新的类属性blocks_path,表示是否会阻碍蜜蜂前进,在Ant中设置成True。这样的话继承自Ant的子类默认都是True,在NinjaAnt中设置成`False

其次修改Bee中的方法blocked,在没有蚂蚁拦路或者拦路的蚂蚁blocks_path为False时返回False。这样的话,蜜蜂将会从NinjaAnts身边飞过。

最后,我们希望NinjaAnt会攻击所有飞过的蜜蜂。在action函数当中实现这个功能,让它能够攻击所有在同一格的蜜蜂。和FireAnt一样,我们需要遍历可能发生改变的序列,所以需要先进行拷贝

开发前答题:

python3?ok?-q?00?-u4

开发后测试:

python3?ok?-q?00?-u5

尝试只用HarvestAnt和NinjaAnt获胜。

答案python3?ok?-q?00?-u6Phase 3: But They Also Protect

我们现在已经有了很多进攻手段,也需要开发一些防御机制。

在这个阶段,我们将会开发一些拥有防御功能的蚂蚁,比如增加护甲,或者是保护同伴。

Problem 8

我们将要开发WallAnt为蚂蚁王国提供保护,它每个回合不会做任何事情只会阻挡蜜蜂,拥有很高的护甲,类似于植物大战僵尸里的坚果。

ClassFood CostArmor WallAnt44

和之前的蚂蚁不同,我们没有提供类的初始代码。

你需要从零开始实现WallAnt,将类中的name属性设置成Wall,implemented设置成True,这样图像才能正常显示。

开发前答题:

python3?ok?-q?00?-u7

开发后测试:

python3?ok?-q?00?-u8答案

实现WallAnt继承自Ant,设置对应的参数,并且调用父类的构造函数设置护甲(可以参考其他类)。

python3?ok?-q?00?-u9Problem 9

我们的蚂蚁除了WallAnt之外都太脆弱,所以我们希望开发BodyguardAnt为蚂蚁们提供保护。

ClassFood CostArmor BodyguardAnt42

BodyguardAnt和普通的蚂蚁不同,因为它是一个容器。它可以容纳一只另外的蚂蚁,并且保护它,它们在同一个Place里。当有蜜蜂攻击时,只有容器会受到攻击。

容器内的蚂蚁每回合同样可以执行action,当容器被摧毁,被保护的蚂蚁依然留存。

每个BodyguardAnt拥有一个实例属性ant,它用来存储被保护的蚂蚁。初始时因为还没有保护蚂蚁,所以是None。实现contain_ant函数,它会传入一个ant表示要被保护的ant,需要将它赋给ant属性。另外,实现BodyguardAnt的action方法,来执行被保护的ant的action。

另外,你需要做如下的改动,保证容器和被它容纳的蚂蚁一起占据同一个格子。每个格子最多两只蚂蚁,并且有一只一定是容器。

添加Ant.container类属性,表明某一个Ant的子类是否是容器。对于Ant的所有子类来说,除了BodyguardAnt以外都必须是False,除了BodyguardAnt是True。

实现Ant.can_contain方法,接收一个参数other,当满足一下条件时返回True:

当前的ant是容器

当前的ant没有容纳蚂蚁

other蚂蚁不是容器

修改Place.add_insect函数,使得它兼容容器的case,该函数接收一个参数insect,表示要放置在Place上的昆虫,考虑以下情况:

当前格子上有一个ant,并且可以容纳传入add_insect函数的insect,那么将insect容纳进ant当中

如果insect是容器,可以容纳当前格子上的ant,并且将Place.ant设置成insect

如果不满足上述条件,抛出AssertionError异常

编码前答题:

python3?ok?-q?01?-u0

开发后测试:

python3?ok?-q?01?-u1答案python3?ok?-q?01?-u2Problem 10

BodyguardAnt提供了很好的防御,但常言道:最好的防御是进攻。

TankAnt是一个可以进攻的容器,除了提供保护之外,它每回合还能对和它相同格子的蜜蜂产生1点伤害

ClassFood CostArmor TankAnt62

你只能修改TankAnt类中的代码,如果你发现你还需要修改其他地方的代码,那么说明你之前的实现可能有点问题。想办法在不修改之前代码的前提下,完成TankAnt的功能。

答题:

python3?ok?-q?01?-u3

开发后测试:

python3?ok?-q?01?-u4答案python3?ok?-q?01?-u5Phase 4: Water and Might

在这个最终阶段,你需要开发一个新的格子类型和能够使用它的蚂蚁。

这些蚂蚁当中有最重要的一种:蚁后

Problem 11

让我们为蚂蚁王国当中添加水池,现在我们只有两种格子:Hive和Place. 为了让游戏更有趣,让我们开发新的类型Water。

只有watersafe的蚂蚁可以在水池中存活,为了判断昆虫是否有能力在水中生存,需要在Insect类中添加一个类属性watersafe,默认设置成False。因为蜜蜂可以飞,所以蜜蜂的watersafe是True。

为Water类开发add_insect方法。首先调用Place.add_insect方法来添加昆虫,这一步不需要考虑昆虫的生存能力。如果昆虫不是防水的,那么调用rece_armor方法,将昆虫的护甲减到0。不要复制粘贴代码,使用那些已经开发好的代码。

开发前答题:

python3?ok?-q?01?-u6

开发后测试:

python3?ok?-q?01?-u7

当你开发完成之后,可以使用--water参数来开启水池。

python3?ok?-q?01?-u8答案

在Water类当中,我们可以调用Place中的函数来完成功能,这样可以尽可能复用代码。

别忘了给对应的子类设置watersafe的属性

python3?ok?-q?01?-u9Problem 12

现在没有蚂蚁可以在水中生存,所以我们要开发ScubaThrower蚂蚁,它是ThrowerAnt的子类,开销更大,但能够在水中生存,但和它的基类不同的是,ScubaThrower蚂蚁在水中不会损失护甲(蜜蜂无法下水攻击)。

ClassFood CostArmor ScubaThrower61

我们没有为你提供该类的初始代码,需要从头完成

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
宁波社保卡办理需要什么材料 宁波社保卡如何申领 有什么高性价比的隔离霜可以推荐? 防晒效果好的隔离霜有哪些值得入手? 下雨天经常爬在房子墙上的那种软体动物,不是蜗牛没壳的,可以在墙上把身... 原来是美男啊里面插曲、主题曲都有哪些? 请问下各位大虾,我在外地用外地农行卡网上转账到本地邮政卡星期六转账的... 为什么用支付宝买东西明明我余额足够,付款时却说我余额不足?2个... 萍乡烛式过滤器品牌 衡水烛式过滤器品牌推荐 ...just a place (to go) and fill your mind with numbers.翻译,并解 ... 梦见别人送了四个元宝形状的水晶 【常用】汽车销售工作总结范文简短精选范本 汽车销售个人年终工作总结 守规则除隐患安全文明出行手抄报 守规则除隐患安全文明出行画报 情绪管理有哪些方法 李忠斌中国广告大师、企业策划人、文学愤青李忠斌 怎样才能修炼成一位不吼叫的妈妈 看看这些方法 如何做一个成功的房产销售 新手怎么做房地产销售 水银体温计碎了要通风多久 水银体温计碎了需通风多久? 梦见死去的姥姥是什么意思? 稳岗情况怎么填写上年度稳定岗位基本情况怎么填 生日红包金额多少最好呢? 王侨角色介绍 北京 顺义的 在菜百上班 她叫王侨 她老公叫什么呀 有人知道吗 艾朗是王侨的老婆 “世路荣枯真莫问”的出处是哪里 9大新趋势洞察2022年酒业确定性 千牛如何导出淘宝店里的商品?步骤详解 a ticket to和a ticket for的区别 cbd合法吗 关于CBD产品的挑选和购买系列科普! 关于北京万达国际电影城CBD的一些问题!! 写意葡萄技法目录 写意葡萄技法内容简介 关于CBD? 写意葡萄技法作者简介 临沂烂尾楼有哪些 想在临沂购房,靠河近的,有房产证的,不知哪合适? 优酷会员账号可以同时登录多少台设备? 经常看恐怖片对人的性格有帮助吗? 看恐怖片对人有什么好处跟坏处? 一根铁丝长15米,第一次用去全长的1/3,第二次用去余下的2/5,第一次用... 恐怖片看多了,对心理有害吗? 喜欢看恐怖片对一个人成长有好处吗? 绳子长24米,第一次剪去1/3,第二次剪去3/4米.还剩下多少米? 暴风影音没有图像 【游戏推荐】重装机兵xeno重生 重装机兵xe?