Java 中的奥赛罗:第 1 部分:数据结构

你有一个大问题。有人强迫鼓励你用 Java 实现完整的奥赛罗 UI+AI,但你不知道如何做。如果你已经知道如何实现基础知识并且对更高级的策略概念感兴趣,你可能对本系列的其他部分(即将推出)感兴趣。

在这个多部分系列中,我不会提供任何标准奥赛罗任务的完整解决方案。相反,我将提供(希望)有用的提示如何开始编码并解释你的代码如何工作。

如何编辑和编译

如果你想开发像奥赛罗这样的项目(它不是很难,但也不是非常容易),你应该已经找到了编辑和编译代码的方法。**如果你在普通文本编辑器中编辑代码并执行 javac 获取 .class 文件,就在这里停下!**甚至不要为奥赛罗或更大的项目这样做。它唯一会做的就是浪费你大量的时间。相信我,你无论如何都需要这里描述的技术用于更复杂的项目,所以现在是开始使用它们的时候了。

你需要的是叫做 IDE(集成开发环境)的东西 - 本质上是一个整洁的图形用户界面,带有智能编辑器实时显示你的语法错误和一个"运行"按钮,本质上在必要时调用 javac 然后启动你的程序。IDE 有比这更高级的功能,但在这里不要管它们。

基本上有两个主要 IDE:NetBeansEclipse。除此之外的一切要么昂贵(且不一定更好),要么不适合你的目的。最终你选择哪个并不重要,但我强烈鼓励你使用 NetBeans,因为与 GUI 构建器(一个你可以点击组装图形界面而不是自己编写代码的程序)等高级工具的更好集成以及其他远远超出本文范围的原因,可能会在未来的其他文章中描述。

在 NetBeans 下载页面上,你需要下载"Java SE"版本。其他版本中包含的额外功能与奥赛罗无关。

奥赛罗棋盘

让我们看看我们应该适配到 Java 类中的基本游戏棋盘。它是一个基本的 8x8 棋盘 - 棋盘上的每个单元格在任何时间可以处于三种不同状态:

在 Java 中有多种不同的方式可以表达这三种状态。为了清晰起见,我在本文中只描述一种:枚举。如果你以前没有听说过它们,请查看Java 枚举教程。枚举是一种比整数等替代方案更清晰地表达你想要建模的内容的方式(例如你可以说 0 等于空,1 等于白,2 等于黑)。

单元格数据结构

仅根据上面关于棋盘的信息,我们可以轻松定义单元格状态的枚举:

CellState.java
public enum CellState {
        EMPTY,
        WHITE,
        BLACK
}

棋盘数据结构

现在我们有了一个表示单个单元格的简单对象类型 - 但我们如何实现 8x8 的单元格棋盘?同样,有多种不同的方法。我选择了最明显的一种:多维数组。如果你不熟悉它们,请阅读 Java 教程

在表示棋盘的新类中,我们现在可以声明我们之前定义的 CellState 枚举类型的二维数组。声明数组的最佳方式是作为你刚创建的类的实例成员

Board.java
private CellState[][] board = new CellState[8][8];

完成后,你就有了一个能够表示任何可能奥赛罗棋盘的数据结构。

如果我们想开始新游戏,我们需要正确初始化此数据结构的实例。最简单的方法是构造函数。构造函数内的代码应该类似这样:

BoardInit.java
//将所有单元格设置为空
for (int x = 0; x < 8; x++) {
    for (int y = 0; y < 8; y++) {
        board[x][y] = OthelloCellState.EMPTY;
    }
}
//设置 4 个起始棋子
board[4][3] = OthelloCellState.BLACK;
board[3][4] = OthelloCellState.BLACK;
board[3][3] = OthelloCellState.WHITE;
board[4][4] = OthelloCellState.WHITE;

使用适当的 GUI 显示使用此代码初始化的棋盘时,它应该看起来类似上图。

上面代码中要记住的一个重要方面是遍历棋盘 x 和 y 坐标的嵌套 for 循环以将所有单元格设置为空。因为数组索引从 0 开始(因此 8 大小数组的最后一个有效索引是 7),最左上角的单元格坐标为 0,0,而最右下角的单元格坐标为 7。两个循环(x 和 y 坐标)因此需要从 0 开始并在 7 结束。你肯定会在奥赛罗实现的其他部分需要上面的代码。

为了保持代码清晰(即不让自己困惑),你应该坚持使用从 0 开始的索引或从 1 开始的索引。因为在 Java 中没有简单的方法让数组使用从 1 开始的索引,我将坚持使用所谓的 0 基索引。

现在我们有了一个正确初始化的棋盘,我们可以定义棋盘类的实例方法以更轻松地访问它,例如:

BoardMethods.java
/**
 * @param x 单元格的 x 坐标(0 基)
 * @param y 单元格的 y 坐标(0 基)
 * @return 如果坐标描述的单元格为空则返回 true,否则返回 false
 */
public boolean isEmpty(int x, int y) {
    return board[x][y] == OthelloCellState.EMPTY;
}

作为练习,我强烈鼓励你自己实现以下实例方法(方法应完全按照其上方注释中描述的执行。注释格式是标准化的 Javadoc)。相信我,鉴于上面的示例,这非常简单:

BoardExercise.java
/**
 * @param x 单元格的 x 坐标(0 基)
 * @param y 单元格的 y 坐标(0 基)
 * @return 如果坐标描述的单元格包含白棋则返回 true,否则返回 false
 */
public boolean isWhite(int x, int y) {
    //你的代码放在这里
}

/**
 * @param x 单元格的 x 坐标(0 基)
 * @param y 单元格的 y 坐标(0 基)
 * @return 如果坐标描述的单元格包含黑棋则返回 true,否则返回 false
 */
public boolean isBlack(int x, int y) {
    //你的代码放在这里
}

实现这些函数后,你应该有一个可用的奥赛罗数据结构。可能你的玩家实现需要更多方法,但你可以稍后按需添加。

注意: 我最初计划将此作为多部分教程,但我从未写过第 2 部分(至少还没有)。其中一个原因是编写奥赛罗实现现在是我生活中非常不重要的一部分。


Check out similar posts by category: Java