项目 0:2048¶
MOLI:本次实验你将会学到什么
- 学习如何分解复杂问题为更小、更易管理的子问题
- 掌握Java的基本语法和数组操作
- 理解如何设计和实现游戏的核心算法
- 练习编写辅助方法来提高代码的可维护性和可读性
- 体验如何在已有代码框架中工作,这是软件工程中的常见场景
- 学习基本的测试方法
高难度模式项目¶
如果你是一名能力强的程序员,你可能会对本项目的高难度版本感兴趣。在高难度版本中,你将解决与本项目标准版本相同的问题,但没有指导,你需要自行设计。
完成高难度版本而不是标准版本没有额外加分。
常见问题¶
每个作业都会在顶部链接一个常见问题页面。你也可以通过在网址末尾添加 /faq 来访问。项目 0 的常见问题页面位于此处。
概览¶
先修要求:
在这个小项目中,你将通过创建一个可玩的 2048 游戏来练习 Java。我们已经为你实现了图形和用户交互代码,所以你的任务是实现游戏的逻辑。注意:这个版本的项目比以前版本要简单得多。如果你找到了以前学期 61B 的在线资源(例如视频),它们不适用于这个版本的项目。
如果你不熟悉 2048,你可以在此链接尝试一个演示。
有很多使用你可能没见过的 Java 语法的起始代码,但这没关系!在现实世界中,你经常会处理自己不完全理解的代码库。理想情况下,系统被设计成模块化的,这样在系统的某个部分工作的人不需要了解系统其余部分的细节。
事实上,对于这个项目,我们已经设置好了一切,你只需要打开 GameLogic.java 文件,尽管你也可以自由浏览其他文件。
使用 Git¶
重要的是你要 在频繁的间隔内 将工作提交到你的代码库。版本控制是一个强大的工具,当你搞砸了或你的狗吃掉了你的项目时,它可以拯救你,但你必须经常使用它才能发挥作用。每 15 分钟提交一次是可以的;Git 只保存更改的部分,尽管它表现得好像拍摄了整个项目的快照。
命令 git status 会告诉你自上次提交以来修改、删除或可能添加了哪些文件。它还会告诉你有多少还没有发送到你的 GitHub 代码库。
典型的命令如下:
git status # 查看有哪些文件需要添加或提交。
git add <文件或文件夹路径> # 添加或暂存任何已修改的文件。
git commit -m "提交信息" # 提交更改。使用描述性的消息。
git push origin main # 将你的本地更改反映到GitHub上,以便Gradescope可以看到它们。
然后你可以继续工作,直到准备好再次提交和推送,在这种情况下你将重复上述步骤。最好养成频繁提交并附有说明性提交信息的习惯,这样在需要恢复到旧版本的代码时,不仅可能,而且很容易。我们建议你每次添加一部分重要代码或达到某个里程碑(例如通过一个新测试)时进行提交。
2048 规则:基本规则¶
2048 在一个方格网格上进行。每个方格可以是空的或包含编号的方块。
玩家选择一个方向(使用方向键)来 倾斜 棋盘:北、南、东、西。所有方块在这个方向上滑动,直到运动方向没有空格为止。当一个方块滑动时,它可能与另一个具有相同编号的方块 合并 。你将在任务 2-7 中实现这一点。
每次两个方块合并形成一个更大的方块时,玩家会获得新方块上的分数。我们已经实现了得分追踪。
游戏开始时会随机生成一个值为 2 或 4 的方块。每次倾斜后,一个随机生成的方块将被添加到棋盘上的一个空方格中。注意,如果倾斜没有改变棋盘状态,则不会随机生成新的方块。你的代码不会添加任何新方块!我们已经为你实现了这部分。
游戏在当前玩家没有可用移动(没有倾斜可以改变棋盘)或某个移动形成一个包含 2048 的方格时结束。
如果你想亲自尝试这个游戏,欢迎在这里尝试。
设置¶
获取框架文件¶
请按照作业工作流程指南中的说明获取框架代码并在 IntelliJ 中打开。在这个项目中,我们将在 proj0/ 目录下工作。
如果遇到某种错误,请停止操作,并通过仔细阅读git WTFs解决问题,或在 OH 或 Ed 上寻求帮助。与其使用 git 命令的猜测和检查,不如这样做可以为您节省很多麻烦。如果您发现自己试图使用 Google 推荐的命令(如强制推送),请不要这样做。即使您在 Stack Overflow 上找到的帖子建议这样做,也不要使用
git push -f!如果您无法让 Git 正常工作,请将这段视频作为最后的手段来提交您的作业。
文件结构¶
proj0 文件夹分为两个 包 , game2048logic 和 game2048rendering 。虽然在 61B 中我们不会过多讨论它们,但包是一种将代码组织到不同文件夹中的方式。例如,所有图形代码都在 game2048rendering 包中,所有游戏逻辑代码都在 game2048logic 包中。您可以在下面的文件结构中看到这一点:
proj0
├── game2048logic
| ├── GameLogic.java
| ├── MatrixUtils.java
├── game2048rendering
├── Board.java
... (其他一些文件) ...
├── Main.java
├── Side.java
├── Tile.java
在整个项目中,您只需要阅读和修改
game2048logic/GameLogic.java文件。对其他文件的更改不会被 Gradescope 识别。您无需阅读
game2048rendering中的任何代码,不过如果愿意,可以随意阅读。
运行游戏¶
您可以通过运行 game2048rendering 包中的 Main.java 文件来运行您的游戏。可以通过右键单击该文件并选择 “Run ‘Main.main()’” 来执行此操作:

如果一切设置正确,您应该会得到如下图所示的内容:

现在,当您按下方向键时,您的游戏不会有任何动作,但到本项目结束时,您将拥有一个完整的 2048 实现!
任务 1:理解倾斜¶
在本项目中,您将实现倾斜棋盘的逻辑。
规则:倾斜¶

上面的动画展示了几个倾斜操作。以下是图像中显示的合并发生时的完整规则。
- 两个相同值的方块 合并 为一个包含初始数字两倍的方块。
- 作为合并结果的方块不会在该次倾斜中再次合并。例如,如果我们有[X, 2, 2, 4],其中 X 表示空格,并且我们将方块向左移动,我们应该得到[4, 4, X, X],而不是[8, X, X, X]。这是因为最左边的 4 已经是合并的一部分,因此不应再次合并。
- 当在运动方向上有三个相邻方块具有相同的数字时,运动方向上的两个前导方块合并,而尾随方块不合并。例如,如果我们有[X, 2, 2, 2]并将方块向左移动,我们应该得到[4, 2, X, X]而不是[2, 4, X, X]。
根据这些规则的推论,如果在运动方向上有四个相邻方块具有相同的数字,它们会形成两个合并后的方块。例如,如果我们有[4, 4, 4, 4],然后向左移动,我们得到[8, 8, X, X]。这是因为前导两个方块将根据规则 3 合并,然后尾随两个方块将合并,但由于规则 2,这些合并后的方块(在我们的例子中为 8)在该次倾斜中不会再次合并。
您将在上面的动画 GIF 中找到上述 3 条规则的应用,因此请多次观看以充分理解这些规则。
倾斜规则测验¶
您的任务:完成这个可选的Google Form 测验,以检查您对倾斜规则的理解。
实现倾斜¶
实现倾斜出人意料地具有挑战性。我们必须考虑四个不同的可能方向,三个不同的合并规则,等等。
计算机科学本质上只有一件事:管理复杂性。为了实现这种复杂的功能,我们需要将问题分解成更小的部分,并逐一解决。
在未来的作业中,您将需要自己找出如何将问题分解成更小的部分。对于这个项目,这里是我们决定解决倾斜问题的方法:
- 四个方向: 与其同时担心四个方向的倾斜,不如先从上方向开始。稍后,在最后一个任务(任务 6)中,我们将向您展示一个巧妙的技巧,以概括您的代码,并通过仅几行代码来处理其他三个方向。
- 关键观察: 当您将棋盘向上倾斜时,每列可以独立处理。一个列中的方块不会影响到不同列中的方块。受此观察启发,我们将编写一个 *辅助方法*用于倾斜一列。然后,要向上倾斜整个棋盘(任务 5),我们将调用该辅助方法逐一倾斜每列。
- 另一个关键观察: 当您将一列向上倾斜时,我们需要计算该列中每个方块的最终着陆方格。我们可以在一个方法中完成所有这些,但这样很快就会变得复杂。相反,让我们编写另一个 *辅助方法*用于移动单个方块。然后,要倾斜整个列(任务 4),我们将调用该辅助方法逐一移动每个方块。
- 合并规则: 在我们甚至处理合并之前,让我们尝试实现方块向上倾斜。然后,一旦方块正确向上倾斜,我们可以添加逻辑以实现合并(任务 3)。
任务 2:移动方块向上(不合并)¶
在 GameLogic.java 中,填写 moveTileUpAsFarAsPossible(int[][] board, int r, int c, int minR) 方法。不要修改该文件中的其他文件或函数。
此方法应将位置为 (r, c) 的方块尽可能向上移动。在此任务中,您应该完全忽略 minR 参数。
请记住,方块可以通过空格向上移动,直到方块到达顶行,或者方块到达一个空格,上面直接有另一个方块。
对于此任务,不必担心合并。我们将在下一个任务中添加合并逻辑。
以下是四组 前后对比 棋盘对,说明了 moveTileUpAsFarAsPossible(int[][] board, int r, int c, int minR) 函数的行为。对于每个示例,第一个表是 方法调用前 的棋盘,第二个表是 方法调用后 的棋盘。 r 和 c 的值被明确说明以便于理解这些规则。请记住,对于此任务, 合并被忽略 ,并且 也应忽略 minR 。
示例 1¶
方法调用 : moveTileUpAsFarAsPossible(board, r=3, c=0, minR=0)
之前 :
| 0 | 0 | 0 | 0 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
| 2 | 0 | 0 | 0 |
之后 :
| 2 | 0 | 0 | 0 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
位置 (3, 0) 的方块直接移动到顶行 (0, 0) 。
示例 2¶
方法调用 : moveTileUpAsFarAsPossible(board, r=2, c=1, minR=0)
之前 :
| 0 | 4 | 0 | 0 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 2 | 0 | 0 |
| 0 | 0 | 0 | 0 |
之后 :
| 0 | 4 | 0 | 0 |
|---|---|---|---|
| 0 | 2 | 0 | 0 |
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
位置 (2, 1) 的方块向上移动到 4 的正下方,即落在 (1, 1) 。
示例 3¶
方法调用 : moveTileUpAsFarAsPossible(board, r=0, c=2, minR=0)
之前 :
| 0 | 0 | 4 | 0 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
之后 :
| 0 | 0 | 4 | 0 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
位置 (0, 2) 的方块已经在顶行,因此不移动。
测试与调试¶
要测试您的方法,请在 TestTask2.java 中运行测试,方法是右键单击该文件并选择 “Run ‘TestTask2’” 。

(您也可以通过右键单击 tester2048 > “Run ‘Tests in ‘tester2048’’” 来运行整个文件夹中的所有测试。)
或者,您可以打开 TestTask2.java 文件并单击 public class TestTask2 旁边的绿色箭头来运行测试。以下是上学期的示例动画。文件略有不同,但过程相同:

您也可以以同样的方式运行单个测试。
您将在整个项目(以及课程)中以相同的方式运行所有测试!
运行提供的测试将测试上述三个示例。如果您的实现正确,所有测试都应该通过。
以下是如果您未通过测试时的错误消息示例:

在左侧,您会看到运行的所有测试列表。红色感叹号表示程序出错,黄色 X 表示我们未通过测试(程序运行但给出了错误的输出),绿色勾号表示我们通过了测试。在右侧,您将看到一些有用的错误消息。要单独查看单个测试及其错误消息,请单击左侧的测试。例如,假设我们要查看 two tiles, different values 测试。

右侧现在是此测试的独立错误消息。顶行有一个有用的消息: Boards should match ,后跟 String 表示的预期(应该发生的)和实际(您的代码生成的)棋盘。您会看到,2 留在了其位置,即使它本应该移动到 4 的下方位置,这导致了该测试失败。代码顶部的 javadoc 注释也有一些有用的信息,以防您未通过测试。您可以单击蓝色下划线的文字以查看测试内容。
您可能会遇到的一个常见错误是 ArrayIndexOutOfBoundsException 。以下是 ArrayIndexOutOfBoundsException 错误消息的示例:

ArrayIndexOutOfBoundsException 发生在我们尝试访问非法索引的值时。例如,数组 arr = [4, 2, 2, 4] 的合法索引为 0、1、2 和 3。尝试访问 arr[4] 或 arr[-1] 将抛出 ArrayIndexOutOfBoundsException 。
我们可以通过检查测试输出中提供的堆栈跟踪来评估 ArrayIndexOutOfBoundsException 发生的位置。仔细查看上一个示例:

堆栈跟踪显示了导致错误的代码执行行,最顶行是最近的。 at game2048logic.GameLogic.moveTileUpAsFarAsPossible(GameLogic.java:30) 这一行告诉我们关于错误的一些信息。首先,我们可以看到我们的 ArrayIndexOutOfBoundsException 是在 game2048logic.GameLogic 类中的 moveTileUpAsFarAsPossible 方法中触发的。 GameLogic.java:30 指定了触发错误的行。这是由 TestTask2 类中的 testOneTile 方法的第 33 行调用的,依此类推。
堆栈跟踪是调试的有用起点。您可以单击堆栈跟踪中的蓝色下划线部分以直接跳转到该行代码。
任务 3:合并方块¶
修改 moveTileUpAsFarAsPossible 方法,以便其考虑方块合并的可能性。
请记住,方块可以通过空格向上移动。当方块看到一个非空方格时,如果该格子包含另一个相同值的方块,则两个方块应合并。
如果发生合并, moveTileUpAsFarAsPossible 应返回 1 + 合并发生的行号。如果没有合并,则应返回 0。这看起来可能相当任意。我们将在任务 4 中看到这为什么有用。
与任务 2 一样,您应该忽略 minR 参数。
方法调用 : moveTileUpAsFarAsPossible(board, r=3, c=0, minR=0)
之前 :
| 2 | 0 | 0 | 0 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
| 2 | 0 | 0 | 0 |
之后 :
| 4 | 0 | 0 | 0 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
相同值的方块(2 和 2)合并成列顶部的 4。由于发生了合并,该方法应返回 1,因为合并发生在行 0。
方法调用 : moveTileUpAsFarAsPossible(board, r=2, c=0, minR=0)
之前 :
| 8 | 0 | 0 | 0 |
|---|---|---|---|
| 4 | 0 | 0 | 0 |
| 8 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
之后 :
| 8 | 0 | 0 | 0 |
|---|---|---|---|
| 4 | 0 | 0 | 0 |
| 8 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
位置 (2, 0) 的 8 无法与 (0, 0) 合并,因为中间有一个 4 。该方法应返回 0,因为没有发生合并。
方法调用 : moveTileUpAsFarAsPossible(board, r=3, c=2, minR=0)
之前 :
| 0 | 0 | 2 | 0 |
|---|---|---|---|
| 0 | 0 | 2 | 0 |
| 0 | 0 | 2 | 0 |
| 0 | 0 | 2 | 0 |
之后 :
| 0 | 0 | 2 | 0 |
|---|---|---|---|
| 0 | 0 | 2 | 0 |
| 0 | 0 | 4 | 0 |
| 0 | 0 | 0 | 0 |
底部的方块 (3, 2) 与 (2, 2) 合并,形成 4。在这一次移动中,它不能再次合并。该方法应返回 3,因为合并发生在行 2。
方法调用 : moveTileUpAsFarAsPossible(board, r=2, c=2, minR=0)
之前 :
| 0 | 0 | 2 | 0 |
|---|---|---|---|
| 0 | 0 | 2 | 0 |
| 0 | 0 | 2 | 0 |
| 0 | 0 | 4 | 0 |
之后 :
| 0 | 0 | 2 | 0 |
|---|---|---|---|
| 0 | 0 | 4 | 0 |
| 0 | 0 | 0 | 0 |
| 0 | 0 | 4 | 0 |
位置 (2, 2) 的方块与 (1, 2) 合并形成 4 。位置 (3, 2) 的方块保持为底部的 4 ,直到它可能稍后移动。该方法应返回 2,因为合并发生在行 1。
测试与调试¶
要测试您的方法,请在 TestTask3.java 中运行测试,这将测试上述示例。如果您的实现正确,所有测试都应通过。
任务 4:合并方块到达 minR¶
在 GameLogic.java 中,修改您的 moveTileUpAsFarAsPossible(int[][] board, int r, int c, int minR) 方法,使得方块不能移动到比 minR 更高的行。
注意:现在还不清楚 minR 在以后为什么会有用。您将在任务 5 中看到它的用处!
示例 1¶
方法调用 : moveTileUpAsFarAsPossible(board, r=3, c=0, minR=0)
之前 :
| 2 | 0 | 0 | 0 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
| 2 | 0 | 0 | 0 |
之后 :
| 4 | 0 | 0 | 0 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
说明 :底部的方块 (3,0) 移动到 (0,0) 并与 2 合并成 4 。由于发生了合并,返回值为 1 。
示例 2¶
方法调用 : moveTileUpAsFarAsPossible(board, r=3, c=0, minR=1)
之前 :
| 2 | 0 | 0 | 0 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
| 2 | 0 | 0 | 0 |
之后 :
| 2 | 0 | 0 | 0 |
|---|---|---|---|
| 2 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
说明 :由于 minR = 1 ,位置 (3,0) 的方块只能移动到 (1,0) 而未与位置 (0,0) 的方块合并。由于没有发生合并,该方法返回 0 。
示例 3¶
方法调用 : moveTileUpAsFarAsPossible(board, r=3, c=0, minR=3)
之前 :
| 2 | 0 | 0 | 0 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
| 2 | 0 | 0 | 0 |
之后 :
| 2 | 0 | 0 | 0 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
| 2 | 0 | 0 | 0 |
说明 :由于 minR = 3 ,位置 (3,0) 的方块不能移动,因此棋盘保持不变,并且没有合并发生。该方法应返回 0 。
测试与调试¶
要测试您的方法,请运行 TestTask4.java 中的测试。如果您的实现正确,所有测试都应通过。测试文件包含的测试比上面显示的更多。如果您未通过这些测试,您可能需要查看这些测试的代码以了解问题所在。
任务 5:倾斜列¶
现在我们已经完全实现了 moveTileUpAsFarAsPossible ,我们可以使用它来实现 GameLogic.java 中的 tiltColumn 。此函数将向上倾斜整个列。
该方法应倾斜坐标 x 处的给定列,移动该列中的所有方块到它们应在的位置,并合并该列中需要合并的任何方块。
记得使用您的 moveTileUpAsFarAsPossible 辅助方法以保持简单!考虑:您应该在什么顺序上调用此辅助方法?如何使用 moveTileUpAsFarAsPossible 的返回值来避免重复合并?
注意: moveTileUpAsFarAsPossible 的巧妙设计将使这项任务的代码相对简短简单。我的 tiltColumn 实现只有 8 行,包括括号和定义。您会在整个课程中发现,如果您有一个清晰的设计,例如 moveTileUpAsFarAsPossible 这样的辅助方法,实现系统要容易得多,因为它自然地避免了重复合并。
示例 1:无合并¶
方法调用 : tiltColumn(board, 0)
之前 :
| 0 | 0 | 0 | 0 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 4 | 0 | 0 | 0 |
| 2 | 0 | 0 | 0 |
之后 :
| 4 | 0 | 0 | 0 |
|---|---|---|---|
| 2 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
说明 :方块 4 和 2 在第 0 列中简单地向上移动而不合并。
示例 2:需要一次合并¶
方法调用 : tiltColumn(board, 1)
之前 :
| 0 | 0 | 0 | 0 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 4 | 0 | 0 |
| 4 | 4 | 4 | 4 |
之后 :
| 0 | 8 | 0 | 0 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
| 4 | 0 | 4 | 4 |
说明 :只有第 1 列被处理。位置 (2,1) 的 4 和位置 (3,1) 的 4 合并成位置 (0,1) 的 8 。第 3 行的其余部分在第 2 和 3 列中保持不变。
示例 3:相同的方块被分隔,无法通过中间方块合并¶
方法调用 : tiltColumn(board, 3)
之前 :
| 4 | 0 | 0 | 4 |
| 0 | 0 | 0 | 0 |
| 8 | 0 | 0 | 8 |
| 4 | 0 | 0 | 4 |
之后 :
| 4 | 0 | 0 | 4 |
| 0 | 0 | 0 | 8 |
| 8 | 0 | 0 | 4 |
| 4 | 0 | 0 | 0 |
说明 :只有第 3 列向上倾斜。方块尽可能向上堆叠,但位置 (0,3) 的 4 不会与位置 (2,3) 的 8 合并。同样,位置 (2,3) 和位置 (3,3) 的两个 4 也不会合并,因为其中一个实际上是 8 。
示例 4:未与合并的方块合并¶
方法调用 : tiltColumn(board, 2)
之前 :
| 0 | 0 | 2 | 0 |
| 0 | 0 | 2 | 0 |
| 0 | 0 | 4 | 0 |
| 0 | 0 | 0 | 0 |
之后 :
| 0 | 0 | 4 | 0 |
| 0 | 0 | 4 | 0 |
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
说明 :第 2 列向上倾斜。位置 (0,2) 的 2 和位置 (1,2) 的 2 合并为位置 (0,2) 的 4 。然而,生成的 4 不会与位置 (2,2) 的现有 4 在同一次移动中合并,因此它们堆叠在 0 和 1 行。
测试与调试¶
要测试您的方法,请运行 TestTask5.java 中的测试。如果您的实现正确,所有测试都应通过。
任务 6:向上倾斜¶
在 GameLogic.java 中,填写 tiltUp(int[][] board) 方法。
此方法应将整个棋盘向上倾斜,移动所有列中的所有方块到其应在的位置,并合并需要合并的任何方块。
测试与调试¶
要测试您的方法,请运行 TestTask6.java 中的测试。如果您的实现正确,所有测试都应通过。与之前的任务不同,我们在此规范中不提供任何示例。请参阅 TestTask6.java 代码以获取这些示例。
任务 7:四个方向的倾斜¶
既然我们已经为向上方向编写了 tiltUp ,我们的游戏应该可以工作,但仅在用户按上键时。我们需要实现其他三个方向。
一种可能的方法是创建另外三个函数 moveTileRightAsFarAsPossible 、 moveTileDownAsFarAsPossible 和 moveTileLeftAsFarAsPossible ,我们可以用它们来最终实现 tiltRight 、 tiltDown 和 tiltLeft 。这是个糟糕的主意。这会导致代码混乱且难以阅读,容易引入晦涩的错误,例如,如果您在一个副本中修复了某些问题,但没有在其他三个副本中修复呢?
相反,我们将利用提供的 rotateLeft 和 rotateRight 方法,旋转棋盘。
填写提供的 tilt 方法,使其能够正确处理所有四个方向。
测试与调试¶
要测试您的方法,请运行 TestTask7.java 中的测试。如果您的实现正确,所有测试都应通过。
测试文件,例如 TestIntegration.java ,测试您编写的所有内容的协调性。这样的测试称为集成测试,在测试中非常重要。单元测试在隔离中运行,而集成测试则全部一起运行,旨在捕捉由于您编写的不同功能之间的交互而导致的晦涩错误。在通过其余测试之前,请勿尝试调试 TestIntegration.java !
玩游戏¶
既然您已经实现了游戏的逻辑,您可以通过运行 Main.java 来玩游戏!
注意:如果您没有通过所有测试,游戏可能会出现异常行为或崩溃。
风格¶
从这个项目开始, 我们将强制执行风格要求 。您必须遵循风格指南,否则将受到自动评分器的处罚。
您可以并且应该使用 CS 61B 插件在本地检查您的风格。 我们不会因为未检查风格而移除速度限制。
评分概述¶
您的代码将根据我们提供的测试是否通过进行评分。没有隐藏测试;
Gradescope 将只对您的 GameLogic.java 文件进行评分。如果您编辑了其他文件,您的编辑将不会被识别,因此请不要编辑其他文件。
测试在其各自的领域是 “全有或全无” 的。如果您在测试类别中未能通过一个子测试,您将不会获得该类别的信用,尽管您可能已经通过了不同的测试用例。
以下是每个部分的权重分布:
TestTask2: 15%TestTask3: 20%TestTask4: 20%TestTask5: 8%TestTask6: 4%TestTask7: 8%TestIntegration: 25%