人工智能α β剪枝实现的一字棋实验报告
实验二:利用α-β搜索过程的博弈树搜索算法编写一字棋游戏

实验二:利用α-β搜索过程的博弈树搜索算法编写一字棋游戏(3学时)一、实验目的与要求(1)了解极大极小算法的原理和使用方法,并学会用α-β剪枝来提高算法的效率。
(2)使用C语言平台,编写一个智能井字棋游戏。
(3)结合极大极小算法的使用方法和α-β剪枝,让机器与人对弈时不但有智能的特征,而且计算的效率也比较高。
二、实验原理一字棋游戏是一个流传已久的传统游戏。
游戏由两个人轮流来下,分别用“X”和“O”来代替自身的棋子。
棋盘分9个格,双方可以在轮到自己下的时候,可以用棋子占领其中一个空的格子。
如果双方中有一方的棋子可以连成一条直线,则这一方判胜,对方判负。
当所有的格子都被占领,但双方都无法使棋子连成一条直线的话,则判和棋。
这是一个智能型的一字棋游戏,机器可以模拟人与用户对弈。
当轮到机器来下的时候,机器会根据当前棋局的形势,利用极大极小算法算出一个评价值,判断如何下才对自身最有利,同时也是对方来说对不利的,然后下在评价值最高的地方。
另外利用α-β剪枝,使机器在搜索评价值的时候不用扩展不必要的结点,从而提高机器计算的效率。
在用户界面方法,用一个3×3的井字格来显示用户与机器下的结果。
当要求用户输入数据的时候会有提示信息。
用户在下的过程中可以中途按下“0”退出。
当用户与计算机分出了胜负后,机器会显示出比赛的结果,并按任意键退出。
如果用户在下棋的过程中,输入的是非法字符,机器不会做出反应。
三、实验步骤和过程1.α-β搜索过程在极小极大搜索方法中,由于要先生成指定深度以内的所有节点,其节点数将随着搜索深度的增加承指数增长。
这极大地限制了极小极大搜索方法的使用。
能否在搜索深度不变的情况下,利用已有的搜索信息减少生成的节点数呢?设某博弈问题如下图所示,应用极小极大方法进行搜索MINIMAX过程是把搜索树的生成和格局估值这两个过程分开来进行,即先生成全部搜索树,然后再进行端节点静态估值和倒推值计算,这显然会导致低效率。
人工智能实验报告

《人工智能》课外实践报告项目名称:剪枝法五子棋所在班级: 2013级软件工程一班小组成员:李晓宁、白明辉、刘小晶、袁成飞、程小兰、李喜林指导教师:薛笑荣起止时间: 2016-5-10——2016-6-18项目基本信息一、系统分析1.1背景1.1.1 设计背景智力小游戏作为人们日常休闲娱乐的工具已经深入人们的生活,五子棋更成为了智力游戏的经典,它是基于AI的αβ剪枝法和极小极大值算法实现的人工智能游戏,让人们能和计算机进行对弈。
能使人们在与电脑进行对弈的过程中学习五子棋,陶冶情操。
并且推进人们对AI的关注和兴趣。
1.1.2可行性分析通过研究,本游戏的可行性有以下三方面作保障(1)技术可行性本游戏采用Windows xp等等系统作为操作平台,使用人工智能进行算法设计,利用剪枝法进行编写,大大减少了内存容量,而且不用使用数据库,便可操作,方便可行,因此在技术上是可行的。
(2)经济可行性开发软件:SublimText(3)操作可行性该游戏运行所需配置低、用户操作界面友好,具有较强的操作可行性。
1.2数据需求五子棋需要设计如下的数据字段和数据表:1.2.1 估值函数:估值函数通常是为了评价棋型的状态,根据实现定义的一个棋局估值表,对双方的棋局形态进行计算,根据得到的估值来判断应该采用的走法。
棋局估值表是根据当前的棋局形势,定义一个分值来反映其优势程度,来对整个棋局形势进行评价。
本程序采用的估值如下:状态眠二假活三眠三活二冲四假活三活三活四连五分值 2 4 5 8 12 15 40 90 200一般来说,我们采用的是15×15的棋盘,棋盘的每一条线称为一路,包括行、列和斜线,4个方向,其中行列有30路,两条对角线共有58路,整个棋盘的路数为88路。
考虑到五子棋必须要五子相连才可以获胜,这样对于斜线,可以减少8路,即有效的棋盘路数为72路。
对于每一路来说,第i路的估分为E(i)=Ec(i)-Ep(i),其中Ec(i)为计算机的i路估分,Ep(i)为玩家的i路估分。
广工人工智能实验报告

*/
Chessboard。prototype。evaluate = function(){
//max,min权重,max连棋数,min连棋数
var maxW = minW = 0,
maxCount,minCount;
//横向计算
for (var i = 0; i < this.row; i++){
arr[i]= [];
for (var j = 0; j < column;j++) {
var sum = 0;
for (var n = 0; n〈this.column;n++) {
sum +=(this。data[i][n]*matrix。data[n][j]);
}
arr[i][j] = sum;
if(maxCount == this。row){
return Infinity;
} else{
if(minCount == this。row){
return—Infinity;
} else {
//如果没有max的棋子,则min可能连成3子
if (!maxCount){
minW++;
}
//如果没有min的棋子,则max可能连成3子
for (var j = 0, m = this。column;j < m; j++){
this。data[i][j] = Chessboard.NONE;
}
}
this。stack =[];
this.is_ended = false;
人工智能αβ剪枝实现的一字棋实验报告

人工智能αβ剪枝实现的一字棋实验报告LELE was finally revised on the morning of December 16, 2020实验5:-剪枝实现一字棋一、实验目的学习极大极小搜索及-剪枝算法实现一字棋。
二、实验原理1.游戏规则"一字棋"游戏(又叫"三子棋"或"井字棋"),是一款十分经典的益智小游戏。
"井字棋"的棋盘很简单,是一个3×3的格子,很像中国文字中的"井"字,所以得名"井字棋"。
"井字棋"游戏的规则与"五子棋"十分类似,"五子棋"的规则是一方首先五子连成一线就胜利;"井字棋"是一方首先三子连成一线就胜利。
2.极小极大分析法设有九个空格,由MAX,MIN二人对弈,轮到谁走棋谁就往空格上放一只自己的棋子,谁先使自己的棋子构成"三子成一线"(同一行或列或对角线全是某人的棋用圆圈表示MAX,用叉号代表MIN比如左图中就是MAX取胜的棋局。
估价函数定义如下设棋局为P,估价函数为e(P)。
(1)若P对任何一方来说都不是获胜的位置,则e(P)=e(那些仍为MAX空着的完全的行、列或对角线的总数)-e(那些仍为MIN空着的完全的行、列或对角线的总数)(2)若P是MAX必胜的棋局,则e(P)=+(实际上赋了60)。
(3)若P是B必胜的棋局,则e(P)=-(实际上赋了-20)。
需要说明的是,+赋60,-赋-20的原因是机器若赢了,则不论玩家下一步是否会赢,都会走这步必赢棋。
3.-剪枝算法上述的极小极大分析法,实际是先生成一棵博弈树,然后再计算其倒推值,至使极小极大分析法效率较低。
于是在极小极大分析法的基础上提出了-剪枝技术。
-剪枝技术的基本思想或算法是,边生成博弈树边计算评估各节点的倒推值,并且根据评估出的倒推值范围,及时停止扩展那些已无必要再扩展的子节点,即相当于剪去了博弈树上的一些分枝,从而节约了机器开销,提高了搜索效率。
AlphaBeta剪枝算法

AlphaBeta算法是根据Minimax算法得来的,首先我们必须明白MiniMax算法的思想。
Minimax算法常用于棋类等由两方较量的游戏和程序。
该算法是一个零总和算法,即一方要在可选的选项中选择将其优势最大化的选择,另一方则选择令对手优势最小化的方法。
而开始的时候总和为0。
但是如果实际中使用Minimax算法,由于搜索深度和可能的情况很多,算法的效率很不理想,其实并没有必要每个节点都必须搜索完毕,有些事没有必要的。
AlphaBeta算法正是为了解决这个问题。
1. 对于一个MIN节点,若能估计出其倒推值的上确界Beta,并且这个Beta值不大于MIN 的父节点(MAX节点)的估计倒推值的下确界Alpha,即Alpha≥Beta,则就不必再扩展该MIN 节点的其余子节点了,因为这些节点的估值对MIN父节点的倒推值已无任何影响了,这一过程称为Alpha剪枝。
2. 对于一个MAX节点,若能估计出其倒推值的下确界Alpha,并且这个Alpha值不小于MAX 的父节点(MIN节点)的估计倒推值的上确界Beta,即Alpha≥Beta,则就不必再扩展该MAX 节点的其余子节点了,因为这些节点的估值对MAX父节点的倒推值已无任何影响了。
这一过程称为Beta剪枝。
3. 一个MAX节点的Alpha值等于其后继节点当前最大的最终倒推值,一个MIN节点的Beta 值等于其后继节点当前最小的最终倒推值AlphaBeta剪枝算法AlphaBeta剪枝算法(假设方框表示取极大值的节点,圆圈表示取极小值的节点)B的值是18,D的值为16,而C是取极小值,由此可以判断C《=16,而A取Max(B,C),故没必要考虑C的其他子节点了。
Alphabeta的MiniMax形式,伪代码:alpha-beta(player,board,alpha,beta)if(game over in current board position) return winnerchildren = all legal moves for player from this boardif(max's turn)for each childscore = 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 alphareturn alpha (this is our best move)else (min's turn)for each childscore = 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 betareturn beta (this is the opponent's best move)AlphaBeta的递归形式01 int AlphaBeta(int depth, int alpha, int beta)02 {//如果层数为0或者已达最终状态则返回本步棋的估值03 if(depth == 0 || IsGameOver()) return Evaluate();04 for(each possible move){06 MakeMove();08 int val = -AlphaBeta(depth - 1, -beta, -alpha);09 UnMakeMove();11 if(val >= beta){13 return val;14 //注意,这里需要返回val,因为上一层应该知道具体搜索到的值,以配合各种Alpha-Beta算法的变种15 }16 if(val > alpha){18 alpha = val;19 ...20 //当然这里还需要记录这步最佳的走法21 }24 }25 return alpha;//返回最好的值26 }2728 Alpha表示MAX节点的下界值,29AlphaBeta算法的递归形式关键就是理解负号,在每一层节点中都是求最大值(例如CP-OP 的最大值,将A方的最大值返回给B,即最小化B的利益),然后返回给父节点的时候加个负号得到的就是(OP-CP),也就是双方都为对方找最差的走法。
人工智能实验报告材料

标准文档《人工智能》课外实践报告项目名称:剪枝法五子棋所在班级: 2013级软件工程一班小组成员:李晓宁、白明辉、刘小晶、袁成飞、程小兰、李喜林指导教师:薛笑荣起止时间: 2016-5-10——2016-6-18项目基本信息项目名称五子棋项目简介智力小游戏作为人们日常休闲娱乐的工具已经深入人们的生活,五子棋更成为了智力游戏的经典,它是基于AI的αβ剪枝法和极小极大值算法实现的人工智能游戏,让人们能和计算机进行对弈。
这个项目我们实现了当人点击“开始”按钮时,开始下棋,当人的棋子落时,计算机会根据算法进行最佳路径计算,然后落子下棋。
任何一方赢了都会弹出哪方赢了。
然后单击重新开始。
任务分工李晓宁 130904021 白明辉 130904001:负责界面实现和估值函数设计文档整理刘小晶 130904032 袁成飞 130904051:负责极小极大值算法的设计与实现李喜林 130904019 程小兰 130904004:负责αβ剪枝法的设计与实现一、系统分析1.1背景1.1.1 设计背景智力小游戏作为人们日常休闲娱乐的工具已经深入人们的生活,五子棋更成为了智力游戏的经典,它是基于AI的αβ剪枝法和极小极大值算法实现的人工智能游戏,让人们能和计算机进行对弈。
能使人们在与电脑进行对弈的过程中学习五子棋,陶冶情操。
并且推进人们对AI的关注和兴趣。
1.1.2可行性分析通过研究,本游戏的可行性有以下三方面作保障(1)技术可行性本游戏采用Windows xp等等系统作为操作平台,使用人工智能进行算法设计,利用剪枝法进行编写,大大减少了内存容量,而且不用使用数据库,便可操作,方便可行,因此在技术上是可行的。
(2)经济可行性开发软件:SublimText(3)操作可行性该游戏运行所需配置低、用户操作界面友好,具有较强的操作可行性。
1.2数据需求五子棋需要设计如下的数据字段和数据表:1.2.1 估值函数:估值函数通常是为了评价棋型的状态,根据实现定义的一个棋局估值表,对双方的棋局形态进行计算,根据得到的估值来判断应该采用的走法。
一字棋实验

一字棋实验实验目的通过实现人机博弈的一字棋程序,掌握人工智能的搜索原理。
理解当递归深度比较深的时候,α-β剪枝算法对计算机搜索效率的影响。
通过调整评价走步函数的参数以及算法,使得计算机的走步更加智能。
实验原理一字棋的搜索算法采用极大极小值搜索算法,算法如下:(1)将初始节点S放入OPEN表中,开始时搜索树T由初始节点构成。
(2)若OPEN表为空,则转(8)。
(3)将OPEN表中第一个节点n移出放入CLOSED表中。
(4)若n可直接判定为输、赢、或者平局,则令对应的评价函数f(n)= ∞,- ∞,和0,并转(2);否则扩展n,产生n的子节点集{n1},将{n1}放入搜索树T中。
此时,若搜索深度d(n1)小于预先设置的深度k,则将{n1}放入OPEN表的末端,转(2);否则n1达到深度k,计算f(n1),转(2)。
(5)若CLOSED表为空,则转(8);否则将CLOSED表中的第一个节点设为np。
(6)若np属于max层,且对于它的属于min层的子节点{nc1}有值,则f(np)=max{f(nc1)},并将np放入CLOSED表中;若np属于min层,且对于它的属于max层的子节点nc1的f (nc1)有值,则f(np)=min{f(nc1)},将np放入CLOSED表中。
(7)转(5)。
(8)若f(s)不空,则s有值,或者结束或者标记走步。
实际上搜索过程分为两个阶段进行:第一阶段为(2)——(4),用宽度优先法生成规定深度k的全部博弈树,然后对其所有叶子节点计算其f(p);第二阶段为(6)——(8),是由底向上利用极大极小算法逐级求端节点倒推估价值,直至求出初始节点的f(s)为止,再由f(s)选得相对较好的着法,过程结束。
等对手相应走步后,再以当前的格局作为初始节点,重复调用此过程,形成博弈树的极大极小搜索。
估价函数的定义(1)若p对任何一方来说都不是获胜的位置,则f(p)=(那些仍然为max方空着的完全的行、列、对角线的总数)- (那些仍然为min方空着的完全的行、列、对角线的总数);(2)若p为max方获胜的位置,则f(p)=∞;(在实际应用中可以定义一个相对比较大的数字)(3)若p为min方获胜的位置,则f(p)= - ∞;所谓完全的行、列、或对角线是指可能被三个同样的棋子所占满的行、列、或对角线。
人工智能 αβ剪枝

人工智能期中作业一字棋编程姓名:班级:学号:一、程序设计思想:1.通过判断一字棋的棋局是否与之前搜索过的棋局重复来减小搜索的复杂度。
(通过对称属性来判断是否重复)2.主程序采用递归的思想来解决此类复杂问题。
主程序的功能为按照αβ剪枝策略算出当前棋局的分数,依次递归。
int jianzhi(enzo a,int tier)为整个程序的关键函数。
其中enzo 是结构体类型(自定义),int tier 为层数。
递归如下:v[tier]=max(jianzhi(a,tier+1),v[tier]);(其中a每次传递之前都会被更新)。
3.如何判断是否是αβ剪枝是关键。
先用int v[4]数组来存储第0 、1、2、3层的分数。
初始值分别为-100,100,-100,100。
共有3种α剪枝情况和1中β剪枝情况。
详情见Int aorb();子函数。
二、程序源代码:#include <iostream>#include<vector>using namespace std;int jzs=0;int ajz=0,bjz=0;int v[4]= {-100,100,-100,100};class enzo{public:int a[3][3];//棋局enzo()//初始构造函数{for(int i=0; i<3; i++)for(int j=0; j<3; j++)a[i][j]=2;}void pr()//输出棋局{for(int i=0; i<3; i++){for(int j=0; j<3; j++){if(a[i][j]==1) cout<<'X'<<" ";if(a[i][j]==0) cout<<'O'<<" ";if(a[i][j]==2) cout<<". ";}cout<<endl;}}};//计算数组的静态估值int value_1(enzo a,int b){int v=0;for(int i=0; i<3; i++){for(int j=0; j<3; j++)if(a.a[i][j]==2) a.a[i][j]=b;}// a.pr();for(int i=0; i<3; i++)if(a.a[i][0]==b&&a.a[i][1]==b&&a.a[i][2]==b) v++;for(int i=0; i<3; i++)if(a.a[0][i]==b&&a.a[1][i]==b&&a.a[2][i]==b) v++;if(a.a[0][0]==b&&a.a[1][1]==b&&a.a[2][2]==b) v++;if(a.a[0][2]==b&&a.a[1][1]==b&&a.a[2][0]==b) v++;return v;}int value(enzo a){return(value_1(a,1)-value_1(a,0));}bool sym(enzo a,enzo b)//判断是否上下左右斜对称(没有考虑旋转的情况){if(a.a[0][1]==b.a[0][1]&&a.a[1][1]==b.a[1][1]&&a.a[2][1]==b.a[2][1]) //左右对称if(a.a[0][0]==b.a[0][2]&&a.a[1][0]==b.a[1][2]&&a.a[2][0]==b.a[2][2])if(a.a[0][2]==b.a[0][0]&&a.a[1][2]==b.a[1][0]&&a.a[2][2]==b.a[2][0]) return true;if(a.a[1][0]==b.a[1][0]&&a.a[1][1]==b.a[1][1]&&a.a[1][2]==b.a[1][2]) //上下对称if(a.a[0][0]==b.a[2][0]&&a.a[0][1]==b.a[2][1]&&a.a[0][2]==b.a[2][2])if(a.a[2][0]==b.a[0][0]&&a.a[2][1]==b.a[0][1]&&a.a[2][2]==b.a[0][2]) return true;if(a.a[0][0]==b.a[0][0]&&a.a[1][1]==b.a[1][1]&&a.a[2][2]==b.a[2][2]) //两个斜对称if(a.a[0][1]==b.a[1][0]&&a.a[0][2]==b.a[2][0]&&a.a[1][2]==b.a[2][1])if(a.a[1][0]==b.a[0][1]&&a.a[2][0]==b.a[0][2]&&a.a[2][1]==b.a[1][2]) return true;if(a.a[0][2]==b.a[0][2]&&a.a[1][1]==b.a[1][1]&&a.a[2][0]==b.a[2][0])if(a.a[0][0]==b.a[2][2]&&a.a[0][1]==b.a[1][2]&&a.a[1][0]==b.a[2][1])if(a.a[2][2]==b.a[0][0]&&a.a[1][2]==b.a[0][1]&&a.a[2][1]==b.a[1][0]) return true;return false;}bool nsym(enzo a,enzo b){if(sym(a,b)) return false;else return true;}int aorb()//a - 0 b -1{if(v[0]>=v[1]&&v[0]!=-100&&v[1]!=100){jzs++;cout<<jzs<<": "<<"发生a剪枝"<<endl;ajz++;return 1;}else if(v[0]>=v[3]&&v[0]!=-100&&v[3]!=100){jzs++;cout<<jzs<<": "<<"发生a剪枝"<<endl;ajz++;return 1;}else if(v[2]>=v[3]&&v[2]!=-100&&v[3]!=100){jzs++;cout<<jzs<<": "<<"发生a剪枝"<<endl;ajz++;return 1;}else if(v[1]<=v[2]&&v[1]!=100&&v[2]!=-100){jzs++;cout<<jzs<<": "<<"发生b剪枝"<<endl;bjz++;return 1;}else return 0;}int jianzhi(enzo a,int tier){//a.pr();if(tier==4) return value(a);if(tier%2)//极小层{vector<enzo> hi;for(int i=0; i<3; i++){for(int j=0; j<3; j++){if(a.a[i][j]==2){int u=0;int qq=0;a.a[i][j]=0;for(u=0; u<(int)hi.size(); u++){if(sym(hi[u],a)) break;}if((int)hi.size()==u){hi.push_back(a);v[tier]=min(jianzhi(a,tier+1),v[tier]); if(aorb()) qq=1;}a.a[i][j]=2;if(qq==1){a.pr();cout<<endl;v[tier]=100;return -100;}}}}int hj=v[tier];v[tier]=100;return hj;}Else//极大层{vector<enzo> hi;for(int i=0; i<3; i++){for(int j=0; j<3; j++){if(a.a[i][j]==2){int u=0;int qq=0;a.a[i][j]=1;for(u=0; u<(int)hi.size(); u++){if(sym(hi[u],a)) break;}if((int)hi.size()==u){hi.push_back(a);v[tier]=max(jianzhi(a,tier+1),v[tier]); if(aorb()) qq=1;}a.a[i][j]=2;if(qq==1){a.pr();v[tier]=-100;return 100;}}}}int hj=v[tier];v[tier]=-100;return hj;}}int main(){enzo a0;jianzhi(a0,0);cout<<"一共"<<ajz<<"次a剪枝"<<endl;cout<<"一共"<<bjz<<"次b剪枝"<<endl;}三、αβ剪枝搜索过程(其中’.’表示空)共发生了23次α剪枝,5次β剪枝。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
实验5:? -?剪枝实现一字棋一、实验目的学习极大极小搜索及?-?剪枝算法实现一字棋。
二、实验原理1.游戏规则"一字棋"游戏(又叫"三子棋"或"井字棋"),是一款十分经典的益智小游戏。
"井字棋" 的棋盘很简单,是一个 3×3 的格子,很像中国文字中的"井"字,所以得名"井字棋"。
"井字棋"游戏的规则与"五子棋"十分类似,"五子棋"的规则是一方首先五子连成一线就胜利;"井字棋"是一方首先三子连成一线就胜利。
2.极小极大分析法设有九个空格,由 MAX,MIN 二人对弈,轮到谁走棋谁就往空格上放一只自己的棋子,谁先使自己的棋子构成"三子成一线"(同一行或列或对角线全是某人的棋子),谁就用圆圈表示 MAX,用叉号代表 MIN比如左图中就是 MAX 取胜的棋局。
估价函数定义如下设棋局为 P,估价函数为e(P)。
(1) 若 P 对任何一方来说都不是获胜的位置,则 e(P)=e(那些仍为 MAX 空着的完全的行、列或对角线的总数)-e(那些仍为 MIN 空着的完全的行、列或对角线的总数)(2) 若 P 是 MAX 必胜的棋局,则 e(P)=+?(实际上赋了 60)。
(3) 若 P 是 B 必胜的棋局,则 e(P)=-?(实际上赋了-20)。
e(P)=5-4=1需要说明的是,+?赋60,-?赋-20的原因是机器若赢了,则不论玩家下一步是否会赢,都会走这步必赢棋。
3. ? -?剪枝算法上述的极小极大分析法,实际是先生成一棵博弈树,然后再计算其倒推值,至使极小极大分析法效率较低。
于是在极小极大分析法的基础上提出了?-?剪枝技术。
? -?剪枝技术的基本思想或算法是,边生成博弈树边计算评估各节点的倒推值,并且根据评估出的倒推值范围,及时停止扩展那些已无必要再扩展的子节点,即相当于剪去了博弈树上的一些分枝,从而节约了机器开销,提高了搜索效率。
具体的剪枝方法如下:(1) 对于一个与节点 MIN,若能估计出其倒推值的上确界?,并且这个?值不大于 MIN 的父节点(一定是或节点)的估计倒推值的下确界?,即???,则就不必再扩展该MIN 节点的其余子节点了(因为这些节点的估值对 MIN 父节点的倒推值已无任何影响了)。
这一过程称为?剪枝。
(2) 对于一个或节点 MAX,若能估计出其倒推值的下确界?,并且这个?值不小于 MAX 的父节点(一定是与节点)的估计倒推值的上确界?,即???,则就不必再扩展该 MAX 节点的其余子节点了(因为这些节点的估值对 MAX 父节点的倒推值已无任何影响了)。
这一过程称为?剪枝。
从算法中看到:(1) MAX 节点(包括起始节点)的?值永不减少;(2) MIN 节点(包括起始节点)的?值永不增加。
在搜索期间,?和?值的计算如下:(1) 一个 MAX 节点的?值等于其后继节点当前最大的最终倒推值。
(2) 一个 MIN 节点的?值等于其后继节点当前最小的最终倒推值。
4.输赢判断算法设计因为每次导致输赢的只会是当前放置的棋子,输赢算法中只需从当前点开始扫描判断是否已经形成三子。
对于这个子的八个方向判断是否已经形成三子。
如果有,则说明有一方胜利,如果没有则继续搜索,直到有一方胜利或者搜索完整个棋盘。
三、实验代码#include<iostream>using namespace std;int num=0; //记录棋盘上棋子的个数int p,q; //判断是否平局int tmpQP[3][3]; //表示棋盘数据的临时数组,其中的元素0表示该格为空,int now[3][3]; //存储当前棋盘的状态const int depth=3; //搜索树的最大深度void Init() { //初始化棋盘状态for(int i=0;i<3;i++)for(int j=0;j<3;j++)now[i][j]=0; //将初值均置为0}void PrintQP(){ //打印棋盘当前状态for(int i=0;i<3;i++){for(int j=0;j<3;j++)cout<<now[i][j]<<'\t';cout<<endl;}}void playerinput(){ //用户通过此函数来输入落子的位置,比如:用户输入3 1,则表示用户在第3行第1列落子。
int x,y;L1: cout<<"请输入您的棋子位置(x y):"<<endl;cin>>x>>y;if(x>0&&x<4&&y>0&&y<4&&now[x-1][y-1]==0)now[x-1][y-1]=-1; //站在电脑一方,玩家落子置为-1else{cout<<"非法输入!"<<endl; //提醒输入错误goto L1;}}int Checkwin() //检查是否有一方赢棋(返回0:没有任何一方赢;1:计算机赢;-1:人赢){ //该方法没有判断平局for(int i=0;i<3;i++){if((now[i][0]==1&&now[i][1]==1&&now[i][2]==1)||(now[0][i]==1&&now[1][i]= =1&&now[2][i]==1)||(now[0][0]==1&&now[1][1]==1&&now[2][2]==1)||(now[2][0]==1&&now[1][1] ==1&&now[0][2]==1)) //正方行连成线return 1;if((now[i][0]==-1&&now[i][1]==-1&&now[i][2]==-1)||(now[0][i]==-1&&now[1][i ]==-1&&now[2][i]==-1)||(now[0][0]==-1&&now[1][1]==-1&&now[2][2]==-1)||( now[2][0]==-1&&now[1][1]==-1&&now[0][2]==-1)) //反方行连成线return -1;}return 0;}int value() { //评估当前棋盘状态的值(同时可以用p或q判断是否平局)p=0; q=0;for(int i=0;i<3;i++){ //计算机一方将棋盘中的空格填满自己的棋子,既将棋盘数组中的0变为1for(int j=0;j<3;j++){if(now[i][j]==0)tmpQP[i][j]=1;elsetmpQP[i][j]=now[i][j];}}for(int i=0;i<3;i++) //计算共有多少连成3个1的行p+=(tmpQP[i][0]+tmpQP[i][1]+tmpQP[i][2])/3;for(int i=0;i<3;i++) //计算共有多少连成3个1的列p+=(tmpQP[0][i]+tmpQP[1][i]+tmpQP[2][i])/3;p+=(tmpQP[0][0]+tmpQP[1][1]+tmpQP[2][2])/3; //计算共有多少连成3个1的对角线p+=(tmpQP[2][0]+tmpQP[1][1]+tmpQP[0][2])/3;for(int i=0;i<3;i++) { //人一方//将棋盘中的空格填满自己的棋子,既将棋盘数组中的0变为-1for(int j=0;j<3;j++){if(now[i][j]==0)tmpQP[i][j]=-1;elsetmpQP[i][j]=now[i][j];}}for(int i=0;i<3;i++) //计算共有多少连成3个-1的行q+=(tmpQP[i][0]+tmpQP[i][1]+tmpQP[i][2])/3;for(int i=0;i<3;i++) //计算共有多少连成3个1的列q+=(tmpQP[0][i]+tmpQP[1][i]+tmpQP[2][i])/3;q+=(tmpQP[0][0]+tmpQP[1][1]+tmpQP[2][2])/3; //计算共有多少连成3个1的对角线q+=(tmpQP[2][0]+tmpQP[1][1]+tmpQP[0][2])/3;return p+q; //返回评估出的棋盘状态的值}int cut(int &val,int dep,bool max){ //主算法部分,实现a-B剪枝的算法,val为上一个结点的估计值,dep为搜索深度,max记录上一个结点是否为上确界if(dep==depth||dep+num==9) //如果搜索深度达到最大深度,或者深度加上当前棋子数已经达到9,就直接调用估计函数return value();int i,j,flag,temp; //flag记录本层的极值,temp记录下层求得的估计值bool out=false; //out记录是否剪枝,初始为falseif(max) //如果上一个结点是上确界,本层则需要是下确界,记录flag为无穷大;反之,则为记录为负无穷大flag=10000; //flag记录本层节点的极值elseflag=-10000;for(i=0;i<3 && !out;i++){ //双重循环,遍历棋盘所有位置for(j=0;j<3 && !out;j++){if(now[i][j]==0){ //如果该位置上没有棋子if(max){ //并且上一个结点为上确界,即本层为下确界,轮到用户玩家走了。
now[i][j]=-1; //该位置填上用户玩家棋子if(Checkwin()==-1) //如果用户玩家赢了temp=-10000; //置棋盘估计值为负无穷elsetemp=cut(flag,dep+1,!max); //否则继续调用a-B剪枝函数if(temp<flag) //如果下一步棋盘的估计值小于本层节点的极值,则置本层极值为更小者flag=temp;if(flag<=val) //如果本层的极值已经小于上一个结点的估计值,则不需要搜索下去,剪枝out=true;}else{ //如果上一个结点为下确界,即本层为上确界,轮到计算机走了。