Skip to content

Commit c6af724

Browse files
committed
Commit doc
1 parent 8661fe2 commit c6af724

File tree

4 files changed

+606
-0
lines changed

4 files changed

+606
-0
lines changed

doc/pic/20191018105636428.png

37.2 KB
Loading

doc/五子棋AI算法(1).md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
之前说想写一些比较大型的算法,想了半天,还是觉得写五子棋的AI比较合适。一则因为自己研究过这个,有一些基础,二则尽管现在网上有很多五子棋AI算法的资料,但是确实都有些晦涩难懂。就想着借这个机会,凭自己的理解,尽量的讲通俗一些。然而,这个算法确实有些复杂,想要通俗一些需要较大的篇幅,一篇文章难以讲完,这里就分很多个章节,一点一点的深入探讨。为了让文章更加通俗一些,我会略去一部分很简单但是占用篇幅很长的代码,改为用几行注释说明。
2+
3+
# 框架的搭建
4+
5+
首先,我们计划是做一个五子棋AI,也就是说让玩家和这个AI对下。整个游戏的框架是这样的:
6+
7+
![五子棋游戏框架](pic/20191018105636427.png)
8+
9+
其中,棋盘是一个Object,存放当前的棋局情况,通知每个Player“轮到你下棋了”、“对方下了什么棋”、“游戏结束,XXX获胜”等消息,并且从每个Player那里获取他下了什么棋。两个Player分别是人类玩家和AI。Player的基类应该是一个interface,里面只有三个方法。人类玩家和AI是它的子类,分别实现这三个方法。
10+
11+
```java
12+
public interface Player {
13+
Point play();
14+
void display(Point p);
15+
void notifyWinner(int color);
16+
}
17+
```
18+
19+
```java
20+
public final class Point {
21+
public final int x;
22+
public final int y;
23+
public Point(int x, int y) {
24+
this.x = x;
25+
this.y = y;
26+
}
27+
@Override
28+
public String toString() {return "(" + x + "," + y + ")";}
29+
}
30+
```
31+
32+
解释一下:
33+
34+
- *play* 方法,告知“轮到你下棋了”,并且返回一个*Point*,也就是下一步下的棋。对于人类玩家,则就是阻塞,等待玩家在界面上选取一个点,并且将这个点的坐标返回。对于AI,则是直接开始用我们的AI算法进行计算,并返回计算结果。
35+
- *void display(Point p)* 方法,告知“对方下了什么棋”。对于人类玩家,则就是将对方下了的棋在界面上显示出来。对于AI,则是将对方下了的棋记在AI的缓存中,以便后续的计算。
36+
- *void notifyWinner(int color)* 方法,告知“游戏结束,XXX玩家赢了”。对于人类玩家,则就是在界面上展示谁赢了的文字及特效,并且从此之后再点击棋盘就不再有反应了。对于AI,则是通知AI不要再计算了。
37+
38+
当然了,如果打算连续下多盘棋,可能还需要一个*reset*方法,通知人类玩家和AI清空当前棋盘。当然了,这个和我们的算法关系不大就不列出来了。
39+
40+
然后,我们的*interface Player*需要两个实现类,分别叫做*HumanPlayer**RobotPlayer*,这个*RobotPlayer**play*方法将是五子棋AI算法的核心内容,后面会花费大量篇幅进行讲解。
41+
42+
接下来就是我们的棋盘:
43+
44+
```java
45+
public abstract class Constant {
46+
public static int MAX_LEN = 15;
47+
}
48+
```
49+
50+
```java
51+
public class ChessBoard {
52+
private byte[][] board = new byte[Constant.MAX_LEN][Constant.MAX_LEN];
53+
private Player[] players = new Player[2];
54+
private int whoseTurn = 0;
55+
private int count = 0;
56+
private boolean isEnd = false;
57+
58+
private boolean checkForWin(Point p) {
59+
/* 因为篇幅问题,此处省略十几行代码 */
60+
/* 这个函数就是在下完每一步棋时调用,只需要判断以这步棋若形成五连珠即可判定获胜 */
61+
return false;
62+
}
63+
64+
public void play() {
65+
if (isEnd) return;
66+
Point p = players[whoseTurn].play(); //调用Player的play方法,获取下一步下的棋
67+
if (board[p.y][p.x] != 0) //严谨,以防万一
68+
throw new IllegalArgumentException(p.toString() + board[p.y][p.x]);
69+
board[p.y][p.x] = (byte) (whoseTurn + 1);
70+
System.out.println((whoseTurn == 0 ? "" : "") + p.toString()); //打印日志
71+
if (++count == Constant.MAX_LEN * Constant.MAX_LEN) //严谨,如果棋盘下满了游戏结束
72+
isEnd = true;
73+
if (checkForWin(p)) //如果下了这步棋后赢了,游戏结束
74+
isEnd = true;
75+
whoseTurn = 1 - whoseTurn; //切换当前下棋的人
76+
players[whoseTurn].display(p); //调用Player的display方法,告知他对方下了哪步棋
77+
if (isEnd) { //如果下完这一步棋后有一方赢了,则调用Player的notifyWinner方法通知
78+
players[0].notifyWinner(2 - whoseTurn);
79+
players[1].notifyWinner(2 - whoseTurn);
80+
}
81+
}
82+
83+
public static void main(String[] args) {
84+
ChessBoard b = new ChessBoard();
85+
b.players[0] = new HumanPlayer(1); //这里我从构造函数中传入了颜色,例如1表示执黑,2表示执白
86+
b.players[1] = new RobotPlayer(2);
87+
while (b.getWinner() == null) {
88+
b.play();
89+
}
90+
}
91+
}
92+
```
93+
94+
棋盘的代码确实很简单易懂,也做了很多注释,就不多介绍了。
95+
96+
接下来,就只剩下*HumanPlayer**RobotPlayer*的实现了。
97+
98+
# 人类玩家
99+
100+
人类玩家无非就是实现三个方法:
101+
- *play* 方法,阻塞等待玩家点击棋盘上的一个点并返回这个点
102+
- *display* 方法,将AI下的棋展示在界面上
103+
- *notifyWinner* 方法,显示一行字“你赢(输)了”
104+
105+
这段代码与本文无关,就不贴出来了,我把我做的这个丑陋的界面贴出来展示一下,哈哈。
106+
107+
![丑陋的界面](pic/20191018105636428.png)
108+
109+
(不要吐槽我的界面,这不是重点。)
110+
111+
# RobotPlayer
112+
113+
AI才是重点内容,涉及了大量的算法和数学知识,博弈树、评估函数、极大极小值搜索、启发式搜索、α-β剪枝等等,将会占用大量的篇幅。从下一章开始,将对此逐一展开。

0 commit comments

Comments
 (0)