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

来源:互联网 发布:娱乐圈小鲜肉知乎 编辑:程序博客网 时间:2024/06/07 17:24
八数码难题      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<iostream> #include<cstdlib>#define N 1000using 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)
原创粉丝点击