> For the complete documentation index, see [llms.txt](https://zlt-shadow.gitbook.io/sicp-in-python-cn/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://zlt-shadow.gitbook.io/sicp-in-python-cn/di-1-zhang-yong-han-shu-gou-jian-chou-xiang/xiang-mu-1-hog-you-xi.md).

# 项目1 Hog游戏

![我知道! 我要用高阶函数来求更高的点数。](/files/-MYIC17PcrG17-yIAL1L)

### 介绍

在这个项目中，你将开发一个模拟器和多种策略的骰子游戏Hog。 您需要同时使用控制语句和高阶函数，如本书的1.2到1.6节所述。&#x20;

在《Hog》中，两名玩家轮流尝试着成为第一个获得至少100分的玩家。 在每个回合中，当前玩家选择一些骰子，最多10个。 该玩家在该回合的得分是所有骰子结果的总和。

### 规则

为了给游戏增添趣味，我们还将玩一些特别的规则：

* **狼吞虎咽。**&#x5982;果任何一个骰子结果是1，那么当前玩家在该回合的得分是1。&#x20;

  * 例1：当前玩家掷7个骰子，其中5个是1。他们在这个回合中得了一分。
  * 例子2：当前玩家掷4个骰子，所有骰子都是3。由于没有出现狼吞虎咽，他们在该回合中获得12分。

* **免费的培根。**&#x9009;择掷零骰子的玩家得分等于10减去对手得分的个位数和十位数中的最小值。

  * 例子1：对手有13分，当前玩家选择掷零骰子。1和3的最小值是1，所以当前玩家获得10 - 1 = 9分。&#x20;
  * 例子2：对手有85分，而当前玩家选择掷零骰子。8和5的最小值是5，所以当前玩家获得10 - 5 = 5分。&#x20;
  * 例子3：对手有7分，而当前玩家选择掷零骰子。0和7的最小值是0，所以当前玩家获得10 - 0 = 10分。

* **猪交换。**&#x5728;将该回合的得分添加到当前玩家的得分中之后，如果当前玩家得分的第一个数字(最左)和对手玩家得分的最后一个数字(最右)相等，那么这两个分数就交换了。

  * 例1：当前玩家的总分为31分，对手为83分。 当前玩家掷一个值为5的骰子。 玩家的新分数是36，对手的分数是83。 当前选手得分的最左边数字和对手得分的最右边数字都是3，所以分数被交换了。&#x20;
  * 例2：当前玩家总分为1，对手总分为2。 当前玩家掷一个值为6的骰子。 玩家的新分数是7，对手的分数是2。 当前选手得分的最左边数字是7，对手得分的最右边数字是2。 分数不会被交换。&#x20;
  * 例3：当前玩家的总分为99分，对手为21分。 当前玩家掷三个骰子，总共8分。 玩家的新分数是107，对手的分数是21。 当前玩家得分的最左边数字是1，对手得分的最右边数字都是1，所以比分交换了。&#x20;
  * 例4：当前玩家的总分为35分，对手为25分。 当前玩家掷两个骰子，总数为10。 玩家的新分数是45，对手的分数是25。 当前玩家得分的最左边数字不等于对手得分的最右边数字，因此比分不会交换。

### 下载启动文件

首先，以zip存档的形式下载[所有项目代码](https://inst.eecs.berkeley.edu/~cs61a/su19/proj/hog/hog.zip)。您只需要对hog.py进行更改。

* hog.py：Hog的开始实现
* dice.py：掷骰子函数
* hog\_gui.py：Hog的图形用户界面
* ucb.py：用于cs61a的实用函数
* ok：CS 61A的评分器
* tests：ok使用的tests目录
* images：hog\_gui.py使用的图片目录

### 其他

对于我们要求您完成的功能，可能会有一些我们提供的初始代码。 如果您不想使用这些代码，可以随意删除它，然后从头开始。 您还可以根据需要添加新的函数定义。

但是，请不要修改任何其他功能。 这样做可能会导致您的代码无法通过我们的自动评分器测试。另外，请不要更改任何函数签名(名称、参数顺序或参数数量)。&#x20;

在整个项目中，您应该测试代码的正确性。 经常进行测试是一种很好的做法，这样可以很容易地隔离任何问题。但是，您不应该过于频繁地测试，这样就会给自己留出时间来思考问题。&#x20;

我们提供了一个名为ok的自动评分器来帮助你测试你的代码并跟踪你的进度。 当你第一次运行自动分级器时，你会被要求用你的Ok帐户使用你的网络浏览器登录。 请这样做。 每次运行ok，它都会在我们的服务器上备份您的工作和进度。&#x20;

ok的主要目的是测试您的实现。&#x20;

我们建议您在完成每个问题后提交。 只有你最后提交的作品才会被评分。 对我们来说，有更多的代码备份也是很有用的，以防你遇到提交问题。&#x20;

如果您不希望我们记录您的工作或有关您的进度的信息的备份，您可以运行

```
python3 ok --local
```

有了这个选项，任何信息都不会被发送到我们的课程服务器。如果您想以交互方式测试代码，可以运行

```
 python3 ok -q [question number] -i 
```

插入适当的问题编号(如01)。 这将运行该问题的测试，直到第一个测试失败，然后给您一个机会测试您编写的函数的交互。&#x20;

您还可以通过编写来使用OK中的调试打印特性

```
print("DEBUG:", x) 
```

这将在您的终端中产生一个输出，而不会导致OK测试因为额外的输出而失败。

### 图形用户界面

为您提供了一个图形用户界面(简称GUI)。 目前，它还不能工作，因为你还没有实现游戏逻辑。 一旦你完成了游戏功能，你将能够玩一个完全交互式的版本的Hog!&#x20;

为了渲染图形，请确保您的计算机上安装了Python的主要图形库Tkinter。 完成这些之后，就可以从终端运行GUI了

```
python3 hog_gui.py
```

一旦你完成了这个项目，如果你完成了可选的问题12，你就可以根据你创建的最终策略来玩游戏!

```
python3 hog_gui.py -f
```

### 阶段1：模拟器

在第一阶段，您将为Hog游戏开发一个模拟器。&#x20;

#### 第0题(0 pt)

py文件使用非纯零参数函数表示dice。这些函数是非纯函数，因为它们在每次调用时可能有不同的返回值。dice.py的文档描述了项目中使用的两种不同类型的骰子：

* 骰子可以是公平的，这意味着它们产生每个可能结果的概率相等。例子:six\_sided。&#x20;
* 对于使用dice的测试函数，确定性测试dice总是通过固定的值序列循环，这些值作为参数传递给make\_test\_dice函数。

在开始编写任何代码之前，请阅读dice.py文件，并通过解锁以下测试来检查您是否理解。

```
python3 ok --local -q 00 -u
```

这会显示如下提示符：

```
=====================================================================
Assignment: Project 1: Hog
Ok, version v1.5.2
=====================================================================

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unlocking tests

At each "? ", type what you would expect the output to be.
Type exit() to quit

---------------------------------------------------------------------
Question 0 > Suite 1 > Case 1
(cases remaining: 1)

>>> test_dice = make_test_dice(4, 1, 2)
>>> test_dice()
?
```

您应该输入您期望的输出。要做到这一点，您首先需要根据上面的描述确定test\_dice将做什么。&#x20;

可以输入exit()(不带引号)退出解锁。众所周知，在Windows上键入Ctrl-C以退出解锁程序会导致问题，所以要避免这样做。

#### 第1题(2 pt)

在hog.py中实现roll\_dice函数。 它有两个参数：一个名为num\_rolls的正整数，给出要掷的骰子的数量，以及一个骰子函数。 它返回在一个回合中掷骰子的次数所获得的分数：要么是结果的总和，要么是1(狼吞虎咽)。&#x20;

要获得掷骰子的单个结果，调用dice()。 应该在roll\_dice的函数体中准确地调用dice() num\_rolls times。 记住调用dice()精确的num\_rolls次数，即使在滚动过程中发生Pig Out。 以这种方式，我们正确地模拟所有的骰子一起滚动。

**理解这个问题:**&#x20;

在编写任何代码之前，打开测试以验证您对问题的理解。

```
python3 ok --local -q 01 -u
```

**编写代码并检查你的工作：**

一旦您完成解锁，就开始实现您的解决方案。你可以用以下方法检查你的正确性:

```
python3 ok --local -q 01
```

交互式调试代码：

```
python3 -i hog.py
```

> 注意：要在失败的测试完成后启动解释器，请尝试运行python3 ok -q 01 -i。这将打开一个解释器，然后运行测试，直到第一个doctest失败。

然后，可以在任意数量的骰子上调用roll\_dice函数。roll\_dice函数为dice提供了一个默认参数值，它是一个随机的六面骰子函数。因此，下面对roll\_dice的调用模拟掷四个公平的六面骰子。

```
>>> roll_dice(4)
```

您将发现，前面的表达式在每次调用时可能会有不同的结果，因为它是在模拟随机掷骰子。您还可以使用预先确定骰子结果的测试骰子。例如，当你知道骰子会出现3和4时，掷两次应该会得到7的结果。

```
make_test_dice = make_test_dice(3,4)
> > > roll_dice(2,骰子= fixed_dice)
7
```

> 在大多数系统中，您可以通过按向上箭头，然后按enter或return来再次计算相同的表达式。如果你想要得到第二个最后，第三个最后，等等，你所做的命令，反复按向上箭头。&#x20;
>
> 如果你发现一个问题，你需要改变你的hog.py文件，保存它，退出Python，重新启动它，然后开始计算表达式。即使在重新启动Python之后，按向上箭头也可以访问以前的表达式。

继续调试代码并运行ok测试，直到它们全部通过。您应该遵循相同的过程来理解问题、实现解决方案、测试和调试这个项目上的所有问题。

#### 第2题(1 pt)&#x20;

实现free\_bacon助手函数，该函数根据对手的当前得分返回掷0个骰子获得的分数。你可以假设这个分数小于100。如果分数低于10，则假设两位数字中的第一位是0。&#x20;

在编写任何代码之前，打开测试以验证您对问题的理解：

```
python3 ok --local -q 02 -u
```

一旦您完成解锁，就开始实现您的解决方案。你可以用以下方法检查你的正确性：

```
python3 ok --local -q 02
```

如上所述，您还可以通过在终端中输入python3 -i hogg .py，然后使用各种输入调用free\_bacon来交互式地测试free\_bacon。&#x20;

#### 第3题(2 pt)

实现take\_turn函数，该函数将通过滚动给定的骰子num\_rolls次数返回每轮所获得的点数。&#x20;

你需要根据对手得分来实现免费培根规则，你可以假设这个得分小于100。&#x20;

如果可能的话，take\_turn的实现应该同时调用roll\_dice和free\_bacon。 在编写任何代码之前，打开测试以验证您对问题的理解。

```
python3 ok --local -q 03 -u
```

一旦您完成解锁，就开始实现您的解决方案。你可以用以下方法检查你的正确性：

```
python3 ok --local -q 03
```

#### 第4题(2 pt)

实现is\_swap，它根据规则返回是否应该交换分数。

&#x20;is\_swap函数接受两个参数：玩家的得分。它返回一个布尔值来指示是否满足Swine Swap条件。&#x20;

在编写任何代码之前，打开测试以验证您对问题的理解：

```
python3 ok --local -q 04 -u
```

一旦您完成解锁，就开始实现您的解决方案。你可以用以下方法检查你的正确性：

```
python3 ok --local -q 04
```

#### 第5题 (3pt)

实现了play函数，它模拟了一个完整的Hog游戏。 玩家轮流掷骰子，直到其中一名玩家达到得分。&#x20;

为了确定每轮掷出多少骰子，每个玩家使用各自的策略(玩家0使用策略0，玩家1使用策略1)。 策略是一种函数，给定玩家和对手的分数，返回当前玩家想要掷出的骰子数量。 每个策略函数每回合只能调用一次。 现在还不必担心实施策略的细节。 您将在阶段3中开发它们。&#x20;

当游戏结束时，游戏返回两位玩家的最终总分，玩家0的得分第一，玩家1的得分第二。 这里有一些提示：

* 您应该使用您已经编写的函数!您将需要使用所有三个参数调用take\_turn。&#x20;
* 每轮只呼叫一次take - turn。&#x20;
* 执行所有的特殊规则。&#x20;
* 通过调用提供的函数other，可以获得另一个玩家的号码(0或1)。&#x20;
* 你可以暂时忽略play函数的say参数。您将在项目的第2阶段使用它。

在编写任何代码之前，打开测试以验证您对问题的理解：

```
python3 ok --local -q 05 -u
```

一旦您完成解锁，就开始实现您的解决方案。你可以用以下方法检查你的正确性：

```
python3 ok --local -q 05
```

> 问题5的最后一个测试是模糊测试，它检查你的play功能是否适用于大量不同的输入。如果这个测试失败，说明有问题，但您应该查看其他测试，以确定问题可能在哪里。

一旦你完成了，你就可以玩图形版的游戏了。我们提供了一个名为hog\_gui.py的文件，你可以在终端上运行它：

```
python3 hog_gui.py
```

如果您还没有安装Tkinter (Python的图形库)，则需要先安装它，然后才能运行GUI。

> Windows用户(2019年6月):如果你没有看到名为“\_tkinter”的模块，而且你使用了我们的自动安装程序，你需要回到lab 0，按照下面的步骤，使用替代方法干净地卸载并重新安装Python和Git-Bash。 不幸的是，我们没有意识到安装程序安装的Python版本没有包含所需的Tkinter模块。 这将在自动化安装程序的更高版本中得到解决。

GUI依赖于您的实现，因此如果您的代码中有任何错误，它们将反映在GUI中。这意味着您也可以使用GUI作为调试工具;但是，最好先运行测试。&#x20;

阶段二跟阶段三直接使用英文的做了，我这边就不翻译了
