蚂蚁大战蜜蜂

Ants vs. Somebees

蜜蜂来袭!
通过继承蚂蚁类
创造更强的战士。

介绍

要获得满分:

  • 在下列日期前提交完成的第 1、2 阶段: 7 月 23 日(星期四).
  • 在下列日期前提交全部阶段: 7 月 28 日(星期二).

请按顺序解决这些问题,因为后面的问题会用到前面问题的知识。

你可以和你的小伙伴一起完成这个项目。

在下列日期前提交完整项目可获得 1 分奖励: 7 月 27 日(星期一).

在这个项目中,你将创建一个名为 Ants Vs. SomeBees (蚂蚁大战蜜蜂) 的 塔防 游戏“蚂蚁大战蜜蜂”。你要用最勇敢的蚂蚁充实蚁群,保护蚁后免受邪恶蜜蜂入侵。不断向蜜蜂投掷叶子就能击败它们;若干扰得不够,蚁后就会屈服于蜂群。本游戏灵感来自 PopCap Games 的 植物大战僵尸.

本项目将运用面向对象编程的思想,相关知识可以参考 Composing Programs 的 第 2.5 章 。本项目还涉及理解、扩展和测试大型程序。

下载入门文件

The ants.zip 压缩包包含多个文件,但你只需要修改 ants.py.

  • ants.py: 蚂蚁大战 SomeBees 的游戏逻辑
  • ants_plans.py: 每个难度级别的详细信息
  • ucb.py:实用函数
  • gui.py: :蚂蚁大战蜜蜂的图形用户界面(GUI)。
  • ok: 自动评分器
  • proj3.okok 配置文件
  • tests:自动评分器使用的测试目录 ok
  • libs:程序使用的库目录 gui.py
  • static:GUI 使用的图片和文件目录 gui.py
  • templates:GUI 使用的 HTML 模板目录 gui.py

说明

项目共 20 分。

在指定提前截止时间前提交完整项目可获得 1 分额外奖励。 7 月 27 日(星期一).

对于需要你完成的函数,我们可能会提供一些初始代码。你可以选择使用或者完全重写,也可以根据需要添加新的函数。

  • ants.py

完成本项目不需要修改或提交其他文件。提交项目时,请 把要求的文件提交到对应的 Gradescope 作业入口。

完成本项目时,不得使用人工智能工具提供帮助,也不得参考网上找到的答案。

对于要求你补全的函数,我们可能已经提供了一些初始代码。如果你不想沿用,可以删除后从头实现;也可以根据需要添加新的函数定义。

但是,请不要修改其他函数,也不要编辑上面未列出的文件。否则代码可能无法通过自动评分器测试。也请不要更改任何函数签名(包括名称、参数顺序或参数数量)。

在整个项目过程中,你应不断检查代码是否正确。经常测试是个好习惯,因为这样更容易定位问题;不过也不要测试得 too 过于频繁,要给自己留出认真思考问题的时间。

我们提供了一个名为 autograder called ok 的自动评分器,帮助你测试代码并记录进度。第一次运行时,系统会要求你 在浏览器中登录 Ok 账户。请按提示完成登录。此后每次运行 ok时,它都会把你的作业和进度备份到服务器。

的主要用途是 ok 测试你的实现。

如果想以交互方式测试代码,可以运行

 python3 ok -q [question number] -i 
并填入对应的问题编号 (例如 01)。 这会运行该问题的测试,直到你遇到第一个失败的测试为止, 之后你可以交互式地测试你所编写的函数。

你还可以使用 Ok 的调试输出功能:在 print 语句前加上“DEBUG:”。例如,要查看变量 x的值,可以这样写:

 print(f"DEBUG: x is {x}") 
这会在你的终端生成输出,而不会因为多余的输出导致OK测试失败。

游戏

蚂蚁大战蜜蜂是一个回合制游戏。每个回合,新的蜜蜂可能会入侵蚂蚁的领地。 接着,玩家放置新的蚂蚁来保卫家园。 最后,所有昆虫 (包括蚂蚁和蜜蜂) 都会执行各自的行动。蜜蜂要么试图向隧道的尽头移动,要么蛰伤阻挡其前进的蚂蚁。蚂蚁根据其类型执行不同的动作,例如收集更多食物或向蜜蜂扔树叶。当蜜蜂到达隧道尽头 (蚂蚁失败), 蜜蜂摧毁了 QueenAnt (如果存在), 蚂蚁就会失败,或者整个蜜蜂舰队被击溃(蚂蚁获胜)时,游戏结束。

核心概念

The Colony。游戏在其中进行。蚁群由若干 Place组成,它们首尾相连形成蜜蜂行进的隧道。蚁群还拥有一定食物,可消耗食物在隧道中放置蚂蚁。

地点 (Places)。每个地点都连接着另一个地点,从而形成隧道。玩家可以在每个地点放置一只蚂蚁。但是,一个地点可以有很多蜜蜂。

蜂巢 (The Hive)。这是蜜蜂起源的地方。蜜蜂离开蜂巢进入蚂蚁群。

蚂蚁 (Ants)。玩家可以通过在屏幕上方选择可用的蚂蚁类型,来将蚂蚁放置在群落中。每种蚂蚁都有不同的行动方式,并且需要消耗不同数量的食物才能被放置。两种最基本的蚂蚁类型是 HarvesterAnt,它在每个回合中为群落增加一种食物,还有 ThrowerAnt,它会在每个回合向蜜蜂投掷叶子。 之后你还会实现更多种类的蚂蚁!

蜜蜂 (Bees)。每个回合,如果蜜蜂的路上没有蚂蚁,蜜蜂就会前进到隧道中的下一个地点,否则它会蛰伤阻挡其前进的蚂蚁。当至少有一只蜜蜂到达隧道尽头时,蜜蜂获胜。除了普通的橙色蜜蜂,还有能造成双倍伤害的黄色黄蜂,以及非常难对付的绿色Boss蜜蜂。

核心类

以上描述的每个概念都有一个对应的类来实现其逻辑。

  • GameState: 代表蚁群和关于游戏的一些状态信息,包括有多少食物可用,经过了多久, AntHomeBase 在哪里,以及游戏中所有的 Place。它管理游戏中的蚂蚁和蜜蜂。
  • Place: 代表一个可以容纳昆虫的地点。一个地点最多只能容纳一只 Ant ,但可以容纳多只 Bee集中在同一个位置。 Place 对象有一个向左的 exit 和一个向右的 entrance (同样也是位置)连接。蜜蜂通过逐个移动到下一 Place's exit 来穿过隧道。 Place.
  • Hive: 代表 Bee的起始位置(位于隧道的右侧)。
  • AntHomeBase: 代表 Ant所防御的基地(位于隧道的左侧)。如果 Bee到达这里,它们就赢了:(
  • Insect:所有昆虫的基类 AntBee的基类。每种昆虫都有一个 health 属性,代表其剩余的生命值,以及一个 place 属性,代表其当前所在的 Place 表示当前所在位置。每回合,每只存活的昆虫都会执行动作。 Insect 都会执行其 action.
  • Ant: 代表蚂蚁。每个 Ant 子类都有特殊的属性或特殊的 action ,使其有别于其他 Ant 类型。例如, HarvesterAnt 为蚁群获取食物,而 ThrowerAnt 攻击 Bee。每种蚂蚁类型还有一个 food_cost 属性,指示部署该类型的一只蚂蚁需要多少食物。
  • Bee:表示蜜蜂。每回合,蜜蜂会移动到当前位置的出口 exit Place if the Place is not blocked ;若被蚂蚁阻挡,则蜇伤占据当前位置的蚂蚁。 Place.

游戏布局

下面是 GameState 的可视化图。

为了帮助理解各个类之间的关系, here 展示了所有类及其继承关系图。

第 1 阶段:基本玩法

第 1 阶段要补全基本玩法,使游戏能够使用两种基础蚂蚁运行。 Ants: the HarvesterAnt and the ThrowerAnt.

Problem 0

阅读 entire ants.py 文件后,运行下面的命令回答一组概念题: ok command:

python3 ok -q 00 -u

如果答题遇到困难,可以重新阅读 ants.py ,或在 Ed 上提问。

关于解锁测试:完成解锁测试后若想回顾问题,可以前往 tests 文件夹(位于 ants 文件夹内)。例如,解锁问题 0 后,可在下面的位置查看: tests/00.py.

问题 1

甲部分:目前,放置任何类型的 Ant都不需要消耗食物,因此游戏中没有任何挑战。基类 Antfood_cost 为零。请根据下表中的“食物成本”列,重写 HarvesterAntThrowerAnt 的这个类属性。

食物成本 初始生命值

HarvesterAnt
2 1

ThrowerAnt
3 1

Part B:既然放置 Ant 需要消耗食物,就必须能获得更多食物。请实现 HarvesterAnt class. A HarvesterAnt ;它是一种 Ant ,每回合增加蚁群食物。 gamestate.food by 1 as its action.

编写代码前,请先解锁测试,以确认自己正确理解了题意:

python3 ok -q 01 -u

解锁完成后开始实现答案。你可以用以下命令检查实现是否正确:

python3 ok -q 01

问题 2

本题需要补全 Place.__init__ ,加入入口跟踪。当前 Place 只记录自身的出口 exit。我们希望 Place 还能记录入口。 entrance as well. A Place 只需跟踪一个入口。 entrance跟踪入口能帮助 Ant 查看隧道前方有哪些 Bee

不过,直接把入口传给 Place 构造器会有问题:创建位置前似乎必须同时拥有入口和出口 exit and the entrance ,形成先有鸡还是先有蛋的 Place! (It's a 鸡生蛋问题 。为避开这个问题,我们改用下面的方式记录入口。 Place.__init__ 应使用以下逻辑:

  • 新创建的 Place 对象的 entrance set to None.
  • 如果 Place 对象存在 exit,那么该 exit's entrance 属性应该被设置为该 Place.

Hint: 请记住,在调用 __init__ 方法时,第一个参数 self会绑定到新创建的对象。

Hint: 如果感到困惑,可以尝试将两个 Place对象并排画出来。在 GUI 中,一个 place 的 entrance 在其右侧,而 exit 在其左侧。

Hint: 请记住, Place并不存储在列表中,因此不能通过索引访问。也就是说,你 can't 不能写出类似 colony[index + 1] 的代码来访问相邻位置。 Place. How can 应怎样从一个位置移动到另一个位置?

编写代码前,请先解锁测试,以确认自己正确理解了题意:

python3 ok -q 02 -u

解锁完成后开始实现答案。你可以用以下命令检查实现是否正确:

python3 ok -q 02

问题 3

为了让 ThrowerAnt 投掷叶子,它必须知道该攻击哪只 Bee 。已经提供的 nearest_bee 方法位于 ThrowerAnt 类中,目前只允许攻击 ThrowerAnts to hit Bee同一位置中的蜜蜂 Place。你的任务是修改它,使投掷蚂蚁能攻击 ThrowerAnt will throw_at the nearest Bee 前方的蜜蜂 ,但不能攻击仍在蜂巢中的蜜蜂。 Hive. 其中也包括 Bee与投掷蚂蚁位于同一位置的蜜蜂。 Place as a ThrowerAnt

Hint: All Places have an is_hive 属性;当该位置是蜂巢时,此属性设为 TrueHiveFalse otherwise.

修改 nearest_bee 函数,使其从包含蜜蜂的最近的地点返回一个随机的 Bee 从最近的、包含蜜蜂的 Place 位置中选择目标 Bee。实现应遵循以下逻辑:

  • 从投掷蚂蚁所在位置开始。 ThrowerAnt's current Place.
  • 如果 Place 如果当前 Place 包含一只或多只 Bee, Bee就随机返回一只;否则检查前方下一个 Place (存储为当前 Place 的 entrance)。 Place
  • 重复这个过程,直到找到并返回一只 Bee 。若没有可攻击的 Bee ,返回 None。 None.
  • Ensure that Bees in the Hive 绝不能返回蜂巢中的蜜蜂。 nearest_bee.

Hint: The random_bee 中提供的函数 ants.py 会从列表中随机返回一只 Bee ;若列表为空则返回 None Bees or None

Hint: 提醒一下:如果某个 Bee中没有 Bee, Place,那么该 bees 实例的相应列表属性 Place 会是空列表。

Hint: 难以想象测试情况?试着在纸上画出来!可以参考页面中的示意图。 游戏布局 中提供的示例图展示了本题的第一个测试用例。

编写代码前,请先解锁测试,以确认自己正确理解了题意:

python3 ok -q 03 -u

解锁完成后开始实现答案。你可以用以下命令检查实现是否正确:

python3 ok -q 03

玩游戏

实现后, nearest_bee(激光蚂蚁),它是 ThrowerAnt 应当能够攻击 throw_at a Bee 前方且不在蜂巢中的 Bee。 Hive.

现在你可以尝试运行你编写的代码了。要启动图形游戏,请运行:

python3 gui.py

启动图形版后,游戏通常可在 http://127.0.0.1:31415/ 打开。

你可以通过运行 python3 gui.py --help.

usage: gui.py [-h] [-d DIFFICULTY] [-w] [--food FOOD]

Play Ants vs. SomeBees

optional arguments:
  -h, --help     show this help message and exit
  -d DIFFICULTY  sets difficulty of game (test/easy/normal/hard/extra-hard)
  -w, --water    loads a full layout with water
  --food FOOD    number of food to start with when testing

你可以刷新网页以重新启动游戏,但如果你更改了代码,则需要终止 gui.py 并再次运行它。要终止 gui.py,你可以在终端上点击 Ctrl + C

请勿同时打开多个 Ants GUI 页面,否则可能会导致程序出错。

第二阶段:更多蚂蚁!

现在已经用两种基础蚂蚁实现了基本玩法,接下来增加更丰富的攻击方式。 Ant从本题开始,你会实现多种采用不同攻击策略的 Ant蚂蚁。

在这些部分中实现每个 Ant 子类之后,你需要将其 implemented 类属性设置为 True ,以便该类型的蚂蚁将显示在 GUI 中。 欢迎尝试使用新添加的蚂蚁进行游戏,以测试其功能!

从现在开始,对于所有后续的蚂蚁,尝试 python3 gui.py 以在多隧道布局中与完整的蜜蜂群对战,如果你想挑战更高的难度,可以尝试 -d hard-d extra-hard 模式!如果蜜蜂数量过多难以战胜,不妨尝试创建一些新的蚂蚁类型。

问题 4

A ThrowerAnt 对蜜蜂威胁很大,但食物成本较高。 food_cost本题将实现两个较便宜但射程受限的子类: ThrowerAnt

  • The LongThrower can only throw_at a Bee 只能攻击沿入口方向至少经过 5 个位置后找到的 Bee entrance 。换句话说,它不能攻击与自己同处或前方最初 4 个位置中的 Bee与投掷蚂蚁位于同一位置的蜜蜂。 Place Place。如果有两只 Bee,一只太近、另一只在射程内, Bee LongThrower 应只攻击更远、位于射程内的 LongThrower Bee,而不是尝试攻击较近的那只。 Bee.
  • The ShortThrower can only throw_at a Bee 只能攻击沿入口方向最多经过 3 个位置后找到的 Bee entrance 。换句话说,它不能攻击前方超过 3 个位置的 Bee Place

这两种专门化投掷蚂蚁都不能攻击恰好相距 4 个位置的 Bee。 throw_at a Bee Places away.

食物成本 初始生命值

ShortThrower
2 1

LongThrower
2 1

实现这些新投掷蚂蚁时, ShortThrowerLongThrower 类应继承基类中的 nearest_bee 方法。 ThrowerAnt 类中进行更改。

接着修改 nearest_bee method in ThrowerAnt ,使它参考射程上下界属性 lower_boundupper_bound ,并且只返回处在给定范围内的 Bee from a Place if that Place 。之所以需要这样做,是因为不同投掷蚂蚁的射程由 Place ShortThrowerLongThrower ants can throw_at a Bee 分别限制。 upper_bound and a lower_bound

请在基类中为这些 lower_boundupper_bound 属性设置合适的默认值, ThrowerAnt 以保持普通 ThrowerAnt 的行为不变。然后实现 ThrowerAnt 等子类,设置恰当的射程。 LongThrowerShortThrower

不应该ThrowerAnt, ShortThrowerLongThrower.

重要: 请确保您的类属性名称为 upper_boundlower_bound 。 测试会直接引用这些属性名,如果使用了其他名称,测试将会失败。

Hint: float('inf') 会返回一个用浮点数表示的正无穷值,可以与其他数值进行比较。

Hint: lower_boundupper_bound 应该定义一个闭区间范围。

别忘了为新类设置 implemented 类属性。 LongThrowerShortThrower to True.

编写代码前,请先解锁测试,以确认自己正确理解了题意:

python3 ok -q 04 -u

编写代码后,测试您的实现(重新运行 03 的测试以确保它们仍然有效):

python3 ok -q 03
python3 ok -q 04

👩🏽‍💻👨🏿‍💻 结对编程? 记得轮换驾驶者和导航者角色:驾驶者操作键盘,导航者观察、提问并提出建议。

问题 5

实现 FireAnt;它受到伤害时会反击。具体来说,若受到 damage_taken 点伤害,就对同一位置中的所有 Bee 造成等量 damage_taken to all Bee伤害(称为 反射伤害)。若它阵亡,还会按照自身 damage 属性对同一位置的所有 Bee 造成额外伤害。 Bee damage 类中该属性的默认值见代码。 FireAnt class is 3.

要实现此行为,请重写 FireAnt's reduce_health 方法。重写的方法应调用从超类继承的 reduce_health 方法(Ant) 又从其超类继承该方法) Insect ,以降低当前生命值。 FireAnt instance's health调用该方法会把昆虫的 inherited reduce_health method on a FireAnt health 减少给定数值 damage_taken ,并在生命值降至 0 或以下时将其从所在位置移除。 health

Hint: Do 不应该 call self.reduce_health,否则会陷入递归死循环。(能看出原因吗?)

不过,你的方法还必须包含 反射伤害 logic:

  • 确定总 反射伤害 伤害量:先取 FireAnt 受到的 damage_taken 伤害,若它的 FireAnt降至 0 或以下,再加上自身额外伤害属性。 damage health
  • For each Bee 对所在位置中的每只 Bee 调用相应方法,施加总伤害。 反射伤害 reduce_health 方法。

重要: 请记住,任何昆虫 Ant 生命值耗尽时都会从所在位置移除,因此要特别注意逻辑顺序。 Place reduce_health.

食物成本 初始生命值

FireAnt
5 3

重要:伤害 Bee 可能导致它从位置中移除。若遍历列表的同时改变列表内容,可能跳过元素。 可能无法访问所有元素。这可以通过复制列表来防止。你可以使用列表切片,或者使用内置的 list 可使用复制列表的方法,确保遍历用的原列表视图不受影响。

>>> s = [1,2,3,4]
>>> s[:]
[1, 2, 3, 4]
>>> list(s)
[1, 2, 3, 4]
>>> (s[:] is not s) and (list(s) is not s)
True

完成 FireAnt的实现后,将其类属性 implemented ,并使用指定值。 True.

Note: 即使你覆盖了超类的 reduce_health 函数 (Ant.reduce_health),你仍然可以通过调用它在你的实现中使用此方法。

编写代码前,请先解锁测试,以确认自己正确理解了题意:

python3 ok -q 05 -u

解锁完成后开始实现答案。你可以用以下命令检查实现是否正确:

python3 ok -q 05

你也可以通过玩几局游戏来测试你的程序! FireAnt 应该在被蜇时摧毁所有位于同一地点的 Bee。 要启动一个有十个食物的游戏(便于测试):

python3 gui.py --food 10

问题 6

我们将实现一种防御型蚂蚁来保护基地: WallAnt。它每回合不执行动作,但拥有较高的 WallAnt ,可以阻挡蜜蜂。 health value.

食物成本 初始生命值

WallAnt
4 4

与此前不同,本题没有提供类声明,请从头实现 WallAnt 类,并设置类属性 name ,并使用指定值。 'Wall' (供图形界面使用)以及 implemented ,并使用指定值。 True (使其能在游戏中使用)。

Hint: 务必同时实现相应方法,使该蚂蚁可正常行动。 __init__ WallAnt 具有适当的初始生命值 health!

编写代码前,请先解锁测试,以确认自己正确理解了题意:

python3 ok -q 06 -u

解锁完成后开始实现答案。你可以用以下命令检查实现是否正确:

python3 ok -q 06

问题 7

实现 HungryAnt,它会随机选择同一位置中的一只 Bee from its Place ,并造成等于该 Bee 当前生命值的伤害 Bee ,将其一口吃掉。 Bee吃掉 Bee 后, Bee(激光蚂蚁),它是 HungryAnt 必须咀嚼 3 回合才能再次进食。咀嚼期间不能伤害任何 HungryAntBee如果所在位置没有可吃的 Bee, Bee 将什么也不做。 HungryAnt

本题没有提供类头,请实现该类。 HungryAnt 类,并设置类属性 name ,并使用指定值。 'Hungry' (供图形界面使用)以及 implemented ,并使用指定值。 True (使其能在游戏中使用)。

Hint: 当一只 Bee 当 Bee 被吃掉时,应把其 health 减少当前全部生命值。 health.

食物成本 初始生命值

HungryAnt
4 1

Give HungryAnt a chew_cooldown class 设置类属性,表示咀嚼一只 Bee 所需回合数(设为 3)。 HungryAnt 另外,为每个实例设置属性 HungryAnt an instance attribute cooldown ,记录还需咀嚼多少回合,初始为 0,因为开局尚未进食。也可把它理解为 cooldown 距离下一次可以吃 Bee 还要等待的回合数。 HungryAnt Bee.

实现 action 类的方法应按下面步骤实现 HungryAnt:先检查是否正在咀嚼;若是,减少剩余咀嚼回合。 cooldown否则,随机吃掉一只 Bee, Bee in its Place 将其生命值降为 0。务必在吃完后设置剩余咀嚼回合。 Bee cooldown when a Bee is eaten!

Hint: 除了指定的新增属性和方法外,不要改动其他逻辑。 action 方法,还要确保实现 __init__ 方法,以便定义实例变量,并确保 HungryAnt 具有适当的初始生命值 health!

编写代码前,请先解锁测试,以确认自己正确理解了题意:

python3 ok -q 07 -u

解锁完成后开始实现答案。你可以用以下命令检查实现是否正确:

python3 ok -q 07

👩🏽‍💻👨🏿‍💻 结对编程? 建议切换角色,以便双方都能从不同角色中获益。

问题 8

现在,我们的蚂蚁非常脆弱。我们想提供一种方法来帮助它们在蜜蜂的猛攻中存活更长时间。输入 ProtectorAnt.

食物成本 初始生命值

ProtectorAnt
4 2

为了更容易实现后续容器蚂蚁,先理解本题的继承关系。 ProtectorAnt,我们将把这个问题分解为 3 个子部分。在每个部分中,我们都将在 ContainerAnt 类、 Ant 类或 ProtectorAnt 类中进行更改。

Note: 问题 8 分成三个小问。建议每个小问都先完成解锁测试再写代码。每小问 1 分,共 3 分。

问题 8a

首先定义并使用一个 ContainerAnt 父类,后续的容器蚂蚁会继承它。 ProtectorAnt.

A ProtectorAnt 与普通蚂蚁不同,因为它是容器。 ContainerAnt;它能容纳并保护另一只蚂蚁,两者共同占据一个位置。 Place. When a Bee 蜇伤某个有两只蚂蚁的位置时, Place 若一只蚂蚁容纳另一只,只有容器受伤。容器中的蚂蚁仍能执行原动作;容器阵亡后,被容纳的蚂蚁留在原位置,此后可以受到伤害。

Each ContainerAnt 具有实例属性 ant_contained ,用于保存其容纳的蚂蚁。该属性 ant_contained初始为 None ,表示尚未容纳蚂蚁。实现 store_ant 方法,把实例属性设置为传入的 ContainerAnt's ant_contained ant 参数。然后实现 ContainerAnt's action 方法,确保容器当前若容纳了蚂蚁, ContainerAnt ant_contained被容纳蚂蚁的动作也会执行。

此外,为使容器与其内部蚂蚁能同时占据同一个 Place (每个位置最多两只蚂蚁,且必须恰好一只是容器), Place可以实现专门的判断方法。 can_contain 方法。

已经有一个 Ant.can_contain 方法,但它始终返回 False. Override the method can_contain in ContainerAnt 方法,使其接受一个蚂蚁 other 作为参数,并在满足以下条件时返回 True if:

  • ContainerAnt 尚未包含另一只蚂蚁。
  • 另一只蚂蚁不是容器。

Hint: 每个 is_container 具有的容器标记属性 Ant 可用来检查某只 Ant 是否为容器。

编写代码前,请先解锁测试,以确认自己正确理解了题意:

python3 ok -q 08a -u

解锁完成后开始实现答案。你可以用以下命令检查实现是否正确:

python3 ok -q 08a

问题 8b

接下来修改放置昆虫的方法 Ant 类中进行更改。

修改 Ant.add_to ,按以下规则允许容器和被容纳蚂蚁共享位置:

  • 如果 Ant 若原本占据位置的是容器, can_contain the Ant 且新加入的是可被容纳的蚂蚁,则两者共享位置,原容器 Ant Ant 容纳新加入者。 Ant
  • 如果 Ant being added can_contain the Ant 若原位置是普通蚂蚁,而新加入的是容器,则两者共享位置, Ant Ant 新容器容纳原蚂蚁。 Ant.
  • If neither Ant can_contain 若两者都不能容纳对方,抛出与起始代码相同的异常。 AssertionError

重要: 若某个 Ant中有两只 Ant, Place, the ant 该 Place 的 ant 属性 Place引用容器蚂蚁,且容器应容纳非容器蚂蚁。

Hint: 你也应利用类型检查;如果 VS Code Pylance 对此处给出特定警告,可以忽略,因为代码实际能够运行。 can_contain 方法,并避免重复代码。

Note: 如果您通过 VSCode Pylance 扩展收到 Ant.add_to 在这种情况下 警告不准确)。

编写代码前,请先解锁测试,以确认自己正确理解了题意:

python3 ok -q 08b -u

解锁完成后开始实现答案。你可以用以下命令检查实现是否正确:

python3 ok -q 08b

问题 8c

最后实现容器子类。 ProtectorAnt 类中进行更改。

添加一个 ProtectorAnt.__init__ 其构造方法设置初始生命值。 ProtectorAnt无需创建额外的新接口。 action 方法,因为 ProtectorAnt 类会从 ContainerAnt 类继承该方法。另请注意, ProtectorAnt 不会造成任何伤害。

完成 ProtectorAnt的实现后,将其类属性 implemented ,并使用指定值。 True.

编写代码前,请先解锁测试,以确认自己正确理解了题意:

python3 ok -q 08c -u

解锁完成后开始实现答案。你可以用以下命令检查实现是否正确:

python3 ok -q 08c

问题 9

The ProtectorAnt 提供强大防御,但最好的防守是进攻。 TankAnt is a ContainerAnt 既保护同位置中的蚂蚁,又每回合对所有 Bee 造成 1 点伤害。 Bees in its Place 与所有容器蚂蚁一样,它也 ContainerAnt(激光蚂蚁),它是 TankAnt 允许内部蚂蚁每回合执行动作。

食物成本 初始生命值

TankAnt
6 2

本题没有提供类头,请实现该类。 TankAnt 类,并设置类属性 name ,并使用指定值。 'Tank' (供图形界面使用)以及 implemented ,并使用指定值。 True (使其能在游戏中使用)。

您应该不需要修改 TankAnt 类以外的任何代码。如果需要修改其他地方的代码,请尝试修改之前问题的代码,使其不仅适用于 ProtectorAntTankAnt 对象,而应面向一般的 ContainerAnt蚂蚁。

Hint: 只需重写父类中的指定方法。 TankAnt __init__action.

Hint: Like with FireAnt,伤害 Bee 可能导致它从当前位置移除,遍历时要小心。

编写代码前,请先解锁测试,以确认自己正确理解了题意:

python3 ok -q 09 -u

解锁完成后开始实现答案。你可以用以下命令检查实现是否正确:

python3 ok -q 09

检查点提交

请确认已完成第 1、2 阶段的所有题目:

python3 ok --score

然后,在检查点截止日期之前,将 ants.py to the Ants 检查点 上的 Gradescope ,并在检查点截止时间前提交。若要回顾 Gradescope 提交流程,请参阅 Lab 00.

可以在 Gradescope 提交页点击 Edit Group 并输入搭档邮箱来添加搭档。只需一名搭档提交。

运行 ok 命令时仍会看到部分测试被锁定,因为项目尚未全部完成。只要完成此前所有题目,就能获得检查点满分。

恭喜!你已完成项目第 1、2 阶段!

第 3 阶段:水与力量

在最后阶段,您将通过引入一种新型地点和能够占据该地点的新蚂蚁来为游戏添加最后一击。其中一只蚂蚁是所有蚂蚁中最重要的:蚁后!

问题 10

给蚁群加入水域吧!目前只有两类位置,接下来创建新的 Hive and a basic PlacePlace called Water.

Only an Insect 只有防水昆虫才能放入 Water。 Water为了判断某个 Insect 是否防水,请在基类中添加新的类属性,默认设为 Insect class named is_waterproofFalse. Since Bee由于 Bee 会飞,它们的防水属性 is_waterproof 应重写为 True。 True

现在实现 Water 的添加逻辑。 add_insect method for Water首先无论是否防水都先添加昆虫;然后若 Insect to the Place 不防水,就把它的 Insect 生命值降到 0。 Insect's health to 0. 不要重复程序其他位置已有的代码。 请调用已经定义的方法。

编写代码前,请先解锁测试,以确认自己正确理解了题意:

python3 ok -q 10 -u

解锁完成后开始实现答案。你可以用以下命令检查实现是否正确:

python3 ok -q 10

完成此问题后,不妨试试加了水的游戏!要访问包含水的 wet_layout,请在启动游戏时添加 --water 选项(或简写为 -w (简称蚁后)在游戏开始时创建。

python3 gui.py --water

👩🏽‍💻👨🏿‍💻 结对编程? 记得轮换驾驶者和导航者角色:驾驶者操作键盘,导航者观察、提问并提出建议。

问题 11

目前还没有可以放置在 Water上的蚂蚁。实现 ScubaThrower,它是 ThrowerAnt 的一个子类,这种蚂蚁造价更高,并且具有防水性, 除此之外,它和基类蚂蚁完全一样. A ScubaThrower 不应在放置在 Water.

食物成本 初始生命值

ScubaThrower
6 1

本题没有提供类头,请实现该类。 ScubaThrower 类,并设置类属性 name ,并使用指定值。 'Scuba' (这样设置才能在图形界面中正常显示),并记住将类属性 implemented ,并使用指定值。 True (使其能在游戏中使用)。

编写代码前,请先解锁测试,以确认自己正确理解了题意:

python3 ok -q 11 -u

解锁完成后开始实现答案。你可以用以下命令检查实现是否正确:

python3 ok -q 11

Problem 12

最后,实现 QueenAnt。女王是一个 ThrowerAnt ,她通过自身的勇敢激励她的同伴蚂蚁。除了标准的 ThrowerAnt 动作外, QueenAnt 每次行动都会让同一隧道中她身后所有蚂蚁的伤害翻倍。但一只蚂蚁的伤害一旦翻倍,就 这个伤害值 不能再次翻倍。想办法记录是否已经翻倍(提示:使用实例属性)。

Note: The 反射伤害 of a FireAnt 的反射伤害不应加倍,只有当其生命值降至 0 时造成的额外伤害才应加倍。

食物成本 初始生命值

QueenAnt
7 1

能力越大,责任越大。如果 QueenAnt 生命值降到 0,蚂蚁立即失败。需要重写相应方法 Insect.reduce_health in QueenAnt and call ants_lose() ,向模拟器发出游戏结束信号。(任何 Bee 到达隧道末端时也仍会失败。)

Hint: 要让身后蚂蚁伤害翻倍,可以补全 double 类中定义的方法,然后从蚁后的行动方法调用它。 Ant QueenAnt 类中进行更改。

Hint: 在使蚂蚁的伤害加倍时,请记住,一个 Place,并处理容器蚂蚁内部的蚂蚁。

Hint: 请记住, QueenAnt's reduce_health 方法除了自身行为,还需要调用超类的 ants_lose() 方法。 reduce_health 怎样在不重复代码的情况下保留超类方法的全部行为?

Hint: 要寻找蚁后身后的每只蚂蚁,可以从蚁后所在 Place 开始,反复沿出口/入口方向移动到前一个 QueenAnt 位置。 place.exit Place's exit. The exit of a Place 隧道末端的下一个位置为空。 None.

编写代码前,请先解锁测试,以确认自己正确理解了题意:

python3 ok -q 12 -u

解锁完成后开始实现答案。你可以用以下命令检查实现是否正确:

python3 ok -q 12

项目提交

运行 ok 来检查所有题目,确保所有测试都已解锁并通过:

python3 ok

您还可以检查项目每个部分的得分:

python3 ok --score

满意后,通过将 ants.py to the 蚂蚁 (Ants) 上的 Gradescope. 若要回顾 Gradescope 提交流程,请参阅 Lab 00.

可以在 Gradescope 提交页点击 Edit Group 并输入搭档邮箱来添加搭档。只需一名搭档提交。

项目到此完成! 如果还没玩过,现在去试试游戏吧!

python3 gui.py [-h] [-d DIFFICULTY] [-w] [--food FOOD]
  • -h = help
  • -d = 难度(easy/normal/hard/extra-hard)
  • -w = water
  • -food = 初始食物数量(默认 2)

额外挑战(选做)

注意:这些题目 optional不计分.

办公时间和项目辅导时段,助教将优先解答必做题。只有在 队列 为空时,才会解答本题。

实现两种最终投掷蚂蚁:它们不造成伤害,而是给目标 Bee 施加持续若干回合的临时状态。 action method of a Bee throw_at状态结束后不再生效。

额外挑战 1(0 分)

我们将实现新蚂蚁 SlowThrower,它继承自 ThrowerAnt。 ThrowerAnt.

SlowThrower 会向 Bee 投掷黏稠糖浆,使其减速 5 回合。减速时,只有当当前时间为偶数才执行正常动作,其他回合不移动也不蜇人。 gamestate.time 若已减速的 Bee 再次中招,会从第二次命中时起重新减速 5 回合。 most recent 也就是说,第一次命中后经过 2 回合再命中,第二次之后再减速 5 回合, second 总共减速 7 回合,而不是 10 回合。

食物成本 初始生命值

SlowThrower
6 1

要完成该类实现,需要设置合适的类属性并实现指定方法。 SlowThrower throw_at method in SlowThrower.

重要限制: You may 不应该 本题不得修改该类以外的任何代码。 SlowThrower 也就是说,不得直接修改 Bee 的 不应该 modify the Bee.action 方法。测试会检查这一点。

提示:查看 SlowThrower的父类, ThrowerAnt. ThrowerAnt's action 方法会调用 throw_at,这正是应在子类中重写的部分。 SlowThrower传给 target 函数参数的是什么,为什么? SlowThrower's throw_at 其中的 target.action 指向什么?

实现提示: Assign target.action 替换为一个新函数,由它按条件调用原动作。 Bee.action可以用实例属性记录还剩多少减速回合;减速结束后,应正常调用原动作。 Bee.action every turn again.

编写代码前,请先解锁测试,以确认自己正确理解了题意:

python3 ok -q EC1 -u

可以运行已提供的测试,但它们并不全面:

python3 ok -q EC1

务必自行测试。代码应能对同一目标叠加多个状态;每个新状态都作用于 Bee 当前的动作方法(可能已被其他状态修改)。

额外挑战 2(0 分)

必须先正确实现额外挑战 1(SlowThrower)才能通过本题测试。本题实现新蚂蚁 ScaryThrower,它继承自 ThrowerAnt。 ThrowerAnt.

ScaryThrower 会恐吓附近 Bee,使其后退而不是前进。请注意:

  1. 如果 Bee 已紧邻 Hive、无法再后退,就不移动。判断是否紧邻 Hive 时,可利用 is_hive 的实例属性。 Place useful.
  2. Bee 会保持恐惧,直到尝试后退 twice次,因此后退效果持续两个回合。
  3. Bee 在减速导致无法行动的回合不能尝试后退。 slowedgamestate.time is odd
  4. 一只 Bee 一旦受过一次恐吓,以后再也不能被恐吓。
食物成本 初始生命值

ScaryThrower
6 1

要完成实现,需要编写施加恐惧状态的方法,可能还需修改 Bee 的其他方法(例如 action)。 ScaryThrower scare method in Bee Bee

编写代码前,请先解锁测试,以确认自己正确理解了题意:

python3 ok -q EC2 -u

python3 ok -q EC2

务必自行测试。代码应能对同一目标叠加多个状态;每个新状态都作用于 Bee 当前的动作方法(可能已被其他状态修改)。

额外挑战 3(0 分)

实现 NinjaAnt,它伤害所有经过的 Bee, Bee但永远不会被蜇。

食物成本 初始生命值

NinjaAnt
5 1

A NinjaAnt 不会阻挡飞过的 Bee。 Bee 要实现该行为,先在基类中加入新的类属性 Ant ,然后在 NinjaAnt 中重写其值。 blocks_pathTrue blocks_path to False in the NinjaAnt 类中进行更改。

接着修改 Bee 的阻挡判断: Bee's method blocked to return False 若当前位置没有 Ant,或存在不阻挡道路的 Ant, Ant in the Bee's place Bee 就继续飞过。 Ant, but its blocks_path 应重写为 True。 False. Now Bee NinjaAnts.

最后,让 NinjaAnt 伤害飞过的 Bee。 NinjaAnt damage all Bee实现其 action, action method in NinjaAnt 按照 damage 属性减少同一位置所有 Bee 的生命值。 Bee同一位置中的蜜蜂 place as the NinjaAnt by its damage 与 FireAnt 类似, FireAnt必须遍历一个可能在过程中变化的 Bee 列表。

提示:难以想象测试情况?试着在纸上画出来,并参考页面示例。 游戏布局 for help.

编写代码前,请先解锁测试,以确认自己正确理解了题意:

python3 ok -q EC3 -u

python3 ok -q EC3

试试只用 HarvesterAntNinjaAnt.

额外挑战 4(0 分)

我们秘密研发这种蚂蚁已久。它危险到必须锁在隐秘地下金库里,不过现在终于可以上场测试。本题实现最终蚂蚁—— LaserAnt(激光蚂蚁),它是 ThrowerAnt 的变种。

食物成本 初始生命值

LaserAnt
10 1

The LaserAnt 会发射强力激光,伤害路径上的一切。各种 Bees and Ant都有可能受到 LaserAnt 伤害。 LaserAnt. When a LaserAnt 行动时,它会伤害同一位置中的所有 Insect(不包括自己,但包括可能容纳它的容器)以及前方的所有昆虫, Insect Place但不包括 Hive 中的 Bee。 Hive.

如果仅此而已, LaserAnt 会强得无法控制。它的基础伤害为 LaserAnt2. But, LaserAnt不过激光有一些限制:每远离 LaserAnt 一个位置,伤害就减弱 0.25 ;此外电池有限。 LaserAnt LaserAnt 每当它实际伤害一个 LaserAnt Insect,激光总伤害立即下降 Insect(1/16)。 0.0625 因此若同一格前方有两只 BeeInsect,第二只受到的伤害会更低。 LaserAnt Bee. If LaserAnt若受这些限制后伤害变为负数,就改为造成 0 伤害。

同一回合内各对象受伤的具体顺序不重要。

为了完成这种终极蚂蚁的实现,请通读 LaserAnt 类,适当地设置类属性,并实现以下两个函数:

  1. insects_in_front 是由 action 调用的实例方法, action 返回一个字典:键是 Insect, Insect 值是该 Insect 与 LaserAnt 的位置距离。 Insect LaserAnt字典应包含与 LaserAnt 同处或位于其前方的所有 Insect, Insects 但不包括 Hive 中的 Bee。 LaserAnt, excluding LaserAnt itself.
  2. calculate_damage 是一个实例方法,它接受 distance,即昆虫与 LaserAnt 实例的距离。它会根据以下因素计算 LaserAnt 实例造成的伤害值:

    • The distance LaserAnt 计算某只 Insect 应受伤害时,还需考虑: Insect is.
    • LaserAnt 已经伤害过的 Insect 数量, Insects that this LaserAnt 存储在相应实例属性中。 insects_shot

除了实现上述方法之外,您可能还需要根据需要在 LaserAnt 类中修改、添加或使用类或实例属性。

重要:若某只昆虫生命值不受影响,应 保持整数,与创建时一样。

注意:本题没有解锁测试。

python3 ok -q EC4

致谢: Tom Magrino、Eric Tzeng 与 John DeNero 共同开发了本项目,另有许多人参与贡献!

美术由 Alana Tran、Andrew Huang、Emilee Chen、Jessie Salas、Jingyi Li、Katherine Xu、Meena Vempaty、Michelle Chang 和 Ryan Davis 绘制。