吴昊品游戏核心算法 Round 9 —— 黑白棋AI系列之西洋跳棋(第二弹)(双向BFS+STL)(POJ 1198)...
接上回,如图所示,这是黑白棋的一个变种,Solitaire也是一种在智能手机上普遍存在的一种游戏。和翻转棋(Flip Game)一样,西洋跳棋(Solitaire)也没有正统的黑白棋(奥赛罗,又称Othello)受关注,但毕竟这也属于黑白棋的常见的一个变种,所以 我在这里还是将其收录了。
其规则同样很简单,只是比Flip Game多了一条规则,变为了如下的两条(棋子的颜色相同,假设一个8*8的棋盘上一共只有4个棋子)
(1) 每个棋子可以自由移动一个单位长度。
(2) 任何一个棋子如果相邻位置有棋子而后面一位又没有棋子的话,是可以跳过的。
有了以上两个规则,我们看看此问题的AI吧,还是先说明下Source——(POJ 1198),这里我们依然采用BFS,当然,纯粹的单向BFS当然也是可解的,但是,考虑到该问题的某些对称性,我们在这里采用双向BFS,可以提高对该问题求解的时间效率。
2 Highlights:
3 (1)开的空间为什么是1<<24,这个是有解释的,而且bitset也可以装载下来,该容器也可以节省部分空间
4 (2)开两个不同的方向数组,但是,保持对应的关系,这在判断是否可以跳跃的时候有帮助
5 (3)关于结构体里面的与结构体名同名的函数的成员变量设置,我会在后面给出说明
6 (4)两个队列容器共同搜索,不然,搜索八步的话,需要浪费很长的时间以及很大的空间,都不划算
7 (5)在搜索之前,每次一个变化都用sort进行二维点的排序处理
8 (6)利用Hash容器来看看最终是否匹配,其实,这一点上看map容器也是可以的,只是Hash容器更好,其原因在(1)中也已经说明了
9 */
10
11 #include<iostream>
12 #include<cstdio>
13 #include<bitset> //STL容器
14 #include<cstring>
15 #include<algorithm> //C++自带的算法库
16 #include<queue> //STL容器
17 using namespace std;
18
19 const int N=((1<<24)+10); //开这么大一个空间,也是为了增强容错性能
20
21 bitset<N> hash1;
22 bitset<N> hash2; //bitset容器是可以节省内存的
23
24 int dir1[4][2]={{-1,0},{1,0},{0,1},{0,-1}};//上下左右四个方向的移动
25 int dir2[4][2]={{-2,0},{2,0},{0,2},{0,-2}};//上下左右四个方向的跳跃
26
27 char map1[9][9],map2[9][9]; //数组也尽量开大一点
28
29 struct Dir
30 {
31 int x,y;
32 };
33
34 struct Node
35 {
36 Dir dir[4];
37 int step;
38 char map[9][9];
39 Node() //初始化这个结构体的成员变量,其左右相当于类中的构造函数
40 {
41 for(int i=0;i<9;i++)
42 {
43 for(int j=0;j<9;j++)
44 {
45 map[i][j]='A';
46 }
47 }
48 step=0; //初始的步数为0
49 }
50 };
51
52 Node start,end;
53
54 //以下是两个内联函数,其在函数调用的地方直接展开,但是不适合
55 //在很长的函数代码中使用,这样会使代码总长度过于冗长
56
57 //对坐标进行排序
58 inline bool cmp(const Dir &a,const Dir &b)
59 {
60 if(a.x==b.x) return a.y<b.y;
61 else return a.x<b.x;
62 }
63
64 //将二维坐标转换成一维的
65 inline int Hash(const Node &node)
66 {
67 int num;
68 int sum=0;
69 for(int i=0;i<4;i++)
70 {
71 //(0*8+1到7*8+8也就是标注了1到64这64个点)
72 //这里我们可以看到为什么对N的设立是1<<24+10了,因为2^6^4=2^24
73 //int类型是32位的,也完全可以存下来
74 num=(node.dir[i].x-1)*8+node.dir[i].y;
75 sum=64*sum+num;
76 }
77 }
78
79 void BFS()
80 {
81 //设立两个队列容器双向搜索
82 queue<Node> Q1, Q2;
83 Q1.push(start);
84 Q2.push(end);
85 //开启一个结点和相邻的结点,并重置hash表
86 Node now, next;
87 hash1.reset();
88 hash2.reset();
89 hash1[ Hash(start)] = true;
90 hash2[ Hash(end)] = true;
91 int x, y;
92
93 //由于这里是最多八步,两个队列分别移动四步就可以了
94 for(int step = 0; step < 4; step++)
95 {
96 while(!Q1.empty())
97 {
98 next = now = Q1.front();
99 Q1.pop();
100 //搜索到第4步的时候,跳出循环
101 if(next.step > 4)
102 break;
103 for(int i = 0; i < 4; i++) // 四个点 D表示棋子,A表示空白
104 {
105 for(int j = 0; j < 4; j++)
106 {
107 // 先搜一步的方向
108 next = now;
109 x = now.dir[i].x + dir1[j][0];
110 y = now.dir[i].y + dir1[j][1];
111 if(x >= 1 && x <= 8 && y >= 1 && y <= 8 && now.map[x][y] != 'D')
112 {
113 next.dir[i].x = x;
114 next.dir[i].y = y;
115 next.step = now.step + 1;
116 //这里转换过来,移动一步的位置置D而原来的位置置A
117 next.map[x][y] = 'D';
118 next.map[now.dir[i].x][now.dir[i].y] = 'A';
119 sort(next.dir, next.dir + 4 , cmp);
120 //如果已经遍历过,就继续了
121 if(hash1[ Hash(next) ])
122 continue;
123 if(hash2[ Hash(next) ] )
124 {
125 //如果该状态在end搜过来hash过的就说明存在
126 printf("YES\n");
127 return;
128 }
129 //将next结点标记,并载入队列中
130 hash1[ Hash(next) ] = true;
131 Q1.push(next);
132 }
133 }
134 //再搜索两步,也就是跨越的方向
135 for(int j = 0; j < 4; j++)
136 {
137 next = now;
138 x = now.dir[i].x + dir2[j][0];
139 y = now.dir[i].y + dir2[j][1];
140 //跨越的条件
141 if(x >= 1 && x <= 8 && y >= 1 && y <= 8 &&
142 now.map[now.dir[i].x + dir1[i][0] ][now.dir[i].y + dir1[i][1] ] == 'D'
143 && now.map[x][y] != 'D')
144 {
145 next.dir[i].x = x;
146 next.dir[i].y = y;
147 next.step = now.step + 1;
148 next.map[x][y] = 'D';
149 next.map[now.dir[i].x][now.dir[i].y] = 'A';
150 sort(next.dir, next.dir + 4 , cmp);
151 if(hash1[ Hash(next) ])
152 continue;
153 if(hash2[ Hash(next) ] )
154 {
155 printf("YES\n");
156 return ;
157 }
158 hash1[ Hash(next) ] = true;
159 Q1.push(next);
160 }
161 }
162 }
163 }
164
165 //同理,对Q2队列的妹一种情况采用相似的搜索,这里直接搬砖,如果将相似的功能封装入一个函数的话,代码也许就没有这么长了
166 while(!Q2.empty())
167 {
168 next = now = Q2.front();
169 Q2.pop();
170 //这里应该是说如果四步都已经搜索完了,则只有退出,但是,这里为什么用next.step==step+1而不与上面的next.step>4相同,现在我还没有弄清楚
171 if(next.step == step + 1)
172 break;
173 for(int i = 0; i < 4; i++)
174 // 四个点 D表示棋子,A表示空白
175 {
176 for(int j = 0; j < 4; j++)
177 {
178 next = now;
179 x = now.dir[i].x + dir1[j][0];
180 y = now.dir[i].y + dir1[j][1];
181 if(x >= 1 && x <= 8 && y >= 1 && y <= 8 && now.map[x][y] != 'D')
182 {
183 next.dir[i].x = x;
184 next.dir[i].y = y;
185 next.step = now.step + 1;
186 next.map[x][y] = 'D';
187 next.map[now.dir[i].x][now.dir[i].y] = 'A';
188 sort(next.dir, next.dir + 4 , cmp);
189 if(hash2[ Hash(next)])
190 continue;
191 if(hash1[ Hash(next)])
192 {
193 printf("YES\n");
194 return ;
195 }
196 hash2[ Hash(next) ] = true;
197 Q2.push(next);
198 }
199 }
200 for(int j = 0; j < 4; j++)
201 {
202 next = now;
203 x = now.dir[i].x + dir2[j][0];
204 y = now.dir[i].y + dir2[j][1];
205 if(x >= 1 && x <= 8 && y >= 1 && y <= 8 &&
206 now.map[now.dir[i].x + dir1[i][0] ][now.dir[i].y + dir1[i][1] ] == 'D'
207 && now.map[x][y] != 'D')
208 {
209 next.dir[i].x = x;
210 next.dir[i].y = y;
211 next.step = now.step + 1;
212 next.map[x][y] = 'D';
213 next.map[now.dir[i].x ][now.dir[i].y ] = 'A';
214 sort(next.dir, next.dir + 4 , cmp);
215 if(hash2[ Hash(next)])
216 continue;
217 if(hash1[ Hash(next) ] )
218 {
219 printf("YES\n");
220 return ;
221 }
222 hash2[ Hash(next) ] = true;
223 Q2.push(next);
224 }
225 }
226 }
227 }
228 }
229 printf("NO/n");
230 return ;
231 }
232
233 int main()
234 {
235 int x,y;
236 //每一行的前面两个数和后面六个数是需要区分开来的
237 while(scanf("%d%d",&x,&y)!=EOF) //读到文件尾
238 {
239 Node ss,ee; //收尾结点
240 ss.map[x][y]='D';
241 ss.dir[0].x=x;
242 ss.dir[0].y=y;
243 for(int i=1;i<4;i++)
244 {
245 scanf("%d%d",&x,&y);
246 ss.map[x][y]='D';
247 ss.dir[i].x=x;
248 ss.dir[i].y=y;
249 }
250 for(int i=0;i<4;i++)
251 {
252 scanf("%d%d",&x,&y);
253 ee.map[x][y]='D';
254 ee.dir[i].x=x;
255 ee.dir[i].y=y;
256 }
257 start=ss;
258 end=ee;
259 //分别按照cmp函数的规则对这八个点四个四个地排序
260 sort(start.dir,start.dir+4,cmp);
261 sort(end.dir,end.dir+4,cmp);
262 BFS();
263 }
264 return 0;
265 }
如图,以下是解释结构体的名字和其内的函数名同名的情况:
转载于:https://www.cnblogs.com/tuanzang/archive/2013/02/27/2935835.html
吴昊品游戏核心算法 Round 9 —— 黑白棋AI系列之西洋跳棋(第二弹)(双向BFS+STL)(POJ 1198)...相关推荐
- 吴昊品游戏核心算法 Round 16 ——吴昊教你玩口袋妖怪 第三弹 地洞谜题
这样的场景我们应该经常遇到的吧,哈哈! 口袋妖怪的地洞要算是最令人讨厌的了,因为,有些地洞是全黑的,你即使用了闪光灯(必选道具#03),你有时也只能用GBA外壳的荧光屏作为道具才能将整个地洞看清楚. ...
- 吴昊品游戏核心算法 Round 17 —— 吴昊教你玩拼图游戏(8 puzzle)
如图所示,这是一个九宫格(这倒是让我想起了小时候老师在黑板上教导我们的如何通过一系列的拼凑,将横行,竖行,以及斜行都拼到和相等),格子中有一个格子是空的,另外八个格子分别有数字1--8,我们的任务是将 ...
- 吴昊品游戏核心算法 Round 9 —— 正统黑白棋AI(博弈树)
黑白棋程式简史 在1980年代,电脑并不普及,在黑白棋界里,最强的仍然是棋手(人类). 到了1990年代初,电脑的速度以几何级数增长,写出来的黑白棋程式虽然仍然有点笨拙,但由于计算深度(电脑的速度快) ...
- 吴昊品游戏核心算法 Round 10 —— 吴昊教你下围棋(利用递归来解决吃子的问题)...
如图所示,此即为日本动漫棋魂中的千年佐为,也就是SAI.众所周知,围棋的规则相比于中国象棋,国际象棋等等都简单许多,真是因为更简单的规则,才诞生 了更复杂的逻辑.目前的围棋AI还很不行,最NB的应该是 ...
- 吴昊品游戏核心算法 Round 17 —— 吴昊教你玩拼图游戏 序
如图所示,此人就是<死亡笔记>中最终击败夜神月的尼亚(当然,他在击败夜神月的过程中,利用了梅洛的一些帮助),尼亚喜欢玩各种玩具,比如乐高啊,多米诺骨牌啊等等,当然,他最喜欢的仍然是拼图.他 ...
- 吴昊品游戏核心算法 Round 16 —— 吴昊教你玩口袋妖怪 第四弹 拉帝亚斯?!拉帝欧斯?!...
作为讲述口袋妖怪的Round 16,这里,我会采用"夹叙夹议,夹议夹叙"的模式进行编排.也就是在议论一些口袋妖怪中的小游戏的核心算法的同时,叙述一下我对口袋妖怪的理解以及我与这一系 ...
- 吴昊品游戏核心算法 Round 18 —— 吴昊教你玩Zen Puzzle Garden
如果你认为无法因为玩一个电脑游戏而达到精神的顿悟,你可能是正确的.不过你完全可以试着解决一个禅宗花园发生的难题,从而达到静心的精神状态. 这个游戏是获得2003年独立游戏节提名的精品游戏,在注重游 ...
- 吴昊品游戏核心算法 Round 15 —— (转载)德州扑克中的心理战
在我的Round 15中的德州扑克的AI中,设计者曾经赋予了德州扑克一种不错的心理恐吓算法,具体地说,是如下的战术: 如果RR<0.8,那么95%选择弃牌,0%选择叫牌,5%选择加倍(这里加倍的 ...
- 吴昊品游戏核心算法 Round 16 —— 吴昊教你玩口袋妖怪 第九弹 冰系道馆
道馆之战!!!这一次,道馆由电系变为了冰系,和龙系的道馆不一样,冰系的道馆有冰系的规定.如图所示,一共有三层,上面一层总是比下面一层要复杂一些.在冰系道馆中,有如下的两个规则:(1)我们不会在冰面上划 ...
最新文章
- 网络工程师必须具备的素质
- Hadoop集群的安全模式
- SQL Server 一些使用小技巧
- win7 将文件的背景色改为绿色
- Java开发面试问题,牛逼轰轰!
- Python入门100题 | 第033题
- 如何查看linux系统的存储空间大小,linux 如何查看硬盘大小,存储空间大小等系统信息及硬件信息...
- c语言中学生信息管理系统中删除学生信息,学生信息管理系统C语言编程
- java 快逸报表_数据填报 | 快逸报表工具-Java报表软件
- soapUI Pro 4.5.1的新破解方案
- Java开发五年,java面试未来职业规划
- 计算机病毒是指源程序还是特殊小程序,2015年9月计算机一级考试Msoffice上机模拟题(五)...
- 如何去掉CSDN中图片的水印
- 【Python】数据分析.pandas.透视表与交叉表
- apple键盘的home键_如何从Apple HomeKit Home中删除HomeKit设备
- Linux(Centos7)服务器配置Tomcat以及JDK并部署WEB项目
- 没有免费午餐定理(No Free Lunch Theorem)
- AI计算盒子SE5笔记
- 基本linux性能调优技巧
- java中代表人数的单词_计算列表中显示的单词数量
热门文章
- R语言数据可视化分析案例:探索BRFSS数据
- Wondershare UniConverter for Mac(全能视频格式转换器)
- 基于matlab测幅值,基于MATLAB的分析测试系统
- 【历史上的今天】7 月 31 日:“缸中之脑”的提出者诞生;Wi-Fi 之父出生;USB 3.1 标准发布
- Fiddler抓包(下载安装及使用)
- Android仿微信具有表情输入和拍照上传功能的键盘
- QQ分享总是返回走onCancel()方法以及分享到空间 shareToQzone()没有反应
- 安卓报错: android.content.ActivityNotFoundException: Unable to find explicit activity class
- Xshell使用密钥登录Linux服务器
- java潜艇大战游戏源码项目