项目 1:Hog 游戏
我知道!我要用我的
高阶函数来
争取更高的点数。
介绍
提交须知: 为了获得完整学分:
- 在1月30日星期二之前提交完成的第 1 阶段,价值 1 分。
- 在2月7日星期三之前提交完整的项目。
请尽量按顺序完成题目,因为后面的问题可能依赖于前面的问题,这也会影响
ok
测试的运行。你可以和你的伙伴一起完成这个项目。
在2月6日星期二之前提交整个项目可以获得 1 个奖励积分。项目截止日期和检查点截止日期可以申请延期,但提前提交的截止日期则不行。除非您是 DSP 学生,并且有作业延期方面的特殊安排。
在这个项目中,您将开发一个骰子游戏 Hog 的模拟器和多种策略。您需要一起使用控制语句和高阶函数,如在线教科书 Composing Programs 的 1.2 到 1.6 节中所述。
以前有同学没仔细读题就直接写代码,结果经常遇到麻烦。😱 在开始编写代码之前,请仔细阅读每个描述。
规则
在 Hog 游戏中,两名玩家轮流尝试成为第一个以至少 GOAL
总分结束回合的人,其中 GOAL
默认为 100。在每个回合中,当前玩家选择掷出一些骰子,最多 10 个。该玩家该回合的分数是骰子结果的总和。但是,掷出太多骰子的玩家会面临以下风险:
逢一判负:如果掷出的骰子中任何一个为 1,则当前玩家该回合得分为 1。(英文名:Sow Sad)
示例 1: 当前玩家掷出 7 个骰子,其中 5 个骰子的结果为 1。他们该回合得
1
分。示例 2: 当前玩家掷出 4 个骰子,所有骰子的结果均为 3。由于未发生逢一判负,他们该回合得
12
分。
在正常的 Hog 游戏中,这些就是所有的规则。为了给游戏增添趣味,我们将加入一些特殊规则:
野猪乱斗:当玩家掷出零个骰子时,其得分为对手分数十位与自身分数个位之差的绝对值的三倍,或者 1 分,取两者中的较大值。(英文名:Boar Brawl) 个位数为最右边的数字,十位数为倒数第二位数字。如果玩家的分数是个位数(小于 10),则该玩家分数的十位数为 0。
示例 1:
- 当前玩家有
21
分,对手有46
分,当前玩家选择不掷骰子。 - 对手分数的十位数为
4
,当前玩家分数的个位数为1
。 - 因此,玩家获得
3 * abs(4 - 1) = 9
分。
- 当前玩家有
示例 2:
- 当前玩家有
45
分,对手有52
分,当前玩家选择跳过掷骰子环节。 - 对手分数的十位数为
5
,当前玩家分数的个位数为5
。 - 由于
3 * abs(5 - 5) = 0
,因此玩家获得1
分。
- 当前玩家有
示例 3:
- 当前玩家有
2
分,对手有5
分,当前玩家选择掷出零个骰子。 - 对手分数的十位数为
0
,当前玩家分数的个位数为2
。 - 因此,玩家获得
3 * abs(0 - 2) = 6
分。
- 当前玩家有
Sus Fuss。如果一个数字恰好有 3 个或 4 个因子(包括 1 和它本身),我们就称它为 sus,即满足“可疑”条件的数字。如果在掷骰子后,当前玩家的分数是一个 sus 数字,那么他们的分数会直接提升至下一个质数。
示例 1:
- 玩家有 14 分,掷出 2 个骰子,总共得到 7 分。他们的新分数将是 21,新分数 21 包含四个因子:1、3、7 和 21。因为 21 是 sus,所以玩家的分数增加到 23,即下一个质数。
示例 2:
- 玩家有 63 分,掷出 5 个骰子,总共得到 1 分。他们的新分数将是 64,它有 7 个因子:1、2、4、8、16、32 和 64。因为 64 不是 sus,所以玩家的分数保持不变。
示例 3:
- 玩家有 49 分,掷出 5 个骰子,总共得到 18 分。他们的新分数将是 67,这是一个质数,有 2 个因子:1 和 67。因为 67 不是 sus,所以玩家的分数保持不变。
下载起始文件
请首先下载项目代码的 zip 压缩包。以下是解压缩后将在存档中看到的所有文件的列表。对于该项目,您只需修改 hog.py
。
hog.py
:Hog 的起始实现dice.py
:用于制作和掷骰子的函数hog_gui.py
:Hog 的图形用户界面 (GUI)(已更新)ucb.py
:CS 61A 的实用函数hog_ui.py
:Hog 的基于文本的用户界面 (UI)ok
:CS 61A 自动评分器tests
:ok
使用的测试目录gui_files
:Web GUI 使用的各种事物的目录
您可能会注意到一些文件,除了上面列出的文件之外,这些文件是制作自动评分器和 GUI 部分工作所必需的。请不要修改除 hog.py
之外的任何文件。
后勤
该项目总价值 25 分。其中,在 1 月 30 日星期二的检查点日期前提交第一阶段,可获得 1 分。
您将提交以下文件:
hog.py
您无需修改或提交任何其他文件即可完成项目。要提交项目:请将所需文件提交到相应的 Gradescope 作业。
对于我们要求您完成的函数,可能会提供一些初始代码。如果您不想使用该代码,请随时删除它并从头开始。您也可以根据需要添加新的函数定义。
但是,请不要修改任何其他函数或编辑上述未列出的任何文件。这样做可能会导致您的代码无法通过我们的自动评分器测试。另外,请不要更改任何函数签名(名称、参数顺序或参数个数)。
在整个项目中,您应该测试代码的正确性。经常测试是一个好习惯,这样可以很容易地隔离任何问题。但是,您不应该过于频繁地测试,以便让自己有时间思考问题。
我们提供了一个自动评分器,称为 ok
,以帮助您测试代码并跟踪您的进度。第一次运行自动评分器时,系统会要求您使用 Web 浏览器登录您的 Ok 帐户。请这样做。每次运行 ok
时,它都会在我们的服务器上备份您的工作和进度。
ok
的主要目的是测试您的实现。
如果您想以交互方式测试您的代码,您可以运行:
python3 ok -q [问题编号] -i
使用相应的问题编号(例如 01
)运行。这将运行该问题的测试,直到遇到第一个失败的测试为止,然后您将有机会以交互方式测试您编写的函数。
您还可以通过输入以下代码,在 OK 中使用调试打印功能:
print("DEBUG:", x)
这会在终端中生成输出,且不会因多余输出而导致 OK 测试失败。
图形用户界面(GUI)
我们为您提供了一个图形用户界面(GUI)。目前,它无法工作,因为您尚未实现游戏逻辑。完成 play
函数后,您就能体验完整的交互式 Hog 游戏了!
完成之后,您可以从终端运行 GUI:
python3 hog_gui.py
入门视频
这些视频可能对您解决此作业中的编码问题有所帮助。
要观看这些视频,您应该登录您的 berkeley.edu 电子邮件。
第一阶段:游戏规则
在第一阶段,您将开发 Hog 游戏的模拟器。
问题 0 (0 分)
dice.py
文件使用不纯的、不带参数的函数来模拟骰子。这些函数是不纯的,因为每次调用它们时可能会有不同的返回值,因此调用该函数的副作用是更改再次调用该函数时将返回的内容。
以下是 dice.py
中的文档,您需要阅读才能在此项目中模拟骰子。
骰子函数不接受任何参数,并返回 1 到 n(包括 1 和 n)之间的数字,其中 n 是骰子的面数。
公平骰子以相等的概率产生每种可能的结果。 已经定义了两个公平骰子,four_sided 和 six_sided,它们由 make_fair_dice 函数生成。
测试骰子是确定性的:它们总是循环通过作为参数传递的固定序列的值。 测试骰子由 make_test_dice 函数生成。
def make_fair_dice(sides):
"""返回一个骰子,以相等的概率返回 1 到 SIDES。"""
...
four_sided = make_fair_dice(4)
six_sided = make_fair_dice(6)
def make_test_dice(...):
"""返回一个骰子,它以确定性的方式循环通过 OUTCOMES。
>>> dice = make_test_dice(1, 2, 3)
>>> dice()
1
>>> dice()
2
>>> dice()
3
>>> dice()
1
>>> dice()
2
通过解锁以下测试来检查您的理解。
python3 ok -q 00 -u
您可以通过键入 exit()
退出解锁器。
已知在 Windows 系统中使用 Ctrl-C 退出解锁器可能会导致问题,请尽量避免。
问题 1 (2 分)
在 hog.py
中实现 roll_dice
函数。它接受两个参数:一个名为 num_rolls
的正整数,表示掷骰子的次数,以及一个 dice
函数。它返回在一回合中掷 num_rolls
次骰子所获得的点数:骰子点数之和,或者 1(如果触发了 Sow Sad 规则)。
Sow Sad。如果任何骰子的结果是 1,则当前玩家的回合得分为
1
。- 示例 1: 当前玩家掷了 7 个骰子,其中 5 个是 1。他们在本回合中获得
1
分。 - 示例 2: 当前玩家掷了 4 个骰子,全部都是 3。由于没有发生 Sow Sad,他们在本回合中获得
12
分。
- 示例 1: 当前玩家掷了 7 个骰子,其中 5 个是 1。他们在本回合中获得
要获得一次掷骰子的结果,请调用 dice()
。您应该在 roll_dice
的主体中精确地调用 dice()
num_rolls
次。
记住,即使在掷骰子过程中发生了“猪之悲伤”(Sow Sad)事件,也要务必调用 dice()
函数 num_rolls
次。这样做,你就能正确模拟同时掷出所有骰子的情形(并且用户界面也能正常工作)。
注意:
roll_dice
函数,以及本项目中的许多其他函数,都使用了默认参数值——你可以在函数定义中看到这一点:def roll_dice(num_rolls, dice=six_sided): ...
参数
dice=six_sided
表示在调用roll_dice
函数时,dice
参数是可选的。 如果没有为dice
参数提供值,则默认使用six_sided
。例如,
roll_dice(3, four_sided)
或roll_dice(3, dice=four_sided)
模拟的是掷 3 个四面骰子,而roll_dice(3)
模拟的是掷 3 个六面骰子。
理解问题:
在编写任何代码之前,请先解锁测试,以确认你理解了题目要求:
python3 ok -q 01 -u
注意: 在解锁相应问题的测试用例之前,你将无法使用
ok
测试你的代码。
编写代码并检查你的工作:
完成解锁后,开始实现你的解决方案。 你可以使用以下命令检查你的正确性:
python3 ok -q 01
请参考调试指南!
调试技巧
如果测试没有通过,就需要进行调试。 你可以直接使用 Python 来观察函数的行为。 首先,启动 Python 解释器并加载 hog.py
文件。
python3 -i hog.py
接下来,你可以使用任意数量的骰子来调用 roll_dice
函数。
>>> roll_dice(4)
你会发现,每次调用上述表达式时,结果可能都不一样,因为它模拟的是随机掷骰子的过程。 例如,如果已知骰子会依次掷出 3 和 4,那么掷两次的总点数应该是 7。
>>> fixed_dice = make_test_dice(3, 4)
>>> roll_dice(2, fixed_dice)
7
在大多数系统中,你可以通过按向上箭头,然后按 Enter 或 Return 键来再次评估相同的表达式。 要评估更早的命令,请重复按向上箭头。
如果你发现了问题,首先需要修改
hog.py
文件来解决它,然后保存文件。 接着,要验证你的修改是否有效,你必须使用exit()
或Ctrl+D
命令退出 Python 解释器,然后重新运行解释器来测试你所做的修改。 即使在重新启动 Python 之后,在终端和 Python 解释器中按向上箭头也应该可以访问你之前的表达式。继续调试你的代码并运行
ok
测试,直到它们全部通过。还有一个调试技巧:要在
ok
测试失败时自动启动交互式解释器,可以使用-i
选项。 例如,python3 ok -q 01 -i
将运行问题 1 的测试,然后在测试失败时启动加载了hog.py
的交互式解释器。
问题 2 (2 分)
实现 boar_brawl
函数,该函数接受玩家的当前分数 player_score
和对手的当前分数 opponent_score
作为输入,并返回当玩家掷 0 个骰子时,通过野猪争斗所获得的分数。
- 野猪争斗(Boar Brawl)。 掷零个骰子的玩家获得的分数为:对手分数的十位数与当前玩家分数的个位数之差的绝对值的三倍,与 1 相比,取较大值。
个位数是最右边的数字,十位数是从右边数起的第二个数字。
例子 1:
- 当前玩家 21 分,对手 46 分,选择掷 0 个骰子。
- 对手分数的十位是 4,当前玩家分数的个位是 1。
- 因此,玩家获得
3 * abs(4 - 1) = 9
分。
例子 2:
- 当前玩家 45 分,对手 52 分,选择掷 0 个骰子。
- 对手分数的十位是 5,当前玩家分数的个位是 5。
- 由于
3 * abs(5 - 5) = 0
,玩家获得 1 分。
例子 3:
- 当前玩家 2 分,对手 5 分,选择掷 0 个骰子。
- 对手分数的十位是 0,当前玩家分数的个位是 2。
- 因此,玩家获得
3 * abs(0 - 2) = 6
分。
不要假设分数低于 100。编写
boar_brawl
函数时,请确保它能正确处理任何非负分数。
注意: 你的代码不应使用
str
、列表或包含方括号[
]
。测试用例会检查是否使用了这些。
在编写代码之前,请解锁测试以确认你理解了题目:
python3 ok -q 02 -u
解锁完成后,就可以开始写代码了。你可以用下面的命令来检查你的代码是否正确:
python3 ok -q 02
你也可以通过在终端运行 python3 -i hog.py
并在各种输入上调用 boar_brawl
来进行交互式测试。
问题 3 (2 分)
实现 take_turn
函数,它通过掷 num_rolls
次骰子(由给定的 dice
函数生成)来返回该回合获得的分数。
你的 take_turn
函数应该调用 roll_dice
和 boar_brawl
,而不是重复实现它们的功能。
在编写代码之前,请解锁测试以确认你理解了题目:
python3 ok -q 03 -u
解锁完成后,就可以开始写代码了。你可以用下面的命令来检查你的代码是否正确:
python3 ok -q 03
问题 4 (2 分)
首先,实现 num_factors
函数,它接受一个正整数 n
,并确定 n
的因数个数。
1 和
n
都是n
的因数!
然后,实现 sus_points
和 sus_update
函数。
sus_points
接受玩家的分数,应用 Sus Fuss 规则后返回新的分数(例如,sus_points(5)
应该返回5
,sus_points(21)
应该返回23
)。你应该在你的实现中使用num_factors
和提供的is_prime
函数。sus_update
返回玩家掷num_rolls
个骰子后的总分,一并考虑 Boar Brawl 规则和 Sus Fuss 规则。你应该在此函数中使用sus_points
。
提示: 你可以查看
hog.py
中提供的simple_update
函数的实现,并将其作为sus_update
函数的起点。
Sus Fuss。如果一个数字恰好有 3 个或 4 个因数(包括 1 和数字本身),我们就称它为 sus。如果在掷骰子后,当前玩家的分数是一个 sus 数字,他们会获得足够的分数,直接升到下一个素数。
示例 1:
- 一名玩家有 14 分,掷出 2 个骰子,总共得到 7 分,因此他们的总分变为 21 分。21 有 1、3、7 和 21 四个因子。因为 21 是 sus 数,玩家的分数会增加到下一个质数 23。
示例 2:
- 一名玩家有 63 分,掷出 5 个骰子,总共得到 1 分,因此他们的总分变为 64 分。64 有 1、2、4、8、16、32 和 64 七个因子。因为 64 不是 sus 数,玩家的分数保持不变。
示例 3:
- 一名玩家有 49 分,掷出 5 个骰子,总共得到 18 分,因此他们的总分变为 67 分。67 是一个质数,只有 1 和 67 两个因子。因为 67 不是 sus 数,玩家的分数保持不变。
在开始写代码之前,先解锁测试,确认你理解了题目要求:
python3 ok -q 04 -u
解锁完成后,就可以开始写代码了。你可以用下面的命令来检查代码是否正确:
python3 ok -q 04
问题 5 (4 分)
实现 play
函数,该函数模拟 Hog 的完整游戏。玩家轮流掷骰子,直到其中一名玩家达到 goal
分数,该函数会返回两名玩家的最终分数。
要决定每回合掷几个骰子,需要调用当前玩家的策略函数(玩家 0 使用 strategy0
,玩家 1 使用 strategy1
)。策略是一个函数,它接受两个参数:当前玩家的分数和对手的分数,并返回玩家在该回合中将掷出的骰子数量。一个策略示例是 always_roll_5
,它出现在 play
函数上方。
要确定玩家在回合结束后更新的分数,请调用 update
函数。update
函数需要输入要掷的骰子数量、当前玩家的分数、对手的分数,以及用于模拟掷骰子的函数。它返回当前玩家在回合结束后更新的分数。update
函数的两个示例是 simple_update
和 sus_update
。
如果玩家在回合结束后达到目标分数,游戏就结束了 (所有规则都已应用)。然后,play
将返回两名玩家的最终总分,玩家 0 的分数在前,玩家 1 的分数在后。
play
函数的一些调用示例:
play(always_roll_5, always_roll_5, simple_update)
模拟两个玩家,他们每回合都掷 5 个骰子,仅使用 Sow Sad 和 Boar Brawl 规则进行游戏。play(always_roll_5, always_roll_5, sus_update)
模拟两个玩家,他们每回合都掷 5 个骰子,除了 Sow Sad 和 Boar Brawl 规则外,还使用 Sus Fuss 规则进行游戏(即所有规则)。
注意: 为了使用户界面正常工作,每个回合只能调用一次策略函数。仅当轮到玩家 0 时才调用
strategy0
,仅当轮到玩家 1 时才调用strategy1
。提示:
- 如果
who
是当前玩家,则下一个玩家是1 - who
。- 要调用
play(always_roll_5, always_roll_5, sus_update)
并打印出每回合发生的事情,请从终端运行python3 hog_ui.py
。
在开始写代码之前,先解锁测试,确认你理解了题目要求:
python3 ok -q 05 -u
解锁完成后,就可以开始写代码了。你可以用下面的命令来检查代码是否正确:
python3 ok -q 05
确认你已经完成了第一阶段的所有题目:
python3 ok --score
然后,请在检查点截止日期前将您的作业提交至 Gradescope。
当您运行 ok
命令时,您仍然会看到部分测试被锁定,因为您尚未完成整个项目。如果您完成了到目前为止的所有问题,您将获得该检查点的全部学分。
恭喜!您已完成本项目的第 1 阶段!
插曲:用户界面
本项目此部分没有必做题,只有一些示例供您阅读和理解。有关剩余的项目问题,请参阅第 2 阶段。
打印游戏事件
我们已经为游戏构建了一个模拟器,但没有添加任何代码来描述如何向人们展示游戏事件。因此,我们构建了一个无人可以玩的电脑游戏。(太逊了!)
但是,模拟器是用小型函数表示的,我们可以用一个版本替换每个函数,该版本打印出调用时发生的事情。使用高阶函数,我们可以在不更改太多原始代码的情况下做到这一点。hog_ui.py
中提供了一个示例,我们鼓励您阅读。
play_and_print
函数调用刚刚实现的相同 play
函数,但使用:
- 新的策略函数(例如,
printing_strategy(0, always_roll_5)
),用于打印得分和掷骰次数。 - 一个新的更新函数 (
sus_update_and_print
),用于打印每回合的结果。 - 一个新的骰子函数 (
printing_dice(six_sided)
),用于打印掷骰子的结果。
请注意有多少原始模拟器代码可以被重用。
从终端运行 python3 hog_ui.py
会调用 play_and_print(always_roll_5, always_roll_5)
。
接受用户输入
内置的 input
函数等待用户输入一行文本,然后将该文本作为字符串返回。内置的 int
函数可以接受包含整数数字的字符串并返回该整数。
interactive_strategy
函数返回一个策略,该策略允许人们通过调用 input
选择每回合掷多少个骰子。
我们终于可以使用 play
函数来玩游戏了:
从终端运行 python3 hog_ui.py -n 1
会调用 play_and_print(interactive_strategy(0), always_roll_5)
,这会在人类玩家(玩家 0)和始终掷 5 的电脑策略之间进行游戏。
从终端运行 python3 hog_ui.py -n 2
会调用 play_and_print(interactive_strategy(0), interactive_strategy(1))
,这会在两个人类玩家之间进行游戏。
欢迎您随意更改 hog_ui.py
,例如使用与 always_roll_5
不同的策略。
图形用户界面 (GUI)
我们还使用与 hog_ui.py
类似的方法为游戏提供了一个基于 Web 的图形用户界面,称为 hog_gui.py
。您可以从终端运行它:
python3 hog_gui.py
与 hog_ui.py
类似,GUI 依赖于您的模拟器实现。因此,如果您的代码中存在任何错误,这些错误也会反映在 GUI 中。这意味着您也可以将 GUI 用作调试工具;但是,最好先运行测试。
Hog GUI 的源代码在 Github 上公开提供,但涉及其他几种编程语言:Javascript、HTML 和 CSS。
第 2 阶段:策略
在此阶段,您将尝试改进始终投掷五个骰子的基本策略的方法。策略是一个接受两个参数的函数:当前玩家的得分和对手的得分。它返回玩家将掷的骰子数量,可以是 0 到 10(包括 0 和 10)。
问题 6 (2 分)
实现 always_roll
,这是一个高阶函数,它接受骰子数 n
并返回一个始终掷 n
个骰子的策略。因此,always_roll(5)
相当于 always_roll_5
。
在编写任何代码之前,请解锁测试以验证您对问题的理解:
python3 ok -q 06 -u
解锁完成后,即可开始编写你的代码。您可以使用以下命令检查您的正确性:
python3 ok -q 06
问题 7 (2 分)
一个策略只有固定数量的可能参数值。例如,在一个目标为 100 的游戏中,有 100 个可能的 score
值(0-99)和 100 个可能的 opponent_score
值(0-99),总共有 10,000 种可能的参数组合。
实现 is_always_roll
,它接受一个策略并返回该策略是否始终为每个可能的参数组合(直到 goal
分数)滚动相同数量的骰子。
提醒: 游戏会一直进行,直到一名玩家达到
goal
分数(在上面的示例中,goal
设置为100
)。请确保你的代码能够处理所有可能的goal
参数组合情况。
在编写任何代码之前,请解锁测试以验证您对问题的理解:
python3 ok -q 07 -u
解锁完成后,即可开始编写你的代码。您可以使用以下命令检查您的正确性:
python3 ok -q 07
问题 8 (2 分)
实现 make_averaged
,这是一个高阶函数 (higher-order function),它接受一个函数 original_function
作为参数。
make_averaged
的返回值是一个函数,该函数接受与 original_function
相同数量的参数。当我们用这些参数调用返回的函数时,它会多次调用 original_function
并返回结果的平均值。
具体来说,此函数应总共调用 original_function
samples_count
次,并返回这些调用结果的平均值。
重要提示: 要实现此函数,您需要使用一种新的 Python 语法。我们希望编写一个接受任意数量参数的函数,然后使用完全相同的参数调用另一个函数。 它是这样运作的。
与其列出函数的正式参数,不如使用
*args
,它表示传递给函数的所有参数。 然后,我们可以将*args
传递给另一个函数,从而使用相同的参数调用该函数。 例如:>>> def printed(f):
... def print_and_return(*args):
... result = f(*args)
... print('Result:', result)
... return result
... return print_and_return
>>> printed_pow = printed(pow)
>>> printed_pow(2, 8)
Result: 256
256
>>> printed_abs = printed(abs)
>>> printed_abs(-10)
Result: 10
10在这里,我们可以通过
*args
语法将任意数量的参数传递到print_and_return
中。 我们还可以在print_and_return
函数中使用*args
来使用相同的参数进行另一次函数调用。
在编写任何代码之前,请解锁测试以验证您对问题的理解:
python3 ok -q 08 -u
解锁完成后,即可开始编写你的代码。您可以使用以下命令检查您的正确性:
python3 ok -q 08
问题 9 (2 分)
实现 max_scoring_num_rolls
,它运行一个实验来确定哪个掷骰次数(从 1 到 10)能使一回合的平均得分最大化。 您的实现应使用 make_averaged
和 roll_dice
。
如果两个掷骰次数的平均得分最高,则返回较低的数字。 例如,如果 3 和 6 都达到最高平均得分,则返回 3。
在解锁测试之前,建议先阅读本题的 doctest 以及 doctest 中的示例,会对你有所帮助。
重要提示: 为了通过我们所有的测试,请确保您正在测试从 1 到 10 开始的掷骰子,而不是从 10 到 1。 在编写任何代码之前,请解锁测试以验证您对问题的理解:
python3 ok -q 09 -u
解锁完成后,就可以开始编写你的解决方案了。你可以用以下命令来检查你的代码是否正确:
python3 ok -q 09
运行实验
提供的 run_experiments
函数调用 max_scoring_num_rolls(six_sided)
并打印结果。你可能会发现,掷6个骰子通常能使roll_dice
函数在使用六面骰子时得到最高平均分。
要调用此函数并查看结果,请使用 -r
标志运行 hog.py
:
python3 hog.py -r
此外,run_experiments
还会将不同的策略与 always_roll(6)
进行比较。你可以随意修改 run_experiments
的实现。请注意,在完成接下来的两个问题之前,boar_strategy
和 sus_strategy
的实验结果可能不准确。
一些实验可能需要长达一分钟才能运行。 你始终可以减少对 make_averaged
的调用中的试验次数,以加快实验速度。
运行实验不会影响你在项目中的得分。
问题 10 (2 分)
一种策略可以尝试在最有利的情况下,利用Boar Brawl规则掷0。 实施 boar_strategy
,如果掷0能获得至少 threshold
分,则返回0;否则返回 num_rolls
。 此策略不应考虑 Sus Fuss 规则。
提示:您可以使用在问题 2 中定义的
boar_brawl
函数。
在编写任何代码之前,请解锁测试以验证您对问题的理解:
python3 ok -q 10 -u
完成解锁后,开始实施您的解决方案。您可以使用以下命令检查您的正确性:
python3 ok -q 10
您应该发现,现在运行 python3 hog.py -r
显示 boar_strategy
的胜率接近 66-67%。
问题 11 (2 分)
更优的策略会结合利用 Boar Brawl 和 Sus Fuss 规则。 例如,如果一个玩家有 53 分,而他们的对手有 60 分,掷 0 会使他们达到 62 分,这是一个 sus 数字,因此他们将以 67 分结束回合:相当于增加了 14 分!
sus_strategy
在掷0后,得分至少比玩家回合开始时高 threshold
分,则返回0。
提示:您可以使用
sus_update
函数。
在编写任何代码之前,请解锁测试以验证您对问题的理解:
python3 ok -q 11 -u
完成解锁后,开始实施您的解决方案。您可以使用以下命令检查您的正确性:
python3 ok -q 11
您应该发现,现在运行 python3 hog.py -r
显示 sus_strategy
的胜率接近 67-69%。
可选:问题 12 (0 分)
实施 final_strategy
,它结合了这些想法和您拥有的任何其他想法,以实现针对基线策略的高胜率。 一些建议:
- 如果您知道目标分数(默认情况下为 100),则得分超过目标没有任何好处。 检查您是否可以通过掷 0、1 或 2 个骰子来获胜。 如果您处于领先地位,您可能会决定承担更少的风险。
- 不要使用阈值,而是在掷 0 会给您带来比掷 6 个骰子更高的平均分时掷 0。
你可以通过运行 ok
命令来检查你的策略是否有效。
python3 ok -q 12
项目提交
在所有问题上运行 ok
以确保所有测试都已解锁并通过:
python3 ok
你也可以查看你在每个项目部分的得分情况:
python3 ok --score
确认所有题目已完成,请将 hog.py
文件上传至 Gradescope 以提交本次作业。具体操作步骤请参考 Lab 00。
您可以在 Gradescope 提交页面点击姓名右侧的 + Add Group Member 以添加合作伙伴。只需一位合作伙伴提交至 Gradescope 即可。
恭喜您,您已经完成了 CS 61A 的第一个项目! 不妨放松一下,和朋友一起玩几局 Hog 游戏吧!