您的位置:首页 > 大数据 > 人工智能

【alpha-bet剪枝】五子棋AI

2014-06-01 15:48 615 查看
//五子棋AI算法
//加入alpha-bet剪枝

//------------------------------------------------------
//头文件包含

#include "stdio.h"

//------------------------------------------------------
//定义黑白棋手

#define white 1
#define black -1

//------------------------------------------------------
//定义数据类型

struct Space//位置
{
int row;
int list;
};
struct Reaction
{
Space space;
int power;
};
struct Limit
{
int up;
int down;
int left;
int right;
};

//------------------------------------------------------
//函数声明

int max(int x1,int x2);

int min(int x1,int x2);

bool line_win(int player,int a[],int n=9);//单线五子判断

bool win(int x[15][15],int row,int list);//五子判断

int get_line_power(int player,int a[],int n=9);//单线势分析

int get_leaf_power(int x[15][15],int row,int list);//计算叶子的势

Limit get_limit(int x[15][15],int width);//划定搜索范围

Reaction ai(int x[15][15],int player,int depth=4/*搜索深度*/,int width=1/*搜索宽度*/,int uncle_power=1000);//分析棋盘,返回最优势的下棋位置和势

//------------------------------------------------------
//函数定义

int max(int x1,int x2){return x1>x2?x1:x2;}

int min(int x1,int x2){return x1>x2?x2:x1;}

//单线五子判断
bool line_win(int player,int a[],int n)
{
int num=0;
for(int i=0;i<n-(5-1);i++)
{
num=0;
for(int j=0;j<5;j++)
if(a[i+j]==player)num++;
if(num==5)return true;
}
return false;
}

//五子判断
bool win(int x[15][15],int row,int list)
{
int line[9]={0};
int player=x[row][list];

// │ 从上到下赋值
int begin=max(0,row-4);//对应所选取数组的横坐标的开始
int end=min(14,row+4);//对应所选取数组的横坐标的结束
for(int i=begin,j=0/*数组下标*/;i<=end;i++,j++)
line[j]=x[i][list];
if(line_win(player,line,end-begin+1))return true;

// ╲ 从左上到右下赋值
begin=max(max(list-4,0),max(list-row,0));
end=min(min(list+4,14),min(list+(14-row),14));
for(int i=begin,j=0;i<=end;i++,j++)
line[j]=x[row-(list-begin)+j][i];
if(line_win(player,line,end-begin+1))return true;

// ─ 从左到右赋值
begin=max(0,list-4);
end=min(14,list+4);
for(int i=begin,j=0;i<=end;i++,j++)
line[j]=x[row][i];
if(line_win(player,line,end-begin+1))return true;

// ╱ 从左下到右上赋值
begin=max(max(list-4,0),max(list-(14-row),0));
end=min(min(list+4,14),min(list+row,14));
for(int i=begin,j=0;i<=end;i++,j++)
line[j]=x[row+(list-begin)-j][i];
if(line_win(player,line,end-begin+1))return true;

return false;
}

//单线势分析
int get_line_power(int player,int a[],int n/*数组长度*/)
{
int num=0;
int num2=0;

//***** 绝对优势:5
for(int i=0;i<n-(5-1);i++)
{
num=0;
for(int j=0;j<5;j++)
if(a[i+j]==player)num++;
if(num==5)return 5;
}

//_****_ 大优势:4
for(int i=0;i<n-(6-1);i++)
{
num=0;
for(int j=0;j<6;j++)
if(a[i+j]==player)num++;
if(num==4&&(a[i]==0&&a[i+5]==0))return 4;
}

//****_、**_** 优势:3
for(int i=0;i<n-(5-1);i++)
{
num=0;
num2=0;
for(int j=0;j<5;j++)
{
if(a[i+j]==player)num++;
if(a[i+j]==0)num2++;
}
if(num==4&&num2==1)return 3;
}

//_*_**_、__***_ 优势:2
for(int i=0;i<n-(6-1);i++)
{
num=0;
num2=0;
for(int j=0;j<6;j++)
{
if(a[i+j]==player)num++;
if(a[i+j]==0)num2++;
}
if(num==3&&num2==3&&a[i]==0&&a[i+5]==0)return 2;
}

//***_ 小优势:1
for(int i=0;i<n-(4-1);i++)
{
num=0;
num2=0;
for(int j=0;j<4;j++)
{
if(a[i+j]==player)num++;
if(a[i+j]==0)num2++;
}
if(num==3&&num2==1)return 1;
}

//_*_*_ 小优势:1
for(int i=0;i<n-(5-1);i++)
{
num=0;
for(int j=0;j<5;j++)
if(a[i+j]==player)num++;
if(num==2&&(a[i]==0&&a[i+4]==0)&&a[i+2]==0)return 1;
}

//_**_ 小优势:1
for(int i=0;i<n-(4-1);i++)
if(a[i]==0&&a[i+1]==player&&a[i+2]==player&&a[i+3]==0)return 1;

return 0;
}

//计算叶子的势
int get_leaf_power(int x[15][15],int row,int list)
{
int power;
int a[4]={0};//多线势记录
int line[9]={0};
int player=x[row][list];

int begin=max(0,row-4);
int end=min(14,row+4);
for(int i=begin,j=0;i<=end;i++,j++)// │
line[j]=x[i][list];
a[0]=get_line_power(player,line,end-begin+1);

begin=max(max(list-4,0),max(list-row,0));
end=min(min(list+4,14),min(list+(14-row),14));
for(int i=begin,j=0;i<=end;i++,j++)// ╲
line[j]=x[row-(list-begin)+j][i];
a[1]=get_line_power(player,line,end-begin+1);

begin=max(0,list-4);
end=min(14,list+4);
for(int i=begin,j=0;i<=end;i++,j++)// ─
line[j]=x[row][i];
a[2]=get_line_power(player,line,end-begin+1);

begin=begin=max(max(list-4,0),max(list-(14-row),0));
end=min(min(list+4,14),min(list+row,14));
for(int i=begin,j=0;i<=end;i++,j++)// ╱
line[j]=x[row+(list-begin)-j][i];
a[3]=get_line_power(player,line,end-begin+1);

for(int i=0;i<3;i++)//从大到小冒泡排序
for(int j=0;j<3-i;j++)
if(a[j]<a[j+1])
{
a[j]=a[j]^a[j+1];
a[j+1]=a[j]^a[j+1];
a[j]=a[j]^a[j+1];
}
if(a[0]==5)power=4*(4*4*4);
else if(a[0]==4)power=3*(4*4*4)+3*(4*4);
else
{
power=a[0]*4*4*4+a[1]*4*4+a[2]*4+a[3];
if(power>=3*(4*4*4)&&power<3*(4*4*4)+2*(4*4))power=power-4*4*4;
}

return power;
}

//划定搜索范围
Limit get_limit(int x[15][15],int width)
{
Limit limit;

//计算limit.up
for(int i=0;i<15;i++)
for(int j=0;j<15;j++)
if(x[i][j]!=0)
{
limit.up=i;
limit.left=j;
limit.right=j;
limit.down=i;
goto out;
}
out:

//计算limit.left\down
for(int i=limit.up;i<15;i++)
for(int j=0;j<15;j++)
if(x[i][j]!=0)
{
limit.down=i;
if(j<limit.left)limit.left=j;
break;
}

//计算limit.right
for(int i=limit.up;i<15;i++)
for(int j=14;j>=0;j--)
if(x[i][j]!=0&&j>limit.right)
{
limit.right=j;
break;
}

limit.up=max(limit.up-width,0);
limit.left=max(limit.left-width,0);
limit.right=min(limit.right+width,14);
limit.down=min(limit.down+width,14);

return limit;
}

//本质上是一个后序遍历
//分析棋盘,返回最优势的下棋位置和势
//使用到alpha-bet剪枝
Reaction ai(int x[15][15],int player,int depth/*搜索深度*/,int width/*搜索宽度*/,int uncle_power)
{
Reaction reaction;
reaction.space.row=8;
reaction.space.list=8;
reaction.power=-1000;

int n=0;
for(int i=0;i<15;i++)
for(int j=0;j<15;j++)
if(x[i][j]==0)n++;

//棋盘为空
if(n==15*15)
{
reaction.power=0;
return reaction;
}

//棋盘已满
if(n==0)
{
reaction.space.row=-1;
reaction.space.list=-1;
return reaction;
}

//划定搜索范围
Limit limit=get_limit(x,width);

//判断己方是否活四
for(int i=limit.up;i<=limit.down;i++)
for(int j=limit.left;j<=limit.right;j++)
{
if(x[i][j]!=0)continue;//检查落点是否为空

x[i][j]=player;//落点假设

if(win(x,i,j))
{
reaction.space.row=i;
reaction.space.list=j;
reaction.power=4*4*4*4;//赋值为绝对优势
x[i][j]=0;//落点还原
return reaction;
}
x[i][j]=0;//落点还原
}

//判断敌方是否活四
for(int i=limit.up;i<=limit.down;i++)
for(int j=limit.left;j<=limit.right;j++)
{
if(x[i][j]!=0)continue;//检查落点是否为空

x[i][j]=player*-1;//敌方落点假设

if(win(x,i,j))
{
x[i][j]=player;//己方落点假设
reaction.space.row=i;
reaction.space.list=j;
reactio
4000
n.power=ai(x,player*-1,depth-1,width,reaction.power*-1).power*-1;//根据计算情况赋值
x[i][j]=0;//落点还原
return reaction;
}
x[i][j]=0;//落点还原
}

//分支节点
if(depth>0)
{
for(int i=limit.up;i<=limit.down;i++)
for(int j=limit.left;j<=limit.right;j++)
{
if(x[i][j]!=0)continue;//检查落点是否为空

x[i][j]=player;//落点假设

int node_power=ai(x,player*-1,depth-1,width,reaction.power*-1).power*-1;

if(node_power>=uncle_power)//alpha-bet剪枝
{
reaction.space.row=i;
reaction.space.list=j;
reaction.power=node_power;
x[i][j]=0;//落点还原
return reaction;
}

if(node_power==4*4*4*4)//判断是否形成绝对优势:如果是,剪除所有兄弟节点
{
reaction.space.row=i;
reaction.space.list=j;
reaction.power=node_power;
x[i][j]=0;//落点还原
return reaction;
}

if(reaction.power<node_power)
{
reaction.space.row=i;
reaction.space.list=j;
reaction.power=node_power;
}
x[i][j]=0;//落点还原
}

return reaction;
}

//叶子节点
for(int i=limit.up;i<=limit.down;i++)
for(int j=limit.left;j<=limit.right;j++)
{
if(x[i][j]!=0)continue;//检查落点是否为空

x[i][j]=player;//落点假设

int node_power=get_leaf_power(x,i,j);

if(node_power>=uncle_power)//alpha-bet剪枝
{
reaction.space.row=i;
reaction.space.list=j;
reaction.power=node_power;
x[i][j]=0;//落点还原
return reaction;
}

if(reaction.power<node_power)
{
reaction.space.row=i;
reaction.space.list=j;
reaction.power=node_power;
}
x[i][j]=0;//落点还原
}

return reaction;
}

//测试
int main()
{
int a[15][15]=
{
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,1,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};

Reaction b=ai(a,black);
printf("(%d,%d)%d\n",b.space.row,b.space.list,b.power);//(4,6)80

return 0;
}



                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: