人工智能博弈树极大极小搜索算法alpha-beta剪枝实现五子棋,带禁手

article/2025/10/8 18:19:32

由于2020的特殊情况,导致了一个被拖了挺久的大作业。。。。
五子棋其实大家很多时候会在闲暇时刻和朋友随便玩玩,这不仅让我回忆起了高中时候摸鱼休息就喜欢和同学在自己打的格子中用铅笔来一盘五子棋,回想起来确实是至今以来最快乐的一段时光。
高考前最后一次晚自习,当时离开时好像没什么不舍,但不知是什么驱使着我在临走之前留下了这样一张照片,小小的教室里承载了许多光阴,或许我们只是这间教室匆匆岁月中的一位过客,但于我而言,这段时光却是无法取代的。
在这里插入图片描述
OK,言归正传,咱们来谈谈五子棋。

五子棋相关知识

先来看看什么是五子棋,源自百度百科——五子棋是全国智力运动会竞技项目之一,是一种两人对弈的纯策略型棋类游戏。通常双方分别使用黑白两色的棋子,下在棋盘直线与横线的交叉点上,先形成五子连线者获胜。
然后我们大家应该也都能知道一些基本棋形,但是这里我们来具体讲一讲他们的术语是什么。

  • 阳线:直线,棋盘上可见的横纵直线。

  • 阴线:斜线,由交叉点构成的与阳线成45°夹角的隐形斜线。

  • 长连:五枚以上同色棋子在一条阳线或阴线上相邻成一排。

  • 五连:只有五枚同色棋子在一条阳线或阴线上相邻成一排。

  • 活四:有两个点可以成五的四。

  • 冲四:只有一个点可以成五的四。

  • 死四:不能成五的四。

  • 活三:再走一着可以形成活四的三。

  • 连活三:连续、中间不隔空点的活三,即同色棋子在一条阳线或阴线上相邻成一排的活三。简称“连三”。

  • 跳活三:中间隔有一个空点的活三。简称“跳三”。

  • 眠三:再走一着可以形成冲四的三。

  • 死三:不能成五的三。

说到这里,其实对五子棋稍有一些了解的人,都应该能发现先手执黑的胜率异常的大,但实际上,如果没有禁手,先手执黑其实是必胜的,大家有兴趣的话可以去网上搜索一番,什么花月蒲月之类的东西。
所以我们不能避开的就是聊聊什么是禁手。
整个对局过程中黑方有禁手,白方无禁手。黑方禁手有三三禁手、四四禁手和长连禁手三种。

  • 三三禁手:黑方一子落下同时形成两个或两个以上的活三,此步为三三禁手。
  • 四四禁手:黑方一子落下同时形成两个或两个以上的四,活四、冲四、嵌五之四,包括在此四之内,此步为四四禁手。
  • 长连禁手:黑方一子落下形成连续六子或六子以上相连,此步为长连禁手。

规定:

  • 黑棋禁手判负、白棋无禁手。
  • 黑方五连与禁手同时形成,判黑方胜。

好了,五子棋的知识大致讲了出来了,那么接下来我们就来看看整个项目核心所在吧——智能。
其实说起来,大家所熟知的深蓝或者阿尔法狗之类的人工智能应该说是算力极强,甚至可以说是拿钱堆出来的,当然一些棋谱之类的东西也是不可少的,机器学习之类的。我这里只做一些简单的工作,即搜索、评估、剪枝。

评估

评估函数用以估计节点的重要性,被定义为从初始节点 S0 出发,经节点 n 到达目标节点Sg的所有路径中最小路径代价的估计值。一般形式为
f ( n ) = g ( n ) + h ( n ) f(n) = g(n) + h(n) f(n)=g(n)+h(n)
式中,g(n) 是从 S0 到节点n的实际代价,h(n) 是从节点n到目标节点 Sg 的最优路径的估计代价。
在五子棋项目中,根据评分表对节点n处的局面进行一个评分,以这个分数作为由节点n到达目标节点的最小路径代价的估计值。

搜索和剪枝

博弈算法,在极大极小值搜索中应用alpha-beta剪枝。使用这种算法,就是估计几步之内放在哪个位置最有利。根据当前局面,评估每一个可以落子的位置,看看在这儿落子后的得分怎样。极大极小值搜索就是在估计自己走的时候,选得分高的,估计别人走的时候,选得分低的,这里的低是对自己而言的,也就是假设对手选了对他最有利的位置,当然就是让我们得分最低的位置了。然后这样假设几步,选最好的。因为这样的搜索空间往往太大了,则我们需要用alpha-beta剪枝,采用深度优先搜索策略,在应用评估函数计算出叶节点的估值后,估算该叶节点祖先的估值范围,若从某节点或分枝不能得到更大利益,则剪枝,对没有必要的走步就不分析了。
这里为了便于理解附一下图(图源老师课件,如果有侵权行为请告诉我)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
OK,我们来再看看更大的树的情况
在这里插入图片描述
好了,分析一下,我们大致将项目分为业务逻辑与图形界面两部分。

棋盘

图形化界面

一个五子棋游戏,需要一个图形化界面,以允许用户在这个游戏中进行操作。其中最基础的就是一个棋盘了,所以我们需要一个相应的棋盘,对应提供人类当前棋局信息和他的落子策略的实现。根据当前大部分情况,采用15*15的棋盘,并提供重玩和执黑执白选项给玩家选择。
​ 先初始化一个UI,将一些必要的组件添加进容器中,然后将其实例化即可。

package myGoBang;import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.*;
import java.lang.*;public class UI {private JFrame frame;//五子棋游戏窗口//五子棋盘private ChessBoard chessboard = new ChessBoard();//五子棋盘//五子棋逻辑private Chess chess = new Chess();private JMenuBar menu;//菜单栏private JMenu option;//菜单栏中的OPTION菜单private JMenuItem replayOption;//OPTION下拉项中的RESTART选项,重玩一局private JMenuItem AIFirstOption;//OPTION下拉项中的WHITE选项,机器先手private JMenuItem HumanFirstOption;//OPTION下拉项中的BLACK选项,人类先手//游戏运行入口public static void main(String[] args){new UI().init();}public void init(){frame = new JFrame("GOBANG");frame.setSize(518, 565);frame.setLocationRelativeTo(null);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);menu = new JMenuBar();option = new JMenu("OPTION");menu.add(option);replayOption = new JMenuItem("RESTART");AIFirstOption = new JMenuItem("WHITE");HumanFirstOption = new JMenuItem("BLACK");option.add(replayOption);option.addSeparator();option.add(AIFirstOption);option.add(HumanFirstOption);replayOption.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {chess.init();chessboard.init();}});AIFirstOption.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {//如果此时棋盘无子,则机器执黑先行if(chessboard.isEmpty()) {chess.FIRST = -1;//机器先手,则先在中间位置下一个棋子chessboard.addChessman(7, 7, -1);chess.putChess(7, 7, -1);}elseJOptionPane.showMessageDialog(frame, "If you want to play as WHITE, please restart the game.","Message", JOptionPane.PLAIN_MESSAGE);}});HumanFirstOption.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {if(chessboard.isEmpty()){chess.FIRST = 1;}elseJOptionPane.showMessageDialog(frame, "If you want to play as BLACK, please restart the game.", "Message", JOptionPane.PLAIN_MESSAGE);}});frame.setJMenuBar(menu);frame.add(chessboard);chess.init();chessboard.init();chessboard.addMouseListener(new MouseAdapter() {@Overridepublic void mouseClicked(MouseEvent e) {play(e);}private void play(MouseEvent e) {int cellSize = chessboard.getCellSize();//每个格子的边长int x = (e.getX() - 5) / cellSize;//像素值转换成棋盘坐标int y = (e.getY() - 5) / cellSize;//像素值转换成棋盘坐标//判断落子是否合法boolean isLegal = chess.isEmpty(x, y);//如果落子合法if(isLegal){chessboard.addChessman(x, y, 1);//界面方面加一个棋子chess.putChess(x, y, 1);//逻辑业务方面加一个棋子//判断是否违反长连禁手if (chess.longLink(x, y)){JOptionPane.showMessageDialog(frame, "lose.", "Sorry, you lose.", JOptionPane.PLAIN_MESSAGE);chessboard.init();chess.init();return;}//判断人类是否胜利if(chess.isWin(x, y, 1)){JOptionPane.showMessageDialog(frame, "win!", "Congratulations, you win!", JOptionPane.PLAIN_MESSAGE);chessboard.init();chess.init();return;}//只有五连与禁手同时出现,才算胜利if(chess.ban(x, y)){JOptionPane.showMessageDialog(frame, "lose.", "Sorry, you lose.", JOptionPane.PLAIN_MESSAGE);chessboard.init();chess.init();return;}//机器落子Location loc = chess.search(x,y);chessboard.addChessman(loc);chess.putChess(loc.getX(), loc.getY(), loc.getOwner());//判断是否违反长连禁手if (chess.longLink(x, y)){JOptionPane.showMessageDialog(frame, "win!", "Congratulations, you win!", JOptionPane.PLAIN_MESSAGE);chessboard.init();chess.init();return;}//判断机器是否胜利if(chess.isWin(loc.getX(), loc.getY(), -1)){JOptionPane.showMessageDialog(frame, "lose.", "Sorry, you lose.", JOptionPane.PLAIN_MESSAGE);chessboard.init();chess.init();return;}if(chess.ban(loc.getX(), loc.getY())){JOptionPane.showMessageDialog(frame, "win!", "Congratulations, you win!", JOptionPane.PLAIN_MESSAGE);chessboard.init();chess.init();return;}}}});frame.setVisible(true);}
}

后三个类写的略有点冗余,就不献丑了,如果各位有兴趣可以去看看我上传的文件

棋子

程序中需要有一个数据结构存储棋子的信息,这些信息包括棋子坐标,棋子颜色,以及这一点的分数。这样一个数据结构的实例与图形化界面中的一个棋子对应,反映该棋子的信息。
棋子类中包含四个属性,分别表示这一棋子的横坐标、纵坐标、占据该点的棋手(人或电脑)和这一点的分数。

	private int x;//棋盘上点的横坐标0-14private int y;//棋盘上点的纵坐标0-14private int owner;//棋盘上占据该点的棋手,1表示人类,-1表示电脑,0表示空private int score;//棋盘上该点的得分

Location类中还需要有set get方法,以便获取和修改值。

棋盘

在图形化界面中需要显示一个棋盘提供给用户使用,那么在程序中就需要有一个数据结构存储棋盘的信息,这个数据结构存储的信息需要与图像化界面显示的信息实时一致。这个数据结构需要存储的基本信息包括棋盘大小,棋盘线间距,当前棋盘上哪些位置有落子以及这些棋子的颜色等信息。
棋盘类中包含五个属性,分别是棋盘大小(表示棋盘函数行数,列数)、棋盘背景色、棋盘线条色、已落子集合和棋盘边线距离。

    public static final int Chessboard_Size = 15;//棋盘大小为15行15列private Color backgroundColor = new Color(255, 245, 186);//棋盘背景色private Color lineColor = new Color(66, 66, 66);//棋盘线条色private ArrayList<Location> LocationList = new ArrayList<>();//棋盘上已落子的集合private int edge = 20;//棋盘边缘到棋盘第一条线的距离

类中还有若干方法,不一一赘述了。

  • init():初始化棋盘
  • paint():绘制界面
  • paintChessboard():绘制棋盘

智能

评估函数

五子棋游戏当中需要有一个评估函数,这个评估函数可以对于当前的每一个空点进行评估,然后赋给这一点一个分数。一个点得分越高,那么电脑在这一点落子能获得的利益就越大,相应的玩家获得的利益就越小。这个评估函数用来指导电脑面对一个特点的棋局该如何下棋,简单来讲就是电脑应该选择分数最高的空点落子,这样对于电脑越有利,也就是电脑越有可能赢。
我们选择了一种叫做五元组评分法的评估方法。五子棋要赢,必然要有五个棋子连成一线,那么我们就可以计算棋盘中每一个五格相连的线,以下称为五元组。一般情况下棋盘是15*15的,那么应该是572个五元组。针对五元组中黑子和白子的数量(可以不考虑相对位置)的不同,给该五元组评不同的分。然后棋盘上每一个位置的得分就是包含这个位置的所有五元组的得分之和。
​棋盘上的一个五元组可以是横向的、纵向的、右上左下的或左上右下的。评估函数需要遍历棋盘上所有五元组,计算出每个五元组的分数,然后将该分数加到组成五元组的每一个点的分数上。
写几个for循环即可,不再赘述。

搜索

这个游戏中还需要进行搜索,电脑需要针对当前局面向前搜索几层,比较各种情况,然后针对搜索后能得到最好的结果选择落子位置。相比与只面对当前棋局选择落子位置,在五子棋当中引入搜索后电脑面对棋局考虑的更多,也更远,这样做棋力能够得到明显的提升。

剪枝

全局搜索影响搜索速度,因为当部分情况下没有必要搜索全棋盘,而且新一局落子往往不影响距离它较远的位置分数,所以这里我们不仅是用了alpha-beta剪枝,还使用了在每层展开时对应只展开分数前几名的情况。这样就可以进一步提升搜索速度。如图,对应极大极小搜索(仅为示意图,搜索节点比这个多),其中红色为人类落子蓝色为机器落子。对应第五层每个节点都会评估一次返回一个最大值给人类层,然后选取最小的一个作为人类的策略返回给第四层,但是第四层其他节点在搜索第五层时若遇到一个比前兄弟节点小的枝就剪掉,因为没必要,人类会选择对机器来说分数小的而机器是不希望走到这样的节点状态的。同理在向上,第二层节点在搜索第三层时,若遇到比前兄弟节点大的枝也剪掉,因为机器会选对他来讲最优的局面,而人类是不希望走到这样的节点状态的。
在这里插入图片描述

防守

因为在进行评估的分数,我们希望机器的攻杀性能更优一些,所以调高了机器棋子的分数,所以在有一些情况下,机器会忽略掉防守,而去进攻。所以在这样的情况下,我们需要人为的去判断这一步杀棋是否会滞后于对方的杀棋,所以需要判断这一步是否需要防守,对于机器执白只需判断黑棋是否能胜利或活三等;而对于机器执黑还需要判断白棋是否能形成双三等无法围堵的状态。若果需要防守,就要放弃杀棋而去防守。

禁手

判断棋形,看有无违反禁手规则的情况发生。

实现结果

因为只是由搜索评估算法搞定的,没有机器学习等,所以棋力并没有特殊的强,但是绝对是有的,并不是随随便便就能下赢。

执黑胜利

在这里插入图片描述

执白胜利

在这里插入图片描述

执黑触发禁手失利

在这里插入图片描述

胜利提示

在这里插入图片描述

失利提示

在这里插入图片描述

不足

事实上,我们寻找尝试了许多可以使用的评分标准,经过试验我们选择了其中一个较为良好的评分方法,但理论上应该还存在更好的评分方法,使用更好的评分方法还能进一步的提升棋力。而且可以使用一些经典开局之类的固定套路,使得早期开局较为合理。也可以进行一些随机化的算法,防止人类在赢了一次后利用同样的方式再次胜利。
同时,在搜索时,我们进行了一些限定来加速搜索,例如在叶节点进行局面评估时,只针对最新下棋的一点周围的方格评估,这是因为考虑到落子肯定是对这附近的一些局面产生影响,而对远处的局面影响较小;例如alpha-beta剪枝策略等等。但事实上还有一些可以考虑的方法来提高搜索速度。其实也还可以利用多核技术,利用多个线程,让算法实现并行计算,提高AI的速度。我们在第一层用一个线程分配器把第二层的候选节点分配给多个线程,每个线程包含着从第二层一个候选节点开始的搜索,然后等所有线程结束后,将所有线程的结果进行汇总,选出最大值。

最后的最后,也许是我们的结果略有问题,在某些时候,当人类某一步棋没体现出较强杀伤力的时候,机器也许因为思考很深,下在一个令人无法理解的位置,还有改正空间。

好了,这就是全部内容了,非常感谢我的队友兼队长的鼎力相助,才能完成这样一个项目吧,感恩感恩。


http://chatgpt.dhexx.cn/article/jRbYRRru.shtml

相关文章

基于Python的人机交互的五子棋博弈树搜索

1. 算法原理 1.1 博弈树 博弈树针对的是二人零和博弈的问题&#xff0c;二人轮流行动&#xff0c;行动时令自己的优势最大。二人零和博弈有如下特点&#xff1a; 确定性&#xff1a;二人的行动有多种选择&#xff0c;但最终的行动是确定的信息完备性&#xff1a;博弈双方知道…

C++实现基于博弈树的5x5一子棋人机对战

基于博弈树的5x5一子棋人机对战 919106840637实验2 这是智能计算三个课程实验的第二个实验&#xff0c;即博弈树搜索 。我之前对博弈树的了解不多&#xff0c;所以实现起来比较的简略&#xff0c;仅仅是基本达到了要求 实验语言 C 实验内容 实践博弈树搜索——“5x5格子的一…

人工智能作业——搜索树博弈树一阶逻辑表达式CNF范式

1. 人工智能定义 1. 简述什么是人工智能 人工智能可分为两个维度:一个维度是从思维推理过程到行为结果(过程与结果);另一个维度是与人类表现的逼真度到数学与工程结合后的精确性(主观与客观)。 像人—样行动&#xff1a;图灵测试的途径&#xff1b;像人—样思考&#xff1a;认…

AlphaBeta剪枝算法求解博弈树最优选择 头歌实验平台

AlphaBeta剪枝算法求解博弈树最优选择 头歌实验平台 前言一、AlphaBeta剪枝是什么&#xff1f;1.由来, 最大最小决策树2.发展3. AlphaBeta剪枝 二、实验算法伪代码三、实验算法代码四、实验结果在这里插入图片描述 五、感谢 前言 产生本文的缘由 人工智能原理课程 可选实验 “…

并行博弈树搜索算法-第3篇 优秀的园丁:Alpha-Beta算法

3.1 Alpha-Beta算法 虽然博弈树的状态是有限的,但是状态个数却非常多.假设博弈树的深度为d,每个结点有b个分支,即分支因子&#xff08;branchingfactor&#xff09;为b,那么使用Min-Max方法搜索这个博弈树需要搜索个结点. 然而幸运的是,Min-Max方法的一些搜索是没有必要的,…

利用α-β搜索的博弈树算法编写一字棋游戏 python

游戏规则 “一字棋"游戏&#xff08;又叫"三子棋"或"井字棋”&#xff09;&#xff0c;是一款十分经典的益智小游戏。“井字棋"的棋盘很简单&#xff0c;是一个 33 的格子&#xff0c;很像中国文字中的"井"字&#xff0c;所以得名"井字…

博弈树 极小极大分析法

一、博弈概述 博弈特点&#xff1a; &#xff08;1&#xff09;博弈的初始格局是初始节点 &#xff08;2&#xff09;在博弈树中&#xff0c;“或”和“与”是交替出现的。自己一方的扩展节点是“或”关系&#xff0c;对方扩展的节点是“与”关系。双方轮流扩展节点。 &…

第6-2课:决策树、博弈树和行为树

在以各种“XX学习”为代表的人工智能技术普及之前,游戏里常见的角色 AI 都是各种预设的行为逻辑,比如博弈树和行为树,当然也会用到各种专家知识库。当这些预设的行为逻辑足够复杂的时候,往往会让游戏玩家觉得游戏里的人物很“智能”。从本质上来说,这些都还不算是真正的 A…

并行博弈树搜索算法-第4篇 更上一层楼:Alpha-Beta算法的改进

在Alpha-Beta算法被广泛运用后,对该算法的很多改进算法也相继被提出.这些改进算法主要在以下几个方面对Alpha-Beta算法进行改进[7]: 1. 择序&#xff08;ordering&#xff09;.在搜索博弈树时,内部结点有多个可能的移动.择序指的是搜索这些分支的顺序.择序影响着搜索叶结点的个…

博弈树的启发式搜索

什么是博弈树 在博弈过程中, 任何一方都希望自己取得胜利。因此,当某一方当前有多个行动方案可供选择时, 他总是挑选对自己最为有利而对对方最为不利的那个行动方案。 此时,如果我们站在A方的立场上,则可供A方选择的若干行动方案之间是“或”关系, 因为主动权操在A方手里,他或…

博弈树 α-β剪枝

由于搜索的复杂度有点高&#xff0c;所以在树上减少计算量肯定是剪枝了&#xff0c;这里我们把剪枝的办法称作的&#xff1a;α-β剪枝 我们在前面的文章中谈到&#xff0c;当第一次运作的是A&#xff0c;则所有的奇数深度的节点都是A做的选择&#xff0c;所有偶数深度的节点都…

博弈树优化

前言:   对弈类游戏的智能算法, 网上资料颇多, 大同小异. 然而书上得来终觉浅, 绝知此事要躬行. 结合了自己的工程实践, 简单汇总整理下. 一方面是对当年的经典<<PC游戏编程(人机博弈)>>表达敬意, 另一方面, 也想对自己当年的游戏编程人生做下回顾.   承接上两…

博弈树中关于α-β剪枝树要点

目录 一&#xff0c;α-β剪枝树搜索方法&#xff1a;深度优先&#xff08;DFS&#xff09;&#xff0c;一般从博弈树最左边开始一直搜索到最右边。 人工智能导论复习&#xff1a;| 一&#xff0c;α-β剪枝树搜索方法&#xff1a;深度优先&#xff08;DFS&#xff09;&#…

(只此一篇便绝b能懂的)五子棋AI算法原理,博弈树、极大极小搜索、αβ剪枝

我在最近撰写五子棋AI程序设计报告时&#xff0c;翻阅了很多的资料博客&#xff0c;但却发现大佬们的博客&#xff0c;没有一篇是能让我只看它就能理解全部的AI算法。在看了众多博客后&#xff0c;我终于对博弈树、极大极小搜索、αβ剪枝恍然大悟&#xff0c;其实这些看似高大…

五子棋智能算法-博弈树算法思想详解(一)

学习这个算法之前必会链表 关于链表看这两篇博文 https://blog.csdn.net/viafcccy/article/details/84502334 https://blog.csdn.net/viafcccy/article/details/85041942 在五子棋下棋中 我们最容易想到的算法就是对于棋局的推演 从而找到一种最佳的情况去使棋局向这个方向发…

基于博弈树的五子棋 AI 算法及其 C++ 实现

基于博弈树的五子棋 AI 算法及其 C 实现 摘要一 五子棋的游戏规则二 五子棋对弈的算法描述2.1 博弈树搜索算法2.2 α ─ β 剪枝2.3 估价函数 三 五子棋对弈的算法实现3.1 Node类3.1.1 成员变量3.1.2 成员函数 3.2 GameTree类3.2.1 成员变量3.2.2 成员函数 四 五子棋对…

博弈与博弈树

博弈与博弈树 博弈 博弈双方根据事先制定的规则&#xff0c;轮流交替在对应的棋局上做出自己的选择&#xff0c;然后根据规则判定那一方获胜。 博弈树&#xff08;一种特殊的与或树&#xff09; 目标&#xff1a;将当前棋局作为根节点&#xff0c;选出最有利于自己获胜的一步…

博弈树搜索算法

即使满腹经纶&#xff0c;但没有好的口才来授课&#xff0c;也会让学生听得昏昏欲睡、不知所云呢&#xff01;即使满腔热血&#xff0c;没有好的口才来凝聚共识&#xff0c;也会让这份理想温暖黯淡无光。但是&#xff0c;好的说话之道&#xff0c;也要有一颗赤诚的心、诚恳的情…

博弈树-BIT

博弈树-BIT 下棋属于一种博弈游戏&#xff0c;博弈过程可以用树&#xff08;博弈树&#xff09;来表示。假设游戏由两个人&#xff08; A 和 B &#xff09;玩&#xff0c;开始由某个人从根结点开始走&#xff0c;两个人轮流走棋&#xff0c;每次只能走一步&#xff0c; 下一步…

第四章 博弈树game tree

这里写目录标题 perfect-information game从博弈树得到收益表subgamebackward induction 反向推导一个值得思考的例子&#xff1a; 另一个例子umperfect information extensive混合策略和行为策略&#xff08;mexed and behavioral strategies)不完美信息博弈的求解 博弈树用于…