Skip to main content

项目 1:Hog 游戏

5-sided die

我知道!我要用我的
高阶函数来
争取更高的点数。

介绍

提交须知: 为了获得完整学分:

  • 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 自动评分器
  • testsok 使用的测试目录
  • 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 电子邮件。

YouTube 链接

第一阶段:游戏规则

在第一阶段,您将开发 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 分。

要获得一次掷骰子的结果,请调用 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_diceboar_brawl,而不是重复实现它们的功能。

在编写代码之前,请解锁测试以确认你理解了题目:

python3 ok -q 03 -u

解锁完成后,就可以开始写代码了。你可以用下面的命令来检查你的代码是否正确:

python3 ok -q 03

问题 4 (2 分)

首先,实现 num_factors 函数,它接受一个正整数 n,并确定 n 的因数个数。

1 和 n 都是 n 的因数!

然后,实现 sus_pointssus_update 函数。

  • sus_points 接受玩家的分数,应用 Sus Fuss 规则后返回新的分数(例如,sus_points(5) 应该返回 5sus_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_updatesus_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_averagedroll_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_strategysus_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 BrawlSus 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 游戏吧!