机器博弈中的数据结构与基本方法(三)-----博弈树的搜索之九宫重排问题
九宫重排问题,如下图所示,九宫重排(或称八数码问题)是人工智能中一个经典问题,这里利用DFS解决这个问题。

通过设置一个OPEN表和一个CLOSE表记录尚未遍历和遍历过的状态,直至找到目标状态或遍历完所有状态,如果遍历完所有状态还木有找到目标状态则无解。
代码
1 #include <cstdio>
2 //利用深度优先搜索
3 //构造OPEN表和CLOSE表
4 typedef struct {
5 int State[5][5];
6 int FatherState[5][5];
7 }OPEN;
8 typedef struct {
9 int Order;
10 int State[5][5];
11 int FatherState[5][5];
12 }CLOSE;
13 OPEN OpenList[500000];
14 CLOSE CloseList[500000];
15 int OpenCount = -1, CloseCount = -1;
16 //判断位置是否合法的辅助数组
17 int Position[5][5] = {
18 {-1, -1, -1, -1, -1},
19 {-1, 1, 1, 1, -1},
20 {-1, 1, 1, 1, -1},
21 {-1, 1, 1, 1, -1},
22 {-1, -1, -1, -1, -1}
23 };
24 //将某一状态存入OPEN表
25 void Push(int a[][5], int b[][5]){
26 OpenCount++;
27 for (int i = 0; i < 5; i++){
28 for (int j = 0; j < 5; j++){
29 OpenList[OpenCount].State[i][j] = a[i][j];
30 OpenList[OpenCount].FatherState[i][j] = b[i][j];
31 }
32 }
33 }
34 //比较连个状态是否完全相同
35 int Compare(int a[][5], int b[][5]){
36 for (int i = 0; i < 5; i++){
37 for (int j = 0; j < 5; j++){
38 if (a[i][j] != b[i][j]){
39 return 0;
40 }
41 }
42 }
43 return 1;
44 }
45
46 void Move(int a[][5]){
47 int temp[5][5];
48 int i, j, m, n, flag1 = 0, flag2;
49 //找到0的位置
50 for (i = 0; i < 5; i++){
51 for (j = 0; j < 5; j++){
52 if (0 == a[i][j]){
53 flag1 = 1;
54 break;
55 }
56 }
57 if (flag1){
58 break;
59 }
60 }
61 //复制数组
62 for (m = 0; m < 5; m++){
63 for (n = 0; n < 5; n++){
64 temp[m][n] = a[m][n];
65 }
66 }
67 if (-1 != Position[i][j-1]){ //方向 1
68 a[i][j] = a[i][j-1];
69 a[i][j-1] = 0; //移动数字
70 flag2 = 0;
71 for (int k = 0; k <= OpenCount; k++){ //检查当前状态是否在OPEN表中
72 if (Compare(OpenList[k].State, a)){
73 flag2 = 1;
74 break;
75 }
76 }
77 if (!flag2){
78 for (int k = 0; k <= CloseCount; k++){//检查当前状态是否在CLOSE表中
79 if (Compare(CloseList[k].State, a)){
80 flag2 = 1;
81 break;
82 }
83 }
84 }
85 if (!flag2){ //若果都不在就加入OPEN表
86 Push(a, temp);
87 }
88 for (m = 0; m < 5; m++){ //复原数组a,做下一移动方向的判断
89 for (n = 0; n < 5; n++){
90 a[m][n] = temp[m][n];
91 }
92 }
93 }
94
95
96 if (-1 != Position[i][j+1]){ //方向 2
97 a[i][j] = a[i][j+1];
98 a[i][j+1] = 0; //移动数字
99 flag2 = 0;
100 for (int k = 0; k <= OpenCount; k++){
101 if (Compare(OpenList[k].State, a)){
102 flag2 = 1;
103 break;
104 }
105 }
106 if (!flag2){
107 for (int k = 0; k <= CloseCount; k++){
108 if (Compare(CloseList[k].State, a)){
109 flag2 = 1;
110 break;
111 }
112 }
113 }
114 if (!flag2){
115 Push(a, temp);
116 }
117 for (m = 0; m < 5; m++){
118 for (n = 0; n < 5; n++){
119 a[m][n] = temp[m][n];
120 }
121 }
122 }
123
124 if (-1 != Position[i+1][j]){ //方向 3
125 a[i][j] = a[i+1][j];
126 a[i+1][j] = 0; //移动数字
127 flag2 = 0;
128 for (int k = 0; k <= OpenCount; k++){
129 if (Compare(OpenList[k].State, a)){
130 flag2 = 1;
131 break;
132 }
133 }
134 if (!flag2){
135 for (int k = 0; k <= CloseCount; k++){
136 if (Compare(CloseList[k].State, a)){
137 flag2 = 1;
138 break;
139 }
140 }
141 }
142 if (!flag2){
143 Push(a, temp);
144 }
145 for (m = 0; m < 5; m++){
146 for (n = 0; n < 5; n++){
147 a[m][n] = temp[m][n];
148 }
149 }
150 }
151
152 if (-1 != Position[i-1][j]){ //方向 4
153 a[i][j] = a[i-1][j];
154 a[i-1][j] = 0; //移动数字
155 flag2 = 0;
156 for (int k = 0; k <= OpenCount; k++){
157 if (Compare(OpenList[k].State, a)){
158 flag2 = 1;
159 break;
160 }
161 }
162 if (!flag2){
163 for (int k = 0; k <= CloseCount; k++){
164 if (Compare(CloseList[k].State, a)){
165 flag2 = 1;
166 break;
167 }
168 }
169 }
170 if (!flag2){
171 Push(a, temp);
172 }
173 for (m = 0; m < 5; m++){
174 for (n = 0; n < 5; n++){
175 a[m][n] = temp[m][n];
176 }
177 }
178 }
179 }
180
181 //主函数
182 int main(int argc, char const *argv[])
183 {
184 //输出位置
185 freopen("ans.txt", "w", stdout);
186 //起始状态
187 int a[5][5], w[5][5]={
188 {-1, -1, -1, -1, -1},
189 {-1, 1, 3, 4, -1},
190 {-1, 8, 2, 5, -1},
191 {-1, 7, 0, 6, -1},
192 {-1, -1, -1, -1, -1}
193 };
194 //目标状态
195 int result[5][5] = {
196 {-1, -1, -1, -1, -1},
197 {-1, 1, 2, 3, -1},
198 {-1, 8, 0, 4, -1},
199 {-1, 7, 6, 5, -1},
200 {-1, -1, -1, -1, -1}
201 };
202
203 Push(w, w);
204 while (-1 != OpenCount){//OPEN表不为空
205 CloseCount++;
206 //每完成一次移动,该状态即可放入CLOSE表中
207 for (int i = 0; i < 5; i++){
208 for (int j = 0; j < 5; j++){
209 CloseList[CloseCount].State[i][j] = OpenList[OpenCount].State[i][j];
210 CloseList[CloseCount].FatherState[i][j] = OpenList[OpenCount].FatherState[i][j];
211 }
212 }
213 CloseList[CloseCount].Order = CloseCount;
214 //更新当前状态
215 for (int i = 0; i < 5; i++){
216 for (int j = 0; j < 5; j++){
217 a[i][j] = OpenList[OpenCount].State[i][j];
218 }
219 }
220 OpenCount--;
221 if (Compare(result, a)){ //如果达到了目标状态则技术循环
222 break;
223 } else {
224 Move(a); //继续移动
225 }
226 }
227 //输出到达目标状态的着法
228 for (int k = 0; k <= CloseCount; k++){
229 printf("No: %d\n", CloseList[k].Order);
230 printf(" Parent\t\tChild\n");
231 for (int i = 1; i < 4; i++){
232 for (int j = 1; j < 4; j++){
233 printf("%3d", CloseList[k].FatherState[i][j]);
234 }
235 printf("\t");
236 for (int j = 1; j < 4; j++){
237 printf("%3d", CloseList[k].State[i][j]);
238 }
239 printf("\n");
240 }
241 }
242 return 0;
243 }
View Code
结果
No: 0
Parent Child
1 3 4 1 3 4
8 2 5 8 2 5
7 0 6 7 0 6
No: 1
Parent Child
1 3 4 1 3 4
8 2 5 8 0 5
7 0 6 7 2 6
No: 2
Parent Child
1 3 4 1 0 4
8 0 5 8 3 5
7 2 6 7 2 6
No: 3
Parent Child
1 0 4 1 4 0
8 3 5 8 3 5
7 2 6 7 2 6
No: 4
Parent Child
1 4 0 1 4 5
8 3 5 8 3 0
7 2 6 7 2 6
No: 5
Parent Child
1 4 5 1 4 5
8 3 0 8 3 6
7 2 6 7 2 0
No: 6
Parent Child
1 4 5 1 4 5
8 3 6 8 3 6
7 2 0 7 0 2
No: 7
Parent Child
1 4 5 1 4 5
8 3 6 8 0 6
7 0 2 7 3 2
No: 8
Parent Child
1 4 5 1 0 5
8 0 6 8 4 6
7 3 2 7 3 2
No: 9
Parent Child
1 0 5 1 5 0
8 4 6 8 4 6
7 3 2 7 3 2
No: 10
Parent Child
1 5 0 1 5 6
8 4 6 8 4 0
7 3 2 7 3 2
No: 11
Parent Child
1 5 6 1 5 6
8 4 0 8 4 2
7 3 2 7 3 0
No: 12
Parent Child
1 5 6 1 5 6
8 4 2 8 4 2
7 3 0 7 0 3
No: 13
Parent Child
1 5 6 1 5 6
8 4 2 8 0 2
7 0 3 7 4 3
No: 14
Parent Child
1 5 6 1 0 6
8 0 2 8 5 2
7 4 3 7 4 3
No: 15
Parent Child
1 0 6 1 6 0
8 5 2 8 5 2
7 4 3 7 4 3
No: 16
Parent Child
1 6 0 1 6 2
8 5 2 8 5 0
7 4 3 7 4 3
No: 17
Parent Child
1 6 2 1 6 2
8 5 0 8 5 3
7 4 3 7 4 0
No: 18
Parent Child
1 6 2 1 6 2
8 5 3 8 5 3
7 4 0 7 0 4
No: 19
Parent Child
1 6 2 1 6 2
8 5 3 8 0 3
7 0 4 7 5 4
No: 20
Parent Child
1 6 2 1 0 2
8 0 3 8 6 3
7 5 4 7 5 4
No: 21
Parent Child
1 0 2 1 2 0
8 6 3 8 6 3
7 5 4 7 5 4
No: 22
Parent Child
1 2 0 1 2 3
8 6 3 8 6 0
7 5 4 7 5 4
No: 23
Parent Child
1 2 3 1 2 3
8 6 0 8 6 4
7 5 4 7 5 0
No: 24
Parent Child
1 2 3 1 2 3
8 6 4 8 6 4
7 5 0 7 0 5
No: 25
Parent Child
1 2 3 1 2 3
8 6 4 8 0 4
7 0 5 7 6 5
View Code
备注
这个八数码问题可以做成一个类似2048的游戏,不过挑战难度显然更高,不过现在GUI正在学习,恐怕要过一段时间才能做出demo。
本书最后一个是关于MAX-MIN启发式搜索,但是看的不是很明白,也从来没用过,等到以后需要再来略作研究。