本文只是介绍五子棋AI的实现,最终的成品只是一个
AI接口,并不包括GUI,且不依赖GUI。
五子棋 AI 的实现并不难,只需要解决一个问题就行:
怎么确定AI的最佳落子位置?
一般情况下,五子棋棋盘是由15条横线和15条纵线组合而成的,15x15 的棋盘共有 225 个交叉点,也就是说共有 225 个落子点。
假如说,AI 是黑棋,先行落子,所以 AI 总共有 225 个落子点可以选择,我们可以对每个落子点进行评估打分,哪个分高下哪里,这样我们就能确定最佳落子点了。
但这样又引出了一个新的问题:
怎么对落子点进行评估打分呢?
这就是本文的重点了,请看后文!
注:部分基础代码依赖于
lombok,请自行引入,或手写基础代码。
落子位置实体类,这里我们定义棋子类型字段:type,1表示黑子,2表示白子。
- /**
- * 棋子点位
- *
- * @author anlingyi
- * @date 2021/11/10
- */
- @AllArgsConstructor
- @NoArgsConstructor
- @ToString
- public class Point {
- /**
- * 横坐标
- */
- int x;
- /**
- * 纵坐标
- */
- int y;
- /**
- * 棋子类型 1.黑 2.白
- */
- int type;
- }
- 复制代码
AI 对外提供的接口,不会依赖任何 GUI 代码,方便其他程序调用。
- /**
- * 五子棋AI接口
- *
- * @author anlingyi
- * @date 2021/11/10
- */
- public interface AIService {
-
- /**
- * 获取AI棋位
- *
- * @param chessData 已下棋子数据
- * @param point 对手棋位
- * @param started 是否刚开局
- * @return
- */
- Point getPoint(int[][] chessData, Point point, boolean started);
-
- }
- 复制代码
这个接口需要知道我们现在的棋盘落子数据 chessData,还有对手上一步的落子位置 point,started 参数表示是否是刚开局,后续可能对刚开局情况做单独的处理。
我们创建一个类 ZhiZhangAIService,这个类实现 AIService 接口,来写我们的实现逻辑。
- /**
- *
- * 五子棋AI实现
- *
- * @author anlingyi
- * @date 2021/11/10
- */
- public class ZhiZhangAIService implements AIService {
-
- /**
- * 已下棋子数据
- */
- private int[][] chessData;
- /**
- * 棋盘行数
- */
- private int rows;
- /**
- * 棋盘列数
- */
- private int cols;
- /**
- * AI棋子类型
- */
- private int ai;
-
- /**
- * 声明一个最大值
- */
- private static final int INFINITY = 999999999;
-
- @Override
- public Point getPoint(int[][] chessData, Point point, boolean started) {
- // 初始化棋盘数据
- initChessData(chessData);
- // 计算AI的棋子类型
- this.ai = 3 - point.type;
-
- if (started) {
- // AI先下,首子天元
- int centerX = this.cols / 2;
- int centerY = this.rows / 2;
- return new Point(centerX, centerY, this.ai);
- }
-
- // 获取最佳下棋点位
- return getBestPoint();
- }
-
- /**
- * 初始化棋盘数据
- *
- * @param chessData 当前棋盘数据
- */
- private void initChessData(int[][] chessData) {
- // 获取棋盘行数
- this.rows = chessData.length;
- // 获取棋盘列数
- this.cols = chessData[0].length;
- // 初始化棋盘数据
- this.chessData = new int[this.cols][this.rows];
- // 深拷贝
- for (int i = 0; i < cols; i++) {
- for (int j = 0; j < rows; j++) {
- this.chessData[i][j] = chessData[i][j];
- }
- }
- }
-
- /**
- * 获取最佳下棋点位
- *
- * @return
- */
- private Point getBestPoint() {
- Point best = null;
- // 初始分值为最小
- int score = -INFINITY;
-
- /* 遍历所有能下棋的点位,评估各个点位的分值,选择分值最大的点位 */
- for (int i = 0; i < this.cols; i++) {
- for (int j = 0; j < this.rows; j++) {
- if (this.chessData[i][j] != 0) {
- // 该点已有棋子,跳过
- continue;
- }
-
- Point p &