八数码游戏(启发式搜索A*算法)

八数码难题   
   3×3九宫棋盘,放置数码为1 - 88个棋牌,剩下一个空格,只能通过棋牌向空格的移动来改变棋盘的布局。
求解的问题——给定初始布局(即初始状态)和目标布局(即目标状态),如何移动棋牌才能从初始布局到达目标布局 
解答路径——就是一个合法的走步序列 
用宽度优先搜索方法解决该问题:
 为问题状态的表示建立数据结构:3×3的一个矩阵,
* 矩阵元素S ij∈{0,1,…,8};其中1≤i,j≤3,
* 数字0指示空格,
* 数字1 - 8指示相应棋牌。
   初始状态S0:            2     3      目标状态Sg: 1  2  3  
                          1  8  4                  8  0  4  
                          7  6  5                  7  6  5
                          制定操作算子集:
* 直观方法——为每个棋牌制定一套可能的走步:左、上、右、下四种移动。这样就需32个操作算子。
*简易方法——仅为空格制定这4种走步,因为只有紧靠空格的棋牌才能移动。
* 空格移动的唯一约束是不能移出棋盘。 
****************************************      A*算法应用     *****************************************************
    估价函数:f(n)=d(n)+w(n)         
其中:d(n)为n的深度       w(n)为不在位的棋子数
根据f(n)的大小全局择优搜索
 取h(n)=w(n),则有w(n)≤h*(n),h(n)满足A*算法的限制条件
 在编程中注意:
 1:深度和前驱节点的记录。(队列队首元素和队尾元素的下标的作用。)
 2:采用宽度优先搜索,再根据A*算法,从open表中选出相对函数值最小的节点进行拓展。
 注意深度优先搜索不适合,可能选择的一个分支到最后无解。
 3:递归算法应用


*/
以下是代码:
#include 
#include
#define N 1000
using namespace std;
int sum=0;
int head=0,step=0;
int m=0,ta=0;
int zuixiao[N];
char cdir[4]={'u','r','d','l'};// 方向对应字母 
char opcdir[4]={'d','l','u','r'};//反方向对应字母 
const int aim_state[9]={1,2,3,8,0,4,7,6,5};//目标状态
const int aim_loc[9]={4,0,1,2,5,8,7,6,3};//每个数字的目标位置
struct bashuma
{
    int ccstate[9];
    int a;
    char fx;
    int flag;
    int pre;
    int cengshu;
};
inline int Abs(int a)
{
    return a>0?a:-a;//绝对值函数 
}
inline int Manh(int loc1,int loc2) 
{
    return Abs(loc1/3-loc2/3)+Abs(loc1%3-loc2%3);//各列与各行之间的差的和即为该点距离它们的目标位置和为估价值
}
inline int h(int state[9])//以1—8的点的位置距离它们的目标位置和为估价值 
{
    int ans=0;
    for(int i=0;i<9;i++)
    if(state[i])
    ans+=Manh(i,aim_loc[state[i]]);
    return ans;
}
inline int check(int state[9])//比较是否成功 
{
    for(int i=0;i<9;i++)
    if(state[i]!=aim_state[i])
        return 0;
    return 1;
}
inline bool canMove(int loc,int dir,int&nxt)//判断空格在loc位置时能否向dir方向移动,同时返回移动后的位置nxt 
{
    switch(dir)
    {
        case 0:
            nxt=loc-3;    //上 
            return loc>2;
        case 1:
            nxt=loc+1;    //右 
            return loc%3!=2;
        case 2:
            nxt=loc+3;
            return loc<6;//下 
        case 3:
            nxt=loc-1;
            return loc%3;//左 
    }
}
bashuma chushihua(bashuma bsm[])
{
    int k;
    //int bsmchushihua[9]={2,8,3,1,6,4,7,0,5};//五步即到
    int bsmchushihua[9]={3,1,8,7,6,4,0,2,5};//21步
    //int bsmchushihua[9]={1,2,3,8,6,4,7,0,5};//一步即到
    //int bsmchushihua[9]={1,2,3,6,0,5,7,8,4};
    //int bsmchushihua[9]={0,2,3,8,1,4,7,6,4};
    //int bsmchushihua[9]={2,0,3,1,8,4,7,6,5};
    //int bsmchushihua[9]={1,3,4,7,8,0,6,5,2};
    //可以使用上面的不同初始状态进行测试
    for(k=0;k<9;k++)
    {
        bsm[0].ccstate[k]=bsmchushihua[k];
    }
    bsm[0].a=h(bsm[0].ccstate);
    bsm[0].fx=0;
    bsm[0].flag=1;
    bsm[0].pre=-1;
    bsm[0].cengshu=0;
    return bsm[0];

}
void rudui1(bashuma bsm[],bashuma bsm1[],char c)
{   int k;
    ta++;
    for(k=0;k<9;k++)
       {
        bsm1[ta].ccstate[k]=bsm[head].ccstate[k];
       }
    //bsm1[ta].a=h(bsm[head].ccstate)+head;
      bsm1[ta].fx=c;
      bsm1[ta].flag=0;
      if(m==1){zuixiao[m-1]==-1;bsm1[ta].pre=zuixiao[m-1];}
      else
      {
          bsm1[ta].pre=zuixiao[m-1];}
        bsm1[ta].a=h(bsm[head].ccstate)+bsm1[bsm1[ta].pre].cengshu;//bsm1[ta].a=h(bsm[head].ccstate);
    bsm1[ta].cengshu=bsm1[bsm1[ta].pre].cengshu+1;
      /*printf("\n%d",ta);
         for(k=0;k<9;k++)
         {

            if(k%3==0)printf("\n");
             printf("%d  ",bsm1[ta].ccstate[k]);

        }
        printf("\n",ta-1);*/

}
void rudui(bashuma bsm[],bashuma bsm1[],int i)
{   
    head++;
     int k;
    for(k=0;k<9;k++)
       {
        bsm[head].ccstate[k]=bsm1[i].ccstate[k];
       }
      bsm[head].a=bsm1[i].a;
      bsm[head].fx=bsm1[i].fx;
      bsm[head].pre=i;
       /* printf("\n%d",head);
         for(k=0;k<9;k++)
         {

            if(k%3==0)printf("\n");
             printf("%d  ",bsm1[head].ccstate[k]);

        }
        printf("\n",head);*/

}
int min(bashuma bsm[])//找到最小的 
{

    int smallest;
    int i;
   smallest = 100000;
    for( i =1; i <=ta; i++)
    {   if(bsm[i].flag==1)continue;
        if(smallest > bsm[i].a)
            smallest = bsm[i].a;
    }
    for ( i = 1; i <=ta; i++)
    {   if(bsm[i].flag==1)continue;
            if(smallest ==bsm[i].a)
            return i;
    }

}
int locate(bashuma bsm[])
{   int k;
    for(k=0;k<9;k++)
       {
        if(bsm[head].ccstate[k]==0)
        return k;
       }
}
int bianli1(bashuma bsm[],bashuma bsm1[])
{
    int k,j,l;
    for(k=0;k<=ta;k++)
    {
        l=0;
        for(j=0;j<9;j++)
        {
         if(bsm1[k].ccstate[j]==bsm[head].ccstate[j])
             l++;
        }
        if(l==9)
        return 0;
        else return 1;
    }
}
int pp(bashuma bsm[],bashuma bsm1[])
{   int c,k,j,flag,n,a;
    //与终极目标相符返回结束
    int nxt,now; 
    while(!check(bsm[head].ccstate))
    {
        now=locate(bsm);
        for(int dir=0;dir<4;dir++)//空格可以往四个方向移动
        {
            if(head==0)
                if(canMove(now,dir,nxt)) 
                {
                bsm[head].ccstate[now]=bsm[head].ccstate[nxt];
                bsm[head].ccstate[nxt]=0;
                rudui1(bsm,bsm1,cdir[dir]);
                //还原数组,进行下一个方向的变换 
                bsm[head].ccstate[nxt]=bsm[head].ccstate[now];
                bsm[head].ccstate[now]=0;

                }
            if((head!=0)&&(bsm[head].fx!=opcdir[dir])&&canMove(now,dir,nxt))
            {
                bsm[head].ccstate[now]=bsm[head].ccstate[nxt];
                bsm[head].ccstate[nxt]=0;
                if(bianli1(bsm,bsm1)){rudui1(bsm,bsm1,cdir[dir]);}
                bsm[head].ccstate[nxt]=bsm[head].ccstate[now];
                bsm[head].ccstate[now]=0;
             }

        }
        zuixiao[m]=min(bsm1);
        a=zuixiao[m];
        bsm1[a].flag=1;
        m++;
            //A*算法找到值最小的步骤  入队head++ 作为扩展节点,否则不入队
        rudui(bsm,bsm1,a);
    }
    if(check(bsm[head].ccstate))
        return 1;
    else return 0;

}
void shuchu(bashuma bsm[],int i)
{   int k;
    if(bsm[i].pre==-1)
        return;
    if(bsm[i].pre!=-1)
        shuchu(bsm,bsm[i].pre);
     printf("\n");
         for(k=0;k<9;k++)
         {

            if(k%3==0)printf("\n");
             printf("%d  ",bsm[i].ccstate[k]);  
        }
         step++;
        printf("\n");   
}

int main()
{
    int i,k,s;
    bashuma bsm[N],bsm1[N];
    bsm[0]=chushihua(bsm);
    for(k=0;k<9;k++)
         {

            if(k%3==0)printf("\n");
             printf("%d  ",bsm[0].ccstate[k]);

        }
    bsm1[0]=chushihua(bsm1);
    i=pp(bsm,bsm1);
    if(i)
        {
        s=bsm[head].pre;
        shuchu(bsm1,s);
        printf("一共需要走%d步到达目标状态!\n",step+1);
        }
    else 
    printf("在深度限制之下没有找到解!\n");
    return 0;

}

八数码中同时使用了状态空间法,很欣赏的一个亮点便是操作集是空格移动的位置。分为上下左右四步,化繁为简。
同时采用了A*算法,根据不在位的棋子数确定扩展节点。

贴上初始状态为此:

![这里写图片描述](http://img.blog.csdn.net/20170701161702546?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzM0NDk3OTA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![这里写图片描述](http://img.blog.csdn.net/20170701161916209?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzM0NDk3OTA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
运行结果如下:![这里写图片描述](http://img.blog.csdn.net/20170701161825313?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzM0NDk3OTA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)


你可能感兴趣的:(人工智能,人工智能,算法)