吴昊品游戏核心算法 Round 6 —— 独立钻石跳棋游戏AI(BFS)

大约在200多年前,法国的巴士底狱中关押着一名贵族囚犯。此人整日面对铁窗,实在无聊,就在当时欧洲流行的棋盘上,设计出一种能一个人玩的棋,这就是“Solitaire”(独立钻石)。此后,这个游戏便渐渐流行于世界各地。
  在棋盘的33个孔中,除了最中心的一孔外,每孔都放下一个棋子。
  每个子只能沿着棋盘上的纵横线“隔子跳”(像跳棋一样,跳过一个相邻的棋子),跳到一个空格处,跳完后把被跳过的棋子拿掉。这样,当棋子跳到最后,无子可 动时,游戏就结束了。最后剩下5只棋子─“good(好)”;剩下4只棋子─“better(不错)”;剩下3只棋子─“clever(聪明)”;剩下2 只棋子─“wonderful(精彩)”;剩下1只棋子─“great(真棒)”;如果剩下1只,而且在正中央,那就是─“genius(天才)”! 聪明的你,快来试试吧!
  在1908年以前,人们都以为要取得“genius”最少要跳23步(连跳作一步计)。但在1912年,布荷特创下了18步的世界纪录!这纪录后来被证明 为绝对的世界纪录。1986年,上海女工万萍萍找到另一种不同于布荷特的18步取得“天才”的方法。后来,上海计算机研究所开动了大型的计算机,希望再找 出几种取得“天才”的方法,结果令人诧异:“独立钻石”以18步取得“天才”是最快捷的,且方法只有两种,一种是布荷特的,另一种便是万萍萍的!

 


 【goto的妙用
  此 算法用了31步实现这个问题!显然不是最好的!首先模拟棋盘,然后用BFS来分别朝着不同的方向进行搜索。每一次判断是否为“天才”点,其中那个goto 关键字用得恰到好处,如果不行的话,正好可以跳回一步,如果可以继续的话,就不做任何事情。goto一般不该使用的,这是汇编语言的一些跳转指令,但是, 在某些环境下,确实可以收到一些神奇的效果!比如此例。

 

  1 /*问题描述:
  2     独立钻石跳棋问题。在下图中,33个方格顶点摆放着32枚棋子,仅中央的顶点空着未摆放棋子。
  3 下棋的规则是任一棋子可以沿水平或成垂直方向跳过与其相邻的棋子,进入空着的顶点并吃掉被跳过的棋子。
  4 试设计一个算法找出一种下棋方法,使得最终棋盘上只剩下一个棋子在棋盘中央。
  5 0------------------------->x
  6 | 0 1 2 3 4 5 6
  7 |     * * *        0                                                   3
  8 |     * * *        1                                                    |
  9 | * * * * * * *    2                                                    |
 10 | * * * . * * *    3      方向确定规定为: 2<-----*----->0
 11 | * * * * * * *    4                                                    |
 12 |     * * *        5                                                    |
 13 |     * * *        6                                                    1
 14 y
 15 */
 16 /*************************************************************************/
 17 
 18  #include <iostream>
 19  #include <fstream>
 20  using namespace std;
 21  
 22  struct step //记录移动棋子的信息
 23  {
 24    int sx, sy; // 记录移动棋子前棋子的位置
 25    int tx, ty; // 记录移动棋子后棋子的位置
 26    int dir; // dir值代表移动棋子的方向
 27  };
 28 
 29  struct step mystack[100], last_step;
 30  char diamond[7][7];
 31  int Left_diamond = 32;//初始的钻石总数
 32  int x, y, nx, ny, ndir, top; // ndir值代表方向, 0代表向右, 1代表向下, 2代表向左, 3代表向上
 33  int flag=1// 是否成功找到解的标志
 34 
 35  /*****************************初始化棋盘*****************************/
 36  void Init_diamond()
 37  {
 38    for(int i=0; i<7; i++)
 39    {
 40      for(int j=0; j<7; j++)
 41      {
 42        if((i<2 || i>4) && (j<2 || j>4));//if后面分号代表什么都不做
 43        else
 44        {
 45          diamond[i][j] = '*';
 46        }
 47      }
 48    }
 49    diamond[3][3] = '.';//该点为正中央,代表"天才"点
 50  }
 51 /*********************************************************************/
 52 /*******************************移动棋子******************************/
 53  int Move_diamond(int y, int x, int dir)
 54  {
 55    if(diamond[y][x] != '*')
 56    {
 57      return 0;
 58    }
 59    struct step temp;
 60    switch(dir)
 61    {
 62      case 0:
 63        //考虑三种情况,(1)越界(2)中间没有间隔子(2)跳跃的到达处没有空隙
 64        if(x+2>6 || diamond[y][x+1]!='*' || diamond[y][x+2]!='.')
 65        {
 66          return 0;
 67        }
 68        diamond[y][x] = diamond[y][x+1] = '.';
 69        diamond[y][x+2] = '*';
 70        temp.sx = x;
 71        temp.sy = y;
 72        temp.tx = x+2;
 73        temp.ty = y;
 74        temp.dir = dir;
 75        mystack[top++] = temp;//又见这种数组里面变量自增的用法mystack[top++];
 76        return 1;
 77      break;
 78      
 79      case 1:
 80        if(y+2>6 || diamond[y+1][x]!='*' || diamond[y+2][x]!='.')
 81        {
 82          return 0;
 83        }
 84        diamond[y][x] = diamond[y+1][x] = '.';
 85        diamond[y+2][x] = '*';
 86        temp.sx = x;
 87        temp.sy = y;
 88        temp.tx = x;
 89        temp.ty = y+2;
 90        temp.dir = dir;
 91        mystack[top++] = temp;
 92        return 1;
 93      break;
 94  
 95      case 2:
 96        if(x-2<0 || diamond[y][x-1]!='*' || diamond[y][x-2]!='.')
 97        {
 98          return 0;
 99        }
100        diamond[y][x] = diamond[y][x-1] = '.';
101        diamond[y][x-2] = '*';
102        temp.sx = x;
103        temp.sy = y;
104        temp.tx = x-2;
105        temp.ty = y;
106        temp.dir = dir;
107        mystack[top++] = temp;
108        return 1;
109      break;
110  
111      case 3:
112        if(y-2<0 || diamond[y-1][x]!='*' || diamond[y-2][x]!='.')
113        {
114          return 0;
115        }
116        diamond[y][x] = diamond[y-1][x] = '.';
117        diamond[y-2][x] = '*';
118        temp.sx = x;
119        temp.sy = y;
120        temp.tx = x;
121        temp.ty = y-2;
122        temp.dir = dir;
123        mystack[top++] = temp;
124        return 1;
125      break;
126      
127      default:
128        break;
129    }
130    return 0;
131  }
132 
133 /*********************************************************************/
134 /*******************************主函数********************************/
135  void main()
136  {
137    // 输出一个解到文本文件answer.txt
138    ofstream answer("answer.txt");
139    Init_diamond();
140    top = nx = ny = ndir = 0;//从这一个点开始搜索
141    // 回溯遍历,直到找到一个解
142    while(1)
143    {
144      //这是“天才”的解法,但是,步骤却是不能确定的
145      if(Left_diamond == 1 && diamond[3][3] == '*')
146      {
147        break;
148      }
149      for(y=ny; y<7; y++,nx=0)
150      {
151        for(x=nx; x<7; x++,ndir=0)//在结尾的自增的区域调整方向
152        {
153          for(int dir=ndir; dir<4; dir++)
154          {
155            if(Move_diamond(y, x, dir))//每次分别向上,下,左,右尝试移动一次
156            {
157              Left_diamond--;//每次减少一个diamond
158              nx = ny = ndir = 0;//如果有解的话,第二步再从头扫描
159              goto nextstep;
160            }
161          }
162        }
163      }
164      nextstep:
165        if(y == 7)//搜到顶端
166        {
167          top--;
168          // 换一个来搜索
169          if(top >= 0)
170          {
171            //在退回之后,换一个方向
172            last_step = mystack[top];
173            diamond[(last_step.sy + last_step.ty)/2][(last_step.sx + last_step.tx)/2] = '*';
174            diamond[last_step.sy][last_step.sx] = '*';
175            diamond[last_step.ty][last_step.tx] = '.';
176            nx = last_step.sx;
177            ny = last_step.sy;
178            ndir = last_step.dir + 1;
179            Left_diamond++;
180          }
181          else
182          {
183            answer<<"Can't find any answer, I am sorry."<<endl;//这个是文件输出
184            cout<<"Can't find any answer, I am sorry."<<endl;//这个是控制台输出
185            flag=0;
186            break;
187          }
188        }
189    }
190  
191    Init_diamond();
192    //在文件中的初始化地图
193    answer<<"The initialization diamond states:"<<endl;
194    for(int i=0; i<7; i++)
195    {
196      for(int j=0; j<7; j++)
197      {
198        answer<<diamond[i][j]<<' ';
199      }
200      answer<<endl;
201    }
202    answer<<endl<<endl;
203    // 输出解
204    for(int n=0; n<top; n++)
205    {
206      answer<<"step "<<n+1<<": Move diamond ("<<mystack[n].sy+1<<","
207      <<mystack[n].sx+1<<") ---> ("<<mystack[n].ty+1<<","<<mystack[n].tx+1<<")"<<endl;
208      diamond[mystack[n].sy][mystack[n].sx] = '.';
209      diamond[(mystack[n].sy+mystack[n].ty)/2][(mystack[n].sx+mystack[n].tx)/2] = '.';
210      diamond[mystack[n].ty][mystack[n].tx] = '*';
211      answer<<"Left diamonds : "<<top-n<<endl;
212      for(int k=0; k<7; k++)
213      {
214        for(int j=0; j<7; j++)
215        {
216          answer<<diamond[k][j]<<' ';
217        }
218        answer<<endl;
219      }
220      answer<<endl<<endl;
221    }
222    //这里说明已经找到了最优解
223    if(flag)
224    {
225      cout<<"The answer has been exported to file \"answer.txt\" in the same directory.Welcome to see!"<<endl;
226    }
227  }
228 /*********************************************************************/
229 
230 

posted on 2013-02-27 21:00  吴昊系列  阅读(1457)  评论(0编辑  收藏  举报

导航