如图所示,这是一个九宫格(这倒是让我想起了小时候老师在黑板上教导我们的如何通过一系列的拼凑,将横行,竖行,以及斜行都拼到和相等),格子中有一个格子是空的,另外八个格子分别有数字1--8,我们的任务是将原图通过空格转换为前面八格为1--8,而最后一格为空。

以上的截图来自如下的一款android游戏(当然,由于版本的原因,样式换成了一种木板式的,更贴近于我们在现实中的“八数码游戏”),其名字叫:8--Puzzle,在其软件的启动界面中,有阐述两种游戏的模式:

何者为难?何者为易呢?这里我们有一个可以定量化的衡量标准,也就是说,我们可以以该状态还原为目标状态(这里称为Goal-Status)所需的最小步数为一个凭据。当然,这样比较单纯,我们还可以设计出更“客观”一些的,比如,有些移动是难以想到的,而有些是很容易想到的,为每一个步数赋予一个权值,这就是一个不错的想法。

那么,关键的问题是,我们需要设计出一个AI出来,只要给出初始的状态,我们可以通过我们的AI来得到到目标状态具体需要多少步,甚至,我们可以得到每一步的具体操作过程(这里可以用u,d,l,r分别来代表上下左右)

AI的实现方法很多,比如:裸体的BFS(Bruce Force+BFS),双向BFS+STL,开哈希表之后,更省时间,更牛的一些办法还有楼教主在2005年的百度之星总决赛打出的A*(该算法基于启发式,后来,百度公司在之后的比赛中就以Astar作为百度之星的象征,其来源就是楼天成的A*算法)以及其进一步的IDA*算法。在博客园中,有牛人归纳出了八数码问题的八重境界,我会在最后进行转载的。

有人说过,没有看过该AI的人,人生不完美,也许,该问题真的比较深刻吧!

我们利用X来表示这个8--PUZZLE问题的空格,当然,在Input中,可以将其归为一行(毕竟,大家都知道这是个八数码问题吧!)

Input : 1 2 3 x 4 6 7 5 8

对于输出来说,我们只要输出一个方向就可以了(根据方向,我们可以知道是哪一个方块在动),对于这个Input,我们所得到的Output应该是:lul

Output:lul

而对于最少的次数,我们可以根据输出中的字母的数量得到。

首先,考虑一个问题,我们的状态总数(算上那个空格)一共为9! = 362880 种,于是,即使是最野蛮的暴力+BFS也是可以的(纯粹的暴力的话,果断还是不行的),这里给出的一种方法是暴力+BFS+queue容器+Hash表。

Solve:

  1 #include <iostream>
  2 
  3  #include <cstdio>
  4 
  5  #include <cstring>
  6 
  7  #include < string>
  8 
  9  #include <algorithm>
 10 
 11  #include <cmath>
 12 
 13  #include <queue>
 14 
 15  
 16 
 17   using  namespace std;
 18 
 19  
 20 
 21   #define MAXN 363000   // 9!==326880
 22 
 23  
 24 
 25   struct node
 26 
 27  {
 28 
 29     int s[ 9]; // 当前状态
 30 
 31     int loc; // '0'的位置,即'x'的位置
 32 
 33     int stat; // 康托展开的hash值
 34 
 35     string path; // 路径
 36 
 37  };
 38 
 39  
 40 
 41   // 分别存储1--9的阶乘值
 42 
 43   int fac[]={ 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
 44 
 45   // 同以前一样,用一个二维数组dir[4][2]来装填方向
 46 
 47   int dir[ 4][ 2]={- 1, 0, 1, 0, 0,- 1, 0, 1};
 48 
 49   // 存储方向字符,以便在之后表明路径
 50 
 51   char index[ 5]= " udlr ";
 52 
 53   int aim= 322561; // 123456780对应的hash值
 54 
 55   string path; // 路径
 56 
 57    // 作为一种标记,遍历已经访问过的状态
 58 
 59   bool vis[MAXN];
 60 
 61   // 最初的结点
 62 
 63  node st;
 64 
 65  
 66 
 67   // 康托展开求序列的hash值
 68 
 69   int cantor( const  int *s)
 70 
 71  {
 72 
 73     int sum= 0;
 74 
 75     for( int i= 0;i< 9;i++)
 76 
 77    {
 78 
 79       int num= 0;
 80 
 81       for( int j= 0;j<i;j++)
 82 
 83         if(s[j]>s[i])
 84 
 85          num++;
 86 
 87      sum+=(num*fac[i]);
 88 
 89    }
 90 
 91     return (sum+ 1);
 92 
 93  }
 94 
 95  
 96 
 97   // 这里利用越界函数来标识是否越界
 98 
 99   bool isok( int x, int y)
100 
101  {
102 
103     if(x< 0 || x> 2 || y> 2 || y< 0)
104 
105       return  false;
106 
107     return  true;
108 
109  }
110 
111  
112 
113   bool bfs()
114 
115  {
116 
117     // 初始化vis数组,将其都标记为0
118 
119    memset(vis, 0, sizeof(vis));
120 
121     // 队列容器来装载结点
122 
123    queue<node>q;
124 
125    node cur,tmp;
126 
127     // 初始结点进入队列
128 
129    q.push(st);
130 
131    vis[st.stat]= 1;
132 
133     // 如果队列非空,那么就一直重复这个过程
134 
135     while(!q.empty())
136 
137    {
138 
139       // 将首结点取出作为当前结点
140 
141      cur=q.front();
142 
143       // 将队列剥离一个
144 
145      q.pop();
146 
147       // 得到空白方块的横坐标和纵坐标
148 
149       int x=cur.loc/ 3,y=cur.loc% 3;
150 
151       for( int i= 0;i< 4;i++)
152 
153      {
154 
155         // 从四个方向对空白位置进行搜索
156 
157         int tx=x+dir[i][ 0],ty=y+dir[i][ 1];
158 
159         // 如果越界的话,换一个方向
160 
161         if(!isok(tx,ty))
162 
163           continue;
164 
165        tmp=cur;
166 
167        tmp.loc=tx* 3+ty; // '0'移动到该位置
168 
169          // 这里,相当于交换了一个方向
170 
171        tmp.s[cur.loc]=tmp.s[tmp.loc];
172 
173        tmp.s[tmp.loc]= 0;
174 
175         // 获取这个状态的hash值
176 
177        tmp.stat=cantor(tmp.s);
178 
179         // 如果这个状态没有被遍历过的话
180 
181         if(!vis[tmp.stat])
182 
183        {
184 
185          vis[tmp.stat]= 1;
186 
187           // 修改路径,字符串可以直接在后面加
188 
189          tmp.path=cur.path+index[i];
190 
191           // 如果是正解的话
192 
193           if(tmp.stat==aim)
194 
195          {
196 
197            path=tmp.path;
198 
199             return  true;
200 
201          }
202 
203           // 如果不是最优解的话,继续入队列
204 
205          q.push(tmp);
206 
207        }
208 
209      }
210 
211    }
212 
213     return  0;
214 
215  }
216 
217  
218 
219   int main()
220 
221  {
222 
223     // 假设操作的总次数不大于256
224 
225     char buf[ 256];
226 
227     // 利用gets()函数读入一行
228 
229     while(gets(buf))
230 
231    {
232 
233       int len=strlen(buf);
234 
235       int cnt= 0;
236 
237       for( int i= 0;i<len;i++)
238 
239      {
240 
241         if(buf[i]>= ' 1 '&&buf[i]<= ' 9 ')
242 
243        {
244 
245          st.s[cnt++]=buf[i]- ' 0 ';
246 
247        }
248 
249         else  if(buf[i]== ' x ')
250 
251        {
252 
253          st.s[cnt]= 0;
254 
255           // 这里标识空白区域的具体位置
256 
257          st.loc=cnt++;
258 
259        }
260 
261      }
262 
263       // 利用cantor扩展函数得到初始状态
264 
265      st.stat=cantor(st.s);
266 
267       // 如果一开始就是目标状态的话,那么就输出空气好咯!
268 
269       if(st.stat==aim)
270 
271      {
272 
273        puts( "");
274 
275         continue;
276 
277      }
278 
279       // 广搜开始
280 
281       if(bfs())
282 
283         // 输出路径
284 
285        cout<<path<<endl;
286 
287       else
288 
289         // 输出不可解
290 
291        puts( " unsolvable ");
292 
293    }
294 
295     return  0;
296 
297  }

转载于:https://www.cnblogs.com/tuanzang/archive/2013/04/05/3000426.html

吴昊品游戏核心算法 Round 17 —— 吴昊教你玩拼图游戏(8 puzzle)相关推荐

  1. 吴昊品游戏核心算法 Round 17 —— 吴昊教你玩拼图游戏 序

    如图所示,此人就是<死亡笔记>中最终击败夜神月的尼亚(当然,他在击败夜神月的过程中,利用了梅洛的一些帮助),尼亚喜欢玩各种玩具,比如乐高啊,多米诺骨牌啊等等,当然,他最喜欢的仍然是拼图.他 ...

  2. 吴昊品游戏核心算法 Round 16 ——吴昊教你玩口袋妖怪 第三弹 地洞谜题

    这样的场景我们应该经常遇到的吧,哈哈! 口袋妖怪的地洞要算是最令人讨厌的了,因为,有些地洞是全黑的,你即使用了闪光灯(必选道具#03),你有时也只能用GBA外壳的荧光屏作为道具才能将整个地洞看清楚. ...

  3. 吴昊品游戏核心算法 Round 16 —— 吴昊教你玩口袋妖怪 第四弹 拉帝亚斯?!拉帝欧斯?!...

    作为讲述口袋妖怪的Round 16,这里,我会采用"夹叙夹议,夹议夹叙"的模式进行编排.也就是在议论一些口袋妖怪中的小游戏的核心算法的同时,叙述一下我对口袋妖怪的理解以及我与这一系 ...

  4. 吴昊品游戏核心算法 Round 18 —— 吴昊教你玩Zen Puzzle Garden

    如果你认为无法因为玩一个电脑游戏而达到精神的顿悟,你可能是正确的.不过你完全可以试着解决一个禅宗花园发生的难题,从而达到静心的精神状态.   这个游戏是获得2003年独立游戏节提名的精品游戏,在注重游 ...

  5. 吴昊品游戏核心算法 Round 10 —— 吴昊教你下围棋(利用递归来解决吃子的问题)...

    如图所示,此即为日本动漫棋魂中的千年佐为,也就是SAI.众所周知,围棋的规则相比于中国象棋,国际象棋等等都简单许多,真是因为更简单的规则,才诞生 了更复杂的逻辑.目前的围棋AI还很不行,最NB的应该是 ...

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

    道馆之战!!!这一次,道馆由电系变为了冰系,和龙系的道馆不一样,冰系的道馆有冰系的规定.如图所示,一共有三层,上面一层总是比下面一层要复杂一些.在冰系道馆中,有如下的两个规则:(1)我们不会在冰面上划 ...

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

    道馆之战!!!如图所示,此乃口袋妖怪的道馆战中的龙系道馆,一般情况下,在每一个系列的口袋妖怪中,龙系道馆往往是排列在最后的,我们通过一些滑动可以到达我们所想到达的地方(冰块并不会因此而破碎).但是,我 ...

  8. 吴昊品游戏核心算法 Round 16 —— 吴昊教你玩口袋妖怪 第十弹 超能力系道馆

    道馆之战!!!这一次,我们将要挑战的是众人熟知的超能力系道馆,这种道馆不是火箭队的巢穴,也不是冰系道馆的那种踏冰川.何以解释?我说明如下:不同的地板上的机关有不同的颜色,每一个颜色有对应的位置,我们可 ...

  9. 吴昊品游戏核心算法 Round 16 —— 吴昊教你玩口袋妖怪 第二弹 777赌博机

    (此图选自口袋妖怪红宝石的某个镇的娱乐场所的赌博机) 这款777赌博机存在于日本的赌场,这里,我不想再像以前一样,通过各种百科COPY一些资料,这样没有意思.我谈谈自己当年玩POKEMON时的感受吧! ...

最新文章

  1. MVC进阶学习--HtmlHelper控件解析(一)
  2. python sharedctypes 多进程性能测试
  3. MIMIC 以太坊医疗项目开发(4)Axios
  4. makefile 打印变量_[Makefile] 缩进与空格--记录踩过的坑
  5. 3步实现Jetty和Eclipse集成
  6. java sax解析xml_【转】java使用sax解析xml文件
  7. JAVA与DOM解析器基础 学习笔记
  8. Axure谷歌浏览器Chrome扩展程序下载及安装方法
  9. 21天学通JAVA:类的定义和对象的创建
  10. deepin安装nginx服务器
  11. creo绘图属性模板_最好用的工程图格式文件及建模模板文件
  12. Appium 环境搭建
  13. 有理数相加c语言结构体,有理数的加法
  14. 10.24程序员节日快乐
  15. 2021 Summary
  16. Sublime Text 2 设置文件详解转
  17. 查看本地ip地址和端口方法
  18. 十分nb且详细的Elasticsearch教程
  19. uniapp如何实现时间轴会议预约显示?
  20. 应用案例 | 多功能三维摇床与印迹杂交技术

热门文章

  1. bluehost中国主机评测:bluehost香港主机怎么样?
  2. php bluehost,BlueHost主机配置Php.ini中文解释(四)
  3. linux - apollo系统
  4. Sklearn实现岭回归
  5. 【深度首发】小视科技杨帆:瞄准金融+AI,将商业触角不断延伸丨Xtecher 封面
  6. 输入URL后浏览器的渲染过程
  7. 怎样将计算机和电视机连接网络连接,电脑接电视,教您电脑如何连接电视机
  8. 妖怪宝可萌html5游戏,妖怪宝可萌h5斗宠棋玩法规则以及技巧分析
  9. js监听浏览器焦点事件
  10. 共鸣:周董的音乐不适合做影视主题曲