吴昊品游戏核心算法 Round 16 —— 吴昊教你玩口袋妖怪 第六弹 龙系道馆

 

  道馆之战!!!如图所示,此乃口袋妖怪的道馆战中的龙系道馆,一般情况下,在每一个系列的口袋妖怪中,龙系道馆往往是排列在最后的,我们通过一些滑动可以到达我们所想到达的地方(冰块并不会因此而破碎)。但是,我们仍然需要注意一点,就是我们一旦决定要滑动,就必须沿着这个方向滑动到障碍物的所在之处,才可以停下来,不然就会一直滑动到房间的墙角。

  实际上,这样的场景在口袋妖怪中并不仅仅局限于道馆。如图所示,这种场景在口袋妖怪中也是经常出现的:

 

  如图所示,玩家可以决定自己所移动的方向,在冰块的地方,会根据自己所选定的方向进行滑动,在空地或者障碍物的地方,滑动会停下来。玩家可以按照某位大神给出的如下的攻略到达目的地I(中途不要忘记捡起精灵球哦!)

  这里,我们探讨一下如何利用DFS算法来设计一个AI,让我们可以得到到达目的地所需要的最小移动的次数。首先,作为一个简化的模型,我们需要对实际问题进行一定程度的修改,我们修改两点:(1)将所有的空地都转换成障碍物,毕竟从Input来看,空地和障碍物的效果其实是等效的(2)我们假设我们可以完全地通过冰面来到达我们所需要的目的地。

  那么,原实际问题被简化成为如下的模型:

 

  其中,S代表起始点,G代表终结点。在解决问题之后,我们还需要明确一点,也就是我们所提到的,我们的男主角小智只能持续不断地在冰面上滑动,我的意思是这样的,如图所示:

 

  (a)中,由于主角的左边和下面是两堵墙,所以,我们不能朝着那个方向移动,但是,我们仍然是可以朝右方和上方移动的。而(b)中则说明,当我们已经移动出去之后(这里只至少移动了一个单位格之后),我们的主角在一段时间内的方向是不可被控制的,只能遵循他原来的方向进行移动。

  以上的模型比较简单,这里给出模型的一个最优解:

 

  如图,只要如下四步,我们就可以到达目标点(Goal)了。关于DFS的奥义,前文已经阐述过,所以,这里不解释,直接给出实现(为了增强游戏的可玩性,我们这里只要十步以内的解):

  Input:

  由于原图是离散的,我们可以用一个二维数组来表述。将原图的问题描述为如下的形式:

  6 6

  1 0 0 2 1 0

  1 1 0 0 0 0

  0 0 0 0 0 3

  0 0 0 0 0 0

  1 0 0 0 0 1

  0 1 1 1 1 1

  其中的(6,6)代表的是六行六列,0代表冰块,1代表障碍物(或者是空地),2代表起始点,3代表初始点。

  Output:对每一组数据来说,利用一个数字来代表你至少要滑行多少次

  Solve:

 

  1 #include <iostream>
  2 
  3  #include <cstdio>
  4 
  5  //利用该头文件来处理memset函数
  6 
  7  #include <memory.h>
  8 
  9  
 10 
 11  #define MAX 25
 12 
 13  using namespace std;
 14 
 15  
 16 
 17  int map[MAX][MAX];
 18 
 19  //由于是二维数组,所以我们应该记录两个数值,作为一个坐标,对start和end都开一个空间为2个单位的数组
 20 
 21  int start[2],end[2];
 22 
 23  //定义上下左右四个方向
 24 
 25  int dir[4][2]={{0,-1},{1,0},{0,1},{-1,0}};
 26 
 27  //一个地图的长与宽
 28 
 29  int w,h;
 30 
 31  int min_times;
 32 
 33  
 34 
 35  void getmap()
 36 
 37  {
 38 
 39    for(int i=0; i<h; i++)
 40 
 41    {
 42 
 43      for(int j=0; j<w; j++)
 44 
 45      {
 46 
 47        scanf("%d",&map[i][j]);
 48 
 49        if(map[i][j]==2)
 50 
 51        {
 52 
 53          start[0]=i;
 54 
 55          start[1]=j;
 56 
 57        }
 58 
 59      }
 60 
 61    }
 62 
 63  }
 64 
 65  
 66 
 67  void dfs(int x,int y,int step)
 68 
 69  {
 70 
 71    int sx,sy;
 72 
 73    //如果十步都无法走出,就直接退出
 74 
 75    if(step>10return;
 76 
 77    for(int i=0; i<4; i++)
 78 
 79    {
 80 
 81      if(map[x+dir[i][0]][y+dir[i][1]]!=1)
 82 
 83      {
 84 
 85        //找到初始点
 86 
 87        sx=x; sy=y;
 88 
 89        while(map[sx+dir[i][0]][sy+dir[i][1]]!=1)
 90 
 91        {
 92 
 93          //只要没有碰到障碍物,就不断地往前走
 94 
 95          sx=sx+dir[i][0]; 
 96 
 97          sy=sy+dir[i][1];
 98 
 99          //如果越界的话,就回到当前的初始点
100 
101          if(sx<0 || sx>=h || sy<0 || sy>=w) 
102 
103          {
104 
105            break;
106 
107          }
108 
109          //如果到达了终点,修改最短次数,并记录
110 
111          if(map[sx][sy]==3)
112 
113          {
114 
115            if(min_times>step)
116 
117            {
118 
119              min_times=step;
120 
121            }
122 
123            return;
124 
125          }
126 
127        }
128 
129        if(sx>=0 && sx<h && sy>=0 && sy<w) 
130 
131        {
132 
133          map[sx+dir[i][0]][sy+dir[i][1]]=0;
134 
135          dfs(sx,sy,step+1);
136 
137          //如果这条路径最终行不通的话,可以将其改成一个障碍物,代表此路不通
138 
139          map[sx+dir[i][0]][sy+dir[i][1]]=1;
140 
141        }
142 
143      }
144 
145    }
146 
147  }
148 
149  
150 
151  int main()
152 
153  {
154 
155    //我们这里以(0 0)作为结尾
156 
157    while(scanf("%d%d",&w,&h)!=EOF && (w+h))
158 
159    {
160 
161      //初始化地图
162 
163      memset(map,0,sizeof(map));
164 
165      //欲求最小值,我们必须设置一个最大值,这样才会越来越小
166 
167      min_times=9999;
168 
169      //入读一个地图
170 
171      getmap();
172 
173      dfs(start[0],start[1],1);
174 
175      //这表明不能滑到
176 
177      if(min_times==9999)
178 
179      {
180 
181        printf("-1\n");
182 
183      }
184 
185      //输出最小值
186 
187      else
188 
189      {
190 
191        printf("%d\n",min_times);
192 
193      }
194 
195    }
196 
197    return 0;
198 
199  }
200 
201  

posted on 2013-04-01 10:41  吴昊系列  阅读(831)  评论(0)    收藏  举报

导航