人工智能αβ剪枝实现地一字棋实验报告材料文档格式.docx
《人工智能αβ剪枝实现地一字棋实验报告材料文档格式.docx》由会员分享,可在线阅读,更多相关《人工智能αβ剪枝实现地一字棋实验报告材料文档格式.docx(15页珍藏版)》请在冰豆网上搜索。
(2)若P是MAX必胜的棋局,则e(P)=+(实际上赋了60)。
(3)若P是B必胜的棋局,则e(P)=-(实际上赋了-20)。
比如P如下图示,则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>
usingnamespacestd;
intnum=0;
//记录棋盘上棋子的个数
intp,q;
//判断是否平局
inttmpQP[3][3];
//表示棋盘数据的临时数组,其中的元素0表示该格为空,
intnow[3][3];
//存储当前棋盘的状态
constintdepth=3;
//搜索树的最大深度
voidInit(){//初始化棋盘状态
for(inti=0;
i<
3;
i++)
for(intj=0;
j<
j++)
now[i][j]=0;
//将初值均置为0
}
voidPrintQP(){//打印棋盘当前状态
i++){
for(intj=0;
cout<
<
now[i][j]<
'
\t'
;
cout<
endl;
}
}
voidplayerinput(){//用户通过此函数来输入落子的位置,比如:
用户输入31,则表示用户在第3行第1列落子。
intx,y;
L1:
请输入您的棋子位置(xy):
cin>
>
x>
y;
if(x>
0&
&
x<
4&
y>
y<
now[x-1][y-1]==0)
now[x-1][y-1]=-1;
//站在电脑一方,玩家落子置为-1
else{
非法输入!
//提醒输入错误
gotoL1;
intCheckwin()//检查是否有一方赢棋(返回0:
没有任何一方赢;
1:
计算机赢;
-1:
人赢)
{//该方法没有判断平局
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[0][2]==1))//正方行连成线
return1;
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[0][2]==-1))//反方行连成线
return-1;
return0;
intvalue(){//评估当前棋盘状态的值(同时可以用p或q判断是否平局)
p=0;
q=0;
i++){//计算机一方将棋盘中的空格填满自己的棋子,既将棋盘数组中的0变为1
j++){
if(now[i][j]==0)
tmpQP[i][j]=1;
else
tmpQP[i][j]=now[i][j];
i++)//计算共有多少连成3个1的行
p+=(tmpQP[i][0]+tmpQP[i][1]+tmpQP[i][2])/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;
i++){//人一方
//将棋盘中的空格填满自己的棋子,既将棋盘数组中的0变为-1
if(now[i][j]==0)
tmpQP[i][j]=-1;
i++)//计算共有多少连成3个-1的行
q+=(tmpQP[i][0]+tmpQP[i][1]+tmpQP[i][2])/3;
q+=(tmpQP[0][i]+tmpQP[1][i]+tmpQP[2][i])/3;
q+=(tmpQP[0][0]+tmpQP[1][1]+tmpQP[2][2])/3;
q+=(tmpQP[2][0]+tmpQP[1][1]+tmpQP[0][2])/3;
returnp+q;
//返回评估出的棋盘状态的值
intcut(int&
val,intdep,boolmax){//主算法部分,实现a-B剪枝的算法,val为上一个结点的估计值,dep为搜索深度,max记录上一个结点是否为上确界
if(dep==depth||dep+num==9)//如果搜索深度达到最大深度,或者深度加上当前棋子数已经达到9,就直接调用估计函数
returnvalue();
inti,j,flag,temp;
//flag记录本层的极值,temp记录下层求得的估计值
boolout=false;
//out记录是否剪枝,初始为false
if(max)//如果上一个结点是上确界,本层则需要是下确界,记录flag为无穷大;
反之,则为记录为负无穷大
flag=10000;
//flag记录本层节点的极值
flag=-10000;
for(i=0;
3&
!
out;
i++){//双重循环,遍历棋盘所有位置
for(j=0;
if(now[i][j]==0){//如果该位置上没有棋子
if(max){//并且上一个结点为上确界,即本层为下确界,轮到用户玩家走了。
now[i][j]=-1;
//该位置填上用户玩家棋子
if(Checkwin()==-1)//如果用户玩家赢了
temp=-10000;
//置棋盘估计值为负无穷
temp=cut(flag,dep+1,!
max);
//否则继续调用a-B剪枝函数
if(temp<
flag)//如果下一步棋盘的估计值小于本层节点的极值,则置本层极值为更小者
flag=temp;
if(flag<
=val)//如果本层的极值已经小于上一个结点的估计值,则不需要搜索下去,剪枝
out=true;
else{//如果上一个结点为下确界,即本层为上确界,轮到计算机走了。
now[i][j]=1;
//该位置填上计算机棋子
if(Checkwin()==1)//如果计算机赢了
temp=10000;
//置棋盘估计值为无穷
//否则继续调用a-B剪枝函数
if(temp>
flag)
if(flag>
=val)
now[i][j]=0;
//把模拟下的一步棋还原,回溯
if(max){//根据上一个结点是否为上确界,用本层的极值修改上一个结点的估计值
val)
val=flag;
else{
returnflag;
//函数返回的是本层的极值
intcomputer(){
intm=-10000,val=-10000,dep=1;
//m用来存放最大的val
intx_pos,y_pos;
//记录最佳走步的坐标
charch;
您希望先走吗?
(y/n)"
ch;
while(ch!
='
y'
ch!
n'
){
您希望先走吗(y/n)"
system("
cls"
);
Init();
棋盘如下:
"
PrintQP();
if(ch=='
){//计算机先走
L5:
for(intx=0;
x++){
for(inty=0;
y++){
if(now[x][y]==0){
now[x][y]=1;
cut(val,dep,1);
//计算机试探的走一步棋,棋盘状态改变了,在该状态下计算出深度为dep-1的棋盘状态估计值val
if(Checkwin()==1){
电脑将棋子放在:
x+1<
y+1<
电脑获胜!
游戏结束."
if(val>
m){//m要记录通过试探求得的棋盘状态的最大估计值
m=val;
x_pos=x;
y_pos=y;
val=-10000;
now[x][y]=0;
now[x_pos][y_pos]=1;
m=-10000;
dep=1;
x_pos+1<
y_pos+1<
num++;
value();
if(p==0){
平局!
playerinput();
//玩家走一步棋
if(p==0){
if(Checkwin()==-1){
您获胜!
gotoL5;
else{//人先走
L4:
if(q==0){
if(Checkwin()==-1){
m){
gotoL4;
intmain(){
computer();
pause"
return0;
4.主要函数
1估值函数
估价函数:
intCTic_MFCDlg:
:
evaluate(intboard[])
完成功能:
根据输入棋盘,判断当前棋盘的估值,估价函数为前面所讲:
若是MAX的必胜局,则e=+INFINITY,这里为+60
若是MIN的必胜局,则e=-INFINITY,这里为-20,这样赋值的原因是机器若赢了,则不考虑其它因素。
其它情况,棋盘上能使CUMPUTER成三子一线的数目为e1
棋盘上能使PLAYER成三子一线的数目为e2,
e1-e2作为最终权值
参数:
board:
待评估棋盘
返回:
评估结果
2.Alpha-Beta剪枝算法
AlphaBeta剪枝主函数:
intCTic_MFCDlg:
AlphaBeta(intBoard[],intDepth,intturn,intAlpha,intBeta,int*result)
根据输入棋盘,搜索深度,及其他参数,给出一个相应的最优解,存入result中。
board:
Depth:
搜索深度
turn:
当前是机器走(MAX结点)还是玩家走(MIN结点)
Alpha:
alpha值,第一次调用默认-100
Beta:
beta值,第一次调用默认+100
result:
输出结果
若当前点为MAX节点,则返回alpha值;
若当前点为MIN节点,则返回beta值
3.判断胜负
isWin(intcurPos)
根据输入棋盘,判断当前棋盘的结果,COMPUTER胜?
PLAYER胜?
平局?
-1表示:
尚未结束
0表示:
平局
1表示:
PLAYER胜
2表示:
COMPUTER胜
五、实验截图
六、实验总结
通过本次实验进一步对老师课堂上所讲的-剪枝有了更加深刻的了解,对它的一般实现有了初步的认识。
搜索深度并非越深越好,局限于估值函数是根据能够成三子一线的数目决定的,所以搜索到最后一层,如果有人胜,则出现,如果没人胜,则三子一线数目为0,所以毫无意义。
这也是为什么大多数情况下都是平局的原因。