Hog 游戏

5-sided die

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

介绍

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

  • 7 月 1 日(星期三)之前提交完成的第 1 阶段,价值 1 分。
  • 7 月 7 日(星期二).

请尽量按顺序完成题目,并在过程中为每道题运行 ok 测试,因为后面的实现可能依赖前面的题目。

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

7 月 6 日(星期一)之前提交整个项目可以获得 1 个奖励积分。项目截止日期和检查点截止日期可以申请延期,但提前提交的截止日期则不行。除非您是 DSP 学生,并且有作业延期方面的特殊安排。

在这个项目中,您将开发一个骰子游戏 Hog 的模拟器和多种策略。您需要一起使用 控制语句高阶函数 ,相关内容见第 1–3 讲以及教材第 1.2–1.6 节。 课本的 1.2 到 1.6 节中所述。

以前有同学没仔细读题就直接写代码,结果经常遇到麻烦。😱 在开始编写代码之前,请仔细阅读每个描述。

规则

在 Hog 游戏中,两名玩家轮流尝试成为第一个以至少 GOAL 总分结束回合的人,其中 GOAL 默认值为 100。每回合,当前玩家选择同时掷最多 10 个骰子;该回合得分通常是所有骰子点数之和。不过,骰子掷得越多,越可能触发下面的风险:

  • Sow Sad。如果任何骰子的结果是 1,则当前玩家的回合得分为 1,不论其他骰子掷出了什么点数。
  • 示例 1: 当前玩家掷出 7 个骰子,其中 5 个骰子的结果为 1。他们该回合得 1 分。
  • 示例 2: 当前玩家掷出 4 个骰子,所有骰子的结果均为 3。由于未发生逢一判负,他们该回合得 12 分。

在正常的 Hog 游戏中,这些就是所有的规则。为了给游戏增添趣味,我们将加入一些特殊规则:

  • 野猪乱斗。选择掷 0 个骰子的玩家,得分为“对手分数十位与自己分数个位之差的绝对值”的三倍,或 1 分,取较大者。个位是最右侧数字,十位是右起第二位;若分数不足 10,十位视为 0。
  • 示例 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 分。
  • Sus Fuss 规则。如果一个数(包括 1 和它自身在内)恰好有 3 或 4 个因数,我们称它为 sus 。若掷骰后当前玩家的总分是 sus 数,总分会立刻增加到比它大的最近素数。
  • 示例 1:

    • 某玩家原有 14 分,掷 2 个骰子得到 7 分,新总分为 21。21 有 4 个因数:1、3、7、21,因此它是 sus 数,玩家总分立即增加到下一个素数 23。
  • 示例 2:

    • 某玩家原有 63 分,掷 5 个骰子因 Sow Sad 😢 得到 1 分,新总分为 64。64 有 7 个因数:1、2、4、8、16、32、64,因此不是 sus 数,总分不再变化。
  • 示例 3:

    • 某玩家原有 49 分,掷 5 个骰子共得 18 分,新总分为素数 67,只有 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.

后勤

项目共 10 分,其中 1 分要求在 7 月 1 日(星期三)的检查点前提交第 1 阶段。

您将提交以下文件:

  • hog.py

您无需修改或提交任何其他文件即可完成项目。要提交项目: 请将所需文件提交到相应的 Gradescope 作业。

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

对于我们要求您完成的函数,可能会提供一些初始代码。如果您不想使用该代码,请随时删除它并从头开始。您也可以根据需要添加新的函数定义。

但是,请不要修改任何其他函数或编辑上述未列出的任何文件。这样做可能会导致您的代码无法通过我们的自动评分器测试。另外,请不要更改任何函数签名(名称、参数顺序或参数个数)。

在整个项目过程中,你应该测试代码的正确性。经常测试是一个好习惯,这样可以很容易地隔离任何问题。 但是,你不应该测试 频繁,以至于没有时间思考问题。

我们提供了一个 自动评分器 called ok ,以帮助您测试代码并跟踪您的进度。第一次运行自动评分器时,系统会要求您 使用 Web 浏览器登录您的 Ok 帐户。请这样做。每次运行 ok时,它都会在我们的服务器上备份您的工作和进度。

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

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

 python3 ok -q [question number] -i 
使用相应的问题编号(例如 01)运行。这将运行该问题的测试,直到遇到第一个失败的测试为止,然后您将有机会以交互方式测试您编写的函数。

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

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

图形用户界面(GUI)

我们为你提供了一个 图形用户界面 (简称 GUI)。目前游戏逻辑尚未实现,所以它还不能运行;完成 play 函数后,就能游玩完整的交互版 Hog。

完成后,可以从终端启动 GUI,并在浏览器中玩 Hog:

python3 hog_gui.py

第一阶段:游戏规则

在第一阶段,您将开发 Hog 游戏的模拟器。

问题 0

提供给您的 dice.py 文件用非纯的零参数函数表示骰子。这些函数每次调用可能返回不同结果,因此调用的副作用之一,是改变下一次调用时可能返回的值。

以下是 dice.py 中的文档,您需要阅读才能在此项目中模拟骰子。

# A dice function takes no arguments and returns a number from 1 to n
# (inclusive), where n is the number of sides on the dice.

# Fair dice produce each possible outcome with equal probability.
# Two fair dice are already defined, four_sided and six_sided,
# and are generated by the make_fair_dice function.

def make_fair_dice(sides):
    """Return a die that generates values ranging from 1 to SIDES, each with an equal chance."""
    ...

four_sided = make_fair_dice(4)
six_sided = make_fair_dice(6)

# Test dice are deterministic: they always cycles through a fixed
# sequence of values that are passed as arguments.
# Test dice are generated by the make_test_dice function.

def make_test_dice(...):
    """Return a die that cycles deterministically through OUTCOMES.

    >>> dice = make_test_dice(1, 2, 3)
    >>> dice()
    1
    >>> dice()
    2
    >>> dice()
    3
    >>> dice()
    1
    >>> dice()
    2

骰子函数不接受任何参数,并返回 1 到 n(包括 1 和 n)之间的数字,其中 n 是骰子的面数。

python3 ok -q 00 -u

您可以通过键入 exit().

已知在 Windows 系统中使用 Ctrl-C 退出解锁器可能会导致问题,请尽量避免。

问题 1

实现 roll_dice 函数位于 hog.py。它接收两个参数:正整数 num_rolls(指定掷骰次数)以及 dice 函数。它返回本回合掷骰 num_rolls 次所得的分数:要么是点数之和,要么因 (Sow Sad)而得到 1 分.

  • Sow Sad。如果任何骰子的结果是 1,则当前玩家的回合得分为 1,不论其他骰子掷出了什么点数。
  • 示例 1: 当前玩家掷出 7 个骰子,其中 5 个骰子的结果为 1。他们该回合得 1 分。
  • 示例 2: 当前玩家掷出 4 个骰子,所有骰子的结果均为 3。由于未发生逢一判负,他们该回合得 12 分。

要取得一次掷骰结果,请调用 dice()。你应在 dice() exactly num_rolls times 函数体中调用它。 roll_dice.

请记得,即使掷骰途中已经触发 Sow Sad,也要继续调用 dice() exactly num_rolls times ,直到完成全部次掷骰。这样才能正确模拟所有骰子同时掷出,用户界面也才能正常工作。

注意: 提供给您的 roll_dice 函数以及项目中的许多其他函数使用了 默认参数值,可从函数头看出:

def roll_dice(num_rolls, dice=six_sided): ...

参数 dice=six_sided 表示 dice 函数中的参数 roll_dice 是可选的 可选的。如果没有为它提供值,就默认使用 dice,那么 six_sided

例如, roll_dice(3, 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 文件并保存。要检查修复是否生效,需要退出 Python 解释器(可以使用 exit() or Ctrl^D),然后重新启动解释器。无论在终端还是 Python 解释器中,按上箭头都能找回先前输入的表达式,即使已经重启。

继续调试你的代码并运行 ok 测试,直到它们全部通过。

还有一个调试技巧:要在 ok 测试失败时自动启动交互式解释器,可以使用 -i选项。 例如, python3 ok -q 01 -i 将运行问题 1 的测试,然后在测试失败时启动加载了 hog.py 的交互式解释器。

问题 2

实现 boar_brawl函数,该函数接受玩家的当前分数 player_score 和对手的当前分数 opponent_score,返回玩家掷 0 个骰子并触发 Boar Brawl 时获得的分数。

  • 野猪乱斗。选择掷 0 个骰子的玩家,得分为“对手分数十位与自己分数个位之差的绝对值”的三倍,或 1 分,取较大者。个位是最右侧数字,十位是右起第二位;若分数不足 10,十位视为 0。
  • 示例 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 函数时,请确保它能正确处理任何非负分数。

Important: 你的代码 不应 使用 str、列表或包含方括号 [ ]。测试用例会检查是否使用了这些。

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

python3 ok -q 02 -u

解锁完成后,你可以编写代码,并使用以下命令检查正确性:

python3 ok -q 02

也可以在终端运行交互模式来测试 boar_brawlpython3 -i hog.py ,然后对多组输入调用 boar_brawl

问题 3

实现 take_turn 函数,返回一回合掷给定数量骰子所得的分数。 dice num_rolls times.

你的 take_turn 应调用已有的两个辅助函数,而不是重复它们的实现。 roll_diceboar_brawl

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

python3 ok -q 03 -u

解锁完成后,你可以编写代码,并使用以下命令检查正确性:

python3 ok -q 03


问题 4

首先,实现 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

提示:

  • 可以查看 simple_update 在文件中的实现,作为编写 hog.py 的起点。 sus_update 函数。
  • 请记住, take_turn 已经考虑了 Boar Brawl 规则!
  • Sus Fuss 规则。如果一个数(包括 1 和它自身在内)恰好有 3 或 4 个因数,我们称它为 sus 。若掷骰后当前玩家的总分是 sus 数,总分会立刻增加到比它大的最近素数。
  • 示例 1:

    • 某玩家原有 14 分,掷 2 个骰子得到 7 分,新总分为 21。21 有 4 个因数:1、3、7、21,因此它是 sus 数,玩家总分立即增加到下一个素数 23。
  • 示例 2:

    • 某玩家原有 63 分,掷 5 个骰子因 Sow Sad 😢 得到 1 分,新总分为 64。64 有 7 个因数:1、2、4、8、16、32、64,因此不是 sus 数,总分不再变化。
  • 示例 3:

    • 某玩家原有 49 分,掷 5 个骰子共得 18 分,新总分为素数 67,只有 1 和 67 两个因数。67 不是 sus 数,因此总分不变。

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

python3 ok -q 04 -u

解锁完成后,你可以编写代码,并使用以下命令检查正确性:

python3 ok -q 04

问题 5

实现 play 函数,该函数模拟 Hog 的完整游戏。玩家轮流掷骰子,直到其中一名玩家达到 goal 分数;函数最终返回两名玩家的最终总分。

要决定每回合掷几个骰子,需要调用当前玩家的策略函数(玩家 0 使用 strategy0 ,玩家 1 使用 strategy1). A 策略 是一个策略函数:给定玩家和对手的分数,返回当前回合要掷的骰子数。一个简单示例是 always_roll_5 ,它出现在 play.

要确定玩家在回合结束后更新的分数,请调用 update 函数。 update 函数需要输入要掷的骰子数量、当前玩家的分数、对手的分数,以及用于模拟掷骰子的函数。它返回当前玩家在回合结束后更新的分数。 update 函数的两个示例是 simple_updatesus_update。请记住,更新函数返回玩家回合结束后的 总分 ,而不仅是本回合增加的分数。

应用所有相关规则后,只要某位玩家在回合结束时达到或超过目标分数,游戏就结束。 play 将返回两名玩家的最终总分,玩家 0 的分数在前,玩家 1 的分数在后。

下面给出一些调用示例: play are:

  • 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 规则进行游戏(即所有规则)。

Important: 为了使用户界面正常工作,每个回合只能调用一次策略函数。仅当轮到玩家 0 时才调用 strategy0 ,仅当轮到玩家 1 时才调用 strategy1 ,此时轮到玩家 1。

提示:

  • 如果 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

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

确认无误后,上传 hog.py 到 Gradescope 的 Hog Checkpoint 作业入口 务必在 7 月 1 日(星期三)的检查点截止时间前提交。Gradescope 提交流程可回顾 Lab 00.

您可以在 Gradescope 提交页面点击姓名右侧的 编辑小组 并输入搭档的邮箱地址。只需一名搭档提交到 Gradescope。

恭喜!您已完成本项目的第 1 阶段!


插曲:用户界面

本项目此部分没有必做题,只有一些示例供您阅读和理解。有关剩余的项目问题,请参阅第 2 阶段。

Animated die

动画由 Tristan 与 Tyler Roath 制作

打印游戏事件

我们已经为游戏构建了一个模拟器,但没有添加任何代码来描述如何向人们展示游戏事件。因此,我们构建了一个无人可以玩的电脑游戏。(太逊了!)

但是,模拟器是用小型函数表示的,我们可以用一个版本替换每个函数,该版本打印出调用时发生的事情。使用高阶函数,我们可以在不更改太多原始代码的情况下做到这一点。 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 called hog_gui.py。您可以从终端运行它:

python3 hog_gui.py

hog_ui.py类似,GUI 依赖于您的模拟器实现。因此,如果您的代码中存在任何错误,这些错误也会反映在 GUI 中。这意味着您也可以将 GUI 用作调试工具;但是,最好先运行测试。

如果感兴趣,可以查看 hog_gui.py 文件以及 gui_files 目录的内容,了解 GUI 的工作方式。不过 GUI 实现还使用了 JavaScript、HTML 和 CSS。


第 2 阶段:策略

本阶段将尝试改进简单的 always_roll_five 策略(总是掷 5 个骰子)。 策略 是一个接受两个参数的函数:当前玩家的得分和对手的得分。它返回玩家将掷的骰子数量,可以是 0 到 10(包括 0 和 10)。

问题 6

实现 always_roll,这是一个高阶函数,它接受骰子数 n 并返回一个始终掷指定数量骰子的策略函数。 n 个骰子的策略。因此, always_roll(5) 相当于 always_roll_5.

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

python3 ok -q 06 -u

解锁完成后,你可以编写代码,并使用以下命令检查正确性:

python3 ok -q 06

问题 7

策略的参数值组合数量是有限的。例如,若游戏目标分数 goal 为 100,则玩家分数只有 100 种可能值 score 值(0-99)和 100 个可能的 opponent_score (0–99),因此策略函数总共只有 10,000 种参数组合。

玩家分数 对手分数组合
0 (0,0), (0,1), (0,2), ..., (0,99)
1 (1,0), (1,1), (1,2), ..., (1,99)
2 (2,0), (2,1), (2,2), ..., (2,99)
... ...
98 (98,0), (98,1), (98,2), ..., (98,99)
99 (99,0), (99,1), (99,2), ..., (99,99)

实现 is_always_roll,它接收一个策略,判断在所有可能参数组合下是否总是掷相同数量的骰子;每个分数最大为 goal 分。

提醒: 游戏会一直进行,直到一名玩家达到 goal 分(上例中为 goal 设置为 100,也可以是任意数)。请确保答案考虑指定目标下玩家与对手分数的每一种组合。 scoreopponent_score goal.

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

python3 ok -q 07 -u

解锁完成后,你可以编写代码,并使用以下命令检查正确性:

python3 ok -q 07

问题 8

实现 make_averaged,这是一个高阶函数 (higher-order function),它接受一个函数 original_function 作为参数。

该函数返回另一个函数,后者接收与原函数相同的参数。 make_averaged original_function当传入一组具体参数时,新函数应使用同一组参数反复调用原函数 original_function times_called 次,并返回结果平均值。请仔细阅读 make_averaged doctest,并注意究竟有哪些值被传入函数!

Doctest 解析:仔细查看 make_averaged doctest。在这里, original_function is roll_dice。注意这一行: averaged_dice(1, dice)。这说明传给 roll_dice are (1, dice) 的参数是什么(想想为什么!)。观察 averaged_dice 如何接收与原函数相同的参数 roll_dice。参数不是直接传给 roll_dice ,而是传给 averaged_dice。(想想如何实现!)请记住, make_averaged 应适用于任何参数结构与其返回函数相同的 original_functionmake_averaged在本例中,掷一次骰子视为一个样本(roll_dice(1, dice))。由于 times_called 设为 40,因此重复采样 40 次。 make_averaged 随后计算这 40 次调用的平均结果。 roll_dice.

Important: 要实现此函数,您需要使用一种新的 Python 语法。我们希望编写一个接受任意数量参数的函数,然后使用完全相同的参数调用另一个函数。 它是这样运作的。

你可以使用 *args all 表示传入函数的全部参数。随后可以把这些参数传给另一个函数,以相同参数调用它。 *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)  # *args represents the arguments (2, 8)
Result: 256
256
>>> printed_abs = printed(abs)
>>> printed_abs(-10)  # *args represents one argument (-10)
Result: 10
10

这里可以用 print_and_return via the *args 语法传入任意数量的参数,也可以在 *args 函数内部使用 print_and_return ,以同一组参数发起另一次函数调用。

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

python3 ok -q 08 -u

解锁完成后,你可以编写代码,并使用以下命令检查正确性:

python3 ok -q 08

问题 9

实现 max_scoring_num_rolls,它使用固定面数的骰子进行实验,确定在 1 到 10 次掷骰中,哪种次数能让回合平均得分最高。实现中应使用 make_averagedroll_dice.

如果两种掷骰次数并列最高平均分,返回较小者。例如 3 次和 6 次并列时返回 3。

解锁测试前,阅读本题 doctest 以及 make_averaged (问题 8)的 doctest 可能会有帮助。

Important: 为了通过我们所有的测试,请确保您正在测试从 1 到 10 开始的掷骰子,而不是从 10 到 1。 在编写任何代码之前,请解锁测试以验证您对问题的理解:

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

python3 ok -q 09 -u

解锁完成后,你可以编写代码,并使用以下命令检查正确性:

python3 ok -q 09

运行实验

提供的 run_experiments 函数调用 max_scoring_num_rolls(six_sided) 并打印结果。你可能会发现,掷6个骰子通常能使 roll_dice 函数在使用六面骰子时得到最高平均分。

要调用函数并查看结果,请运行 hog.py 并使用 -r flag:

python3 hog.py -r

此外, run_experiments 还会将不同的策略与 always_roll(6)进行比较。你可以随意修改 run_experiments 的实现。请注意,在完成接下来的两个问题之前, boar_strategysus_strategy 的实验结果可能不准确。

一些实验可能需要长达一分钟才能运行。 你始终可以减少对 make_averaged 的调用中的试验次数,以加快实验速度。

运行实验不会影响你在项目中的得分。


问题 10

一种策略可以尝试在最有利的情况下,利用 野猪乱斗 规则掷0。 实施 boar_strategy,如果掷0能获得 至少 threshold 分,则返回0;否则返回 num_rolls 。 此策略 不应 考虑 Sus Fuss 规则。

提示:可以使用 boar_brawl 函数。

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

python3 ok -q 10 -u

解锁完成后,你可以编写代码,并使用以下命令检查正确性:

python3 ok -q 10

您应该发现,现在运行 python3 hog.py -r 显示 boar_strategy 的胜率接近 66-67%。

问题 11

更好的策略会同时利用两条特殊规则。 野猪乱斗Sus Fuss 规则 规则。 例如,如果一个玩家有 53 分,而他们的对手有 60 分,掷 0 会使他们达到 62 分,这是一个 sus 数字,因此他们将以 67 分结束回合:相当于增加了 14 分!

提供给您的 sus_strategy 当掷 0 个骰子能让本回合后的总分至少比回合开始时高 至少 threshold 分时,该策略返回 0。

提示:可以使用 sus_update 使用你在问题 4 中定义的函数。

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

python3 ok -q 11 -u

解锁完成后,你可以编写代码,并使用以下命令检查正确性:

python3 ok -q 11

您应该发现,现在运行 python3 hog.py -r 显示 sus_strategy 的胜率接近 67-69%。

选做:问题 12

实现 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 提交页面点击姓名右侧的 编辑小组 并输入搭档的邮箱地址。只需一名搭档提交到 Gradescope。

恭喜您,您已经完成了 CS 61A 的第一个项目! 不妨放松一下,和朋友一起玩几局 Hog 游戏吧!