基于α-β剪枝算法的智能五子棋
一、基本介绍
游戏界面:使用了Java Swing进行开发,如图所示。
游戏步骤:
1. 先设置游戏的参数,可以选择模式(双人、单人、双机),智能(估值函数、估值函数+搜索树),搜索树(层数、每层节点),再开始游戏;
2. 在棋盘上单击鼠标左键,落下棋子;
3. 在棋盘上单击鼠标右键,查看该点的估值;
4. 可以显示落子顺序和悔棋;
5. 使用搜索树AI时,控制台显示搜索过程;
6. 某方胜利后,游戏结束。
二、棋型确定
问题:
1.五子棋棋型的定义十分模糊,如网上对活二、眠三、死四的定义经常自相矛盾。
2.在大部分论文中,考虑的棋型非常少,如对活三的典型棋型列举不够完整,且常把眠三当死三。
正确、完全的棋型:
以上问题,令我奔溃了很久,最后对比了N个文献,才确定了比较可信的参考,棋型整理如下:
棋型
|
定义
|
表达式
(1黑,2白,0空)
|
图例(来自五子棋贴吧)
|
长连
|
至少五颗同色棋子连在一起
|
11111
|
|
活四
|
有两个连五点(即有两个点可以形成五),图中白点即为连五点
|
011110
|
|
冲四
|
有一个连五点
|
011112
0101110
0110110
|
|
活三
|
可以形成活四的三
|
01110
010110
|
|
眠三
|
只能够形成冲四的三
|
001112
010112
011012
10011
10101
2011102
|
|
活二
|
能够形成活三的二
|
00110
01010
010010
|
|
眠二
|
能够形成眠三的二
|
000112
001012
010012
10001
2010102
2011002
|
(图不全)
|
死四
|
两头都被封堵的四
|
211112
|
(图缺)
|
死三
|
两头都被封堵的三
|
21112
|
(图缺)
|
死二
|
两头都被封堵的二
|
2112
|
(图缺)
|
棋型判断:
由于棋型比较多,判断起来不太有规律。我就直接取出某点的横、竖、撇、捺四个方向的字串,用正则表达式判断棋型了,这样的效率应该还不错。
例如对下图A点,如果放白子,可以在横向形成“20022200000”活三,如果放黑子,可以在捺向形成“00001100000”活二。
三、落子估值方式
分析:棋子落在哪里最好?
1. 需要考虑落下后会在四个方向各形成什么棋型,是否形成组合棋型,然后进行初步打分;
2. 同时还要考虑落子位置,一般同分情况下越中心的点越好;
3. 可以分析对攻击效果、防守效果、综合效果。
初步打分:
就是按照威胁程度给每种棋型打分,在理解棋型后,试验了多组估分方式,最后确定效果最好的一组打分方式如下:
棋型(含组合棋型)
|
分值
|
长连
|
100000
|
活4、双冲4、冲4活3
|
10000
|
双活3
|
5000
|
活3眠3
|
1000
|
眠4
|
500
|
活3
|
200
|
双活2
|
100
|
眠3
|
50
|
活2眠2
|
10
|
活2
|
5
|
眠2
|
3
|
死4
|
-5
|
死3
|
-5
|
死2
|
-5
|
再统计在四个方向各形成什么棋型,是否形成组合棋型,取最高分为初步打分的结果。
落子位置:
根据棋盘分布问题,越中心的点分值应当越高。
private static int[][] position = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
{ 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0 },
{ 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0 },
{ 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 3, 2, 1, 0 },
{ 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 4, 3, 2, 1, 0 },
{ 0, 1, 2, 3, 4, 5, 6, 6, 6, 5, 4, 3, 2, 1, 0 },
{ 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0 },
{ 0, 1, 2, 3, 4, 5, 6, 6, 6, 5, 4, 3, 2, 1, 0 },
{ 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 4, 3, 2, 1, 0 },
{ 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 3, 2, 1, 0 },
{ 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0 },
{ 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0 },
{ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } };
例如,对下图A点,如果放白子,在四个方向分别会形成活3、活2、眠2,形成两种棋型(组合棋型)活3和活2眠2,最高分为活3对应的200分,再加上位置分值6分,就是206分;如果放黑子,同理是25分。因此,当要在J9落下白子时,可获得攻击分206,防守分25,综合分231。
四、棋局估值函数
函数描述:
有了落子估值方式后,可以针对某一棋局,分析一定棋盘范围内的各个可落子点的落子估值,将最大综合分作为该棋局的估值。
简单智能:
利用估值函数,即可实现简单智能,即只考虑当前局面下的最佳落子点,并落子。
例如,人机对战:
优点是速度快,眼前胜利绝不放过,缺点也是显而易见的,对于埋伏了多步的杀法,这种只顾“近忧”的智能无能为力。因此,就需要搜索算法增加“远虑”。
五、搜索算法
极大极小搜索:
分析:要想得更远的,棋子落在哪里最好?
1. 在对弈中,可利用棋局估值函数对任何一个局面进行估值,估值越大表明对当前玩家越有利,估分越小则表明越不利;
2. 选择落子时,要考虑对自己第一步越有利的,对对手下一步越不利的,对自己第二步越有利的,以此类推;
3. 这样选择落子时就表现为一棵极大极小搜索树,逐层搜索,对自己轮的层就选最大值的局面,对对手轮就选最小值的局面,直到一定深度。
但测试后发现该算法速度比较慢,对15×15的棋盘,搜索第一层有15×15=225个结点,第二层就约有15×15×15×15=50625个节点,完全是指数爆炸。
α-β剪枝算法:
在搜索的过程中,实际上有搜索很多点是多余的。经过α-β剪枝,可以极大的减少搜索的数量。在查阅资料的过程中,发现α-β剪枝算法是一个基础而经典的算法,还有很多风格、很多变式,值得深入学习。
伪代码(建立在MinMax算法基础上):
alpha-beta(player,board,alpha,beta)
if(game over in current board position)
return winner
children = all legal moves for player from this board
if(max's turn)
for each child
score = alpha-beta(other player,child,alpha,beta)
(we have found a better best move....)
if score > alpha then alpha = score (cut off...)
if alpha >= beta then return alpha
return alpha (this is our best move)
else (min's turn)
for each child
score = alpha-beta(other player,child,alpha,beta)
(opponent has found a better worse move.....)
if score < beta then beta = score
(cut off....)
if alpha >= beta then return beta
return beta (this is the opponent's best move)
以上述伪代码为原型,我尝试实现五子棋AI的α-β剪枝算法。但是试验后发现该原型还有很多不足,比如速率仍然不快,尤其是开局落子时,比如经常忽视近层的绝杀,还需根据五子棋的特点继续优化算法。
六、算法优化
速率提高:
1. 限制落子范围:在当前棋局的所有棋子的最左、最右、最上、最下点的5格之内,不超过棋盘边界。这样在棋子较少的时候,搜索结点的数量大大减少。
2. 减少每层的搜索结点:在一方落子后,程序自动对新棋局下的可落子点进行落子估值,保存估值前几名的落子点,在拓展搜索树时,只考虑这些落子点作为新层结点,并进行下一步搜索,大大减少搜索范围。
近层绝杀:
在递归过程中,如果发现某落子后可能形成活4、双冲4、冲4活3这些对方无法防守的棋局,则将该落子点的估分人为设成一个无穷大值,即优先这种走法。同时,如果己方和对方同时出现活4、双冲4、冲4活3的棋局,则以己方的攻击为先。
综上,最后的α-β算法代码如下(更多详细内容,可见源代码的注解):
public int alpha_beta(int depth, Board board, int alpha, int beta) {
if (depth == level || board.isGameOver() != 0) {
Chess[] sorted = board.getSorted();
Chess move = board.getData()[sorted[0].x][sorted[0].y];
return move.getSum();// 局面估分
}
Board temp = new Board(board);
Chess[] sorted = temp.getSorted();
int score;
for (int i = 0; i < node; i++) {
int x = sorted[i].x;
int y = sorted[i].y;
if (!temp.putChess(x, y))
continue;
if (sorted[i].getOffense() >= Board.Level.ALIVE_4.score) {
score = INFINITY + 1;
} else if (sorted[i].getDefence() >= Board.Level.ALIVE_4.score) {
score = INFINITY;
} else {
score = alpha_beta(depth + 1, temp, alpha, beta);
}
temp = new Board(board);
if (depth % 2 == 0) {// MAX
if (score > alpha) {
alpha = score;
if (depth == 0) {
movex = x;
movey = y;
}
}
if (alpha >= beta)
return alpha;
} else {// MIN
if (score < beta)
beta = score;
if (alpha >= beta)
return beta;
}
}
return depth%2==0?alpha:beta;
}
例如,深度为2,每层5个结点,H8黑、I7白后考虑落下黑子,依次拓展落子估值前5大的点G7、H7、I9、I8、H6,根据深度优先搜索,先假设落下G7黑,则再次拓展5个结点I9、F6、J10、E5、K11,其中落子I9白(即共四子)后的棋局(I8是该棋局的最大落子估值点)估值为233,以此类推。根据剪枝原理,最后可得最佳落子点。
更多优化的思考:
1. 考虑利用多线程,让算法实现并行计算,每个线程负责不同子树,可提高速率;
2. 落子的影响范围有限,搜索过程中很多点的估值并没有改变,可以考虑把一些点的估值记录下来,以后只要遇到搜索到的节点,就可以直接得到结果,可提高速率;
3. 由于算法的固定性,所以一担玩家一次获胜,按照相同的走法,必然会再次获胜。但除了必杀招或者必防招,一个局面很多时候没有绝对最好的走法,而是有一些都不错的走法。考虑把这些评分差不多走法汇集起来,然后随机选择它们中的一种走法,避免走法的固定。
4. 可利用神经网络思想来提高速率,存储结点信息,增加学习功能,避免再次犯错。
七、结语
感悟:
花了约一周的时间,网上并没有满意的源码参考来作为基础,基本都是自己完成的,挺有成就感,但是也误了时间,非常抱歉!
整个过程中有几个坎,一是资料混乱,棋型很难确定,二是得弄清楚落子估值和棋局估值函数的联系和区别,这个没有找到明确描述的资料,是我自己分析猜测的,三是写完α-β算法不是万事大吉,要做个高智商的AI还很有欠缺。
参考:
(界面设计)
http://slab.sinaapp.com/gomoku/
(基本棋型、专业术语)
http://www.rifchina.com/Article/ShowArticle.asp?ArticleID=1038
http://www.rifchina.com/Article/ShowArticle.asp?ArticleID=1038
(α-β剪枝算法)
http://www.cnblogs.com/Blog_SivenZhang/archive/2010/06/13/1757677.html
http://www.cnblogs.com/speeding/archive/2012/09/20/2694704.html
http://blog.csdn.net/allenlsy/article/details/5324441
http://www.xqbase.com/computer/stepbystep3.htm
http://blog.sina.com.cn/s/blog_5d9ee55e0100uuy2.html
(相关论文)
唐永强,汪波.基于神经网络思想及α-β方法的五子棋算法设计.《电脑应用技术》二零零九总第七十二期
简越.基于UML的五子棋人机对弈.本科毕业论文
相关推荐
制作一个五子棋小游戏,实现人机对战,其中电脑在进行极大值极小值搜索时需要运用α-β剪枝算法。五子棋小游戏的核心是电脑端走步的选取,使用的方法是极大极小值搜索,并且题目要求使用α-β剪枝来提高搜索效率;除...
详细解析α-β剪枝算法过程,并且对原理进行了详细的说明。在最后用matlab代码实践了这个算法在五子棋中的应用。并且特别点名了该算法中容易犯错的地方。
基于Python实现蒙特卡洛树搜索以及极大极小+α-β剪枝算法实现五子棋AI源码.zip 基于Python实现蒙特卡洛树搜索以及极大极小+α-β剪枝算法实现五子棋AI源码.zip 基于Python实现蒙特卡洛树搜索以及极大极小+α-β剪枝...
《C++与MFC结合实现五子棋AI:基于Minimax算法与α-β剪枝》 五子棋是一款深受大众喜爱的智力游戏,而利用计算机编程实现五子棋的人工智能(AI)则需要深入理解博弈论和优化算法。本项目采用C++语言,并结合Microsoft...
《α-β剪枝算法在一字棋游戏中的应用——广工实验报告》 实验课程:人工智能 实验项目:α-β剪枝算法 实验者:毛毛 专业班级:未提供 实验日期:2017年10月22日 一、实验内容与背景 本次实验的主要任务是运用α...
本文将深入探讨如何使用αβ剪枝算法来实现一个五子棋游戏,包括游戏的基本逻辑、AI预测和用户界面的设计。 首先,五子棋是一种双人对弈的策略游戏,目标是先在棋盘上形成连续的五个棋子(横向、纵向或斜向)的玩家...
蒙特卡洛树搜索以及极大极小+α-β剪枝算法实现五子棋_gobang
2 编写程序实现极大极小+α-β 剪枝算法,作为五子棋的下棋算法 3 将两种算法进行博弈对抗,并实现下棋过程的可视化 - 不懂运行,下载完可以私聊问,可远程教学 1、该资源内项目代码都经过测试运行成功,功能ok的...
蒙特卡洛树搜索以及极大极小+α-β剪枝算法实现五子棋、Q-Learning强化学习算法走迷宫_course
安卓五子棋程序设计与实现,特别强调了α-β剪枝树算法的应用,这是一种在计算机...通过将α-β剪枝树算法应用于五子棋程序,研发者不仅能够提升程序的智能水平,还能让更多非专业棋手通过这一平台体验到五子棋的魅力。
利用经典的α-β剪枝算法对博弈树剪枝 每次搜索仅搜索落子点周围 2*2 格范围内存在棋子的位置,这样可以避免搜索一些明显无用的节点,而且可以大幅度提升整体搜索速度 避免对必胜/负局面搜索,当搜索过程中出现了...
本文将探讨如何利用α-β剪枝的极大极小值算法实现简单的五子棋游戏,以及A*算法和IDA*算法在解决迷宫问题中的应用。 首先,五子棋是一个典型的二人对弈游戏,适合使用基于搜索的决策方法。α-β剪枝极大极小值算法...
在本项目中,"Java实现基于Α-β剪枝树的智能五子棋"是一个用Java编程语言编写的五子棋游戏,它采用Α-β剪枝算法来实现AI(人工智能)玩家。Α-β剪枝是搜索算法的一个重要应用,主要用于在有限的计算时间内优化...
五子棋是世界智力运动会竞技项目之一,是一种两人对弈的纯策略型棋类游戏,是世界智力运动会竞技项目之一,通常双方分别使用黑白两色的棋子,下在棋盘直线与横线的交叉点上,先形成5子连线者获胜。 五子棋规则: ...
在人工智能领域,α β剪枝是一种优化搜索树的策略,常用于博弈树的搜索,以减少不必要的计算。这种算法在棋类游戏如国际象棋、围棋或一字棋(也称五子棋)中尤为关键,因为它们有大量可能的走法。本主题将深入探讨...
在本项目中,"αβ剪枝五子棋.zip"是一个包含了实现五子棋游戏的人工智能算法的压缩包。这个项目主要运用了人工智能领域的极大极小搜索(Minimax)和α-β剪枝策略,使得计算机能够智能地与玩家进行对弈。接下来,...
本项目开发了一个基于启发式搜索和α-β剪枝算法的AI五子棋对弈系统,旨在提供高效且具挑战性的人机对弈体验。通过精心设计的算法和优化的数据结构,本系统能够迅速准确地处理复杂的棋局,为玩家提供一个既实用又...
利用α-β剪枝算法,按照不同搜索深度,设计多个水平级别的“一字棋”游戏。 注:“一字棋”游戏(又叫“三子棋”或“井字棋”),是一款十分经典的益智 小游戏。“井字棋”的棋盘很简单,是一个 3×3 的格子,很像...