SHOW:
|
|
- or go back to the newest paste.
1 | /* | |
2 | - | Optimal Denki Blocks solver |
2 | + | (Sub)Optimal Denki Blocks solver |
3 | - | Copyright (C) 2014 Michael Gottlieb |
3 | + | Copyright (C) 2014-5 Michael Gottlieb |
4 | */ | |
5 | ||
6 | ||
7 | // do we care about pairs / 3-of-a-kinds? | |
8 | #define USING_PAIRS 1 // needs to be 1 if anything in this group is | |
9 | #define NO_PAIRS 0 // forbid any pairs | |
10 | #define NO_THREE_KIND 0 // pairs are OK, but forbid 3-of-a-kinds | |
11 | #define THREE_KIND_ONLY 1 // allow 3-of-a-kind only | |
12 | ||
13 | // should we minimize matches? | |
14 | #define MINIMIZE_MATCHES 0 // needs to be 1 if anything in this group is | |
15 | #define ONE_MATCH 0 // forbid unsolved states with some but not all colors paired | |
16 | #define TWO_MATCHES_ONE 0 // forbid all states with exactly one color paired | |
17 | #define TWO_MATCHES_TWO 0 // forbid all states with exactly two colors paired | |
18 | ||
19 | // use alternate heuristic for suboptimal solves? | |
20 | #define ALTERNATE_HEURISTIC 1 | |
21 | ||
22 | // make sure we can make a shape? | |
23 | #define USING_SHAPE 0 // needs to be 1 if anything in this group is | |
24 | #define COLOR_TEST < '5' // test to see if we care about this color | |
25 | // e.g. "== '2'" for bonus shape, "< '5'" for 3 of a kind | |
26 | #define GOAL_SHAPE {0,1,2,5,6,7,16,18,19,20,21,23,24,25,32,33,34,36,37,38,39,41,50,51,52,55,56,57} | |
27 | // goal shape | |
28 | ||
29 | // for use when looking for a partial solution (set to 0 when not in use) | |
30 | #define GOAL_GROUP_NUM 0 | |
31 | ||
32 | // Main struct and control flow of program, with all includes used in it | |
33 | ||
34 | #include <algorithm> | |
35 | #include <fstream> | |
36 | #include <iostream> | |
37 | #include <list> | |
38 | #include <map> | |
39 | #include <queue> | |
40 | #include <string> | |
41 | #include <time.h> | |
42 | #include <vector> | |
43 | #include <windows.h> | |
44 | ||
45 | typedef unsigned char byte; | |
46 | ||
47 | struct Groups { | |
48 | std::vector<std::vector<int> > pieceGroups; | |
49 | std::map<int, int> inversePieceGroups; | |
50 | }; | |
51 | ||
52 | struct Moves { | |
53 | int nMoves; | |
54 | std::vector<byte> moves; | |
55 | }; | |
56 | ||
57 | struct Queued { | |
58 | int minDist; | |
59 | std::vector<byte> key; | |
60 | ||
61 | bool operator< (const Queued other) const { | |
62 | return (minDist < other.minDist); | |
63 | } | |
64 | }; | |
65 | ||
66 | // http://timday.bitbucket.org/lru.html | |
67 | class LruCache | |
68 | { | |
69 | public: | |
70 | typedef std::map<std::string, std::list<std::string>::iterator> key_lookup; | |
71 | ||
72 | LruCache(size_t c): _capacity(c) { | |
73 | } | |
74 | ||
75 | // is k in cache? | |
76 | bool contains(const std::string& k) { | |
77 | return (lookup.find(k) != lookup.end()); | |
78 | } | |
79 | ||
80 | // Obtain value of the cached function for k | |
81 | bool operator()(const std::string& k) { | |
82 | const typename key_lookup::iterator it = lookup.find(k); | |
83 | if (it==lookup.end()) { | |
84 | insert(k); | |
85 | return false; | |
86 | } else { | |
87 | keys.splice(keys.end(), keys, (*it).second); | |
88 | return true; | |
89 | } | |
90 | } | |
91 | ||
92 | // Record a fresh key in the cache | |
93 | void insert(const std::string& k) { | |
94 | if (lookup.size()==_capacity) | |
95 | evict(); | |
96 | std::list<std::string>::iterator it = keys.insert(keys.end(),k); | |
97 | lookup.insert(std::make_pair(k, it)); | |
98 | } | |
99 | ||
100 | private: | |
101 | // Purge the least-recently-used element in the cache | |
102 | void evict() { | |
103 | const typename key_lookup::iterator it = lookup.find(keys.front()); | |
104 | lookup.erase(it); | |
105 | keys.pop_front(); | |
106 | } | |
107 | ||
108 | const size_t _capacity; // Maximum number of key-value pairs to be retained | |
109 | std::list<std::string> keys; // Key access history | |
110 | key_lookup lookup; // Key lookup | |
111 | }; | |
112 | ||
113 | struct DenkiSolver { | |
114 | static void printGrid(std::string state) { | |
115 | std::cout << "+----------------+\n"; | |
116 | for (int i=0; i<16; i++) { | |
117 | std::cout << "|"; | |
118 | for (int j=0; j<16; j++) { | |
119 | if (state[i*16+j] == '0') { | |
120 | std::cout << ' '; | |
121 | } else { | |
122 | std::cout << state[i*16+j]; | |
123 | } | |
124 | } | |
125 | std::cout << "|\n"; | |
126 | } | |
127 | std::cout << "+----------------+\n"; | |
128 | } | |
129 | ||
130 | // moves: 0=U 1=D 2=L 3=R | |
131 | // print compressed moves | |
132 | static std::string printMoves(std::vector<byte> moves, int moveCount) { | |
133 | std::string moveList; | |
134 | std::string moveName = "UDLR"; | |
135 | int i; | |
136 | for (i=0; i<(moveCount/4); i++) { | |
137 | int x = (int) moves[i]; | |
138 | moveList += moveName[(x & 192) >> 6]; | |
139 | moveList += moveName[(x & 48) >> 4]; | |
140 | moveList += moveName[(x & 12) >> 2]; | |
141 | moveList += moveName[(x & 3)]; | |
142 | } | |
143 | int x = moves[i]; | |
144 | if (moveCount % 4 > 2) moveList += moveName[(x & 48) >> 4]; | |
145 | if (moveCount % 4 > 1) moveList += moveName[(x & 12) >> 2]; | |
146 | if (moveCount % 4 > 0) moveList += moveName[x & 3]; | |
147 | return moveList; | |
148 | } | |
149 | ||
150 | // add a move to an existing series of moves | |
151 | static std::vector<byte> addMove(std::vector<byte> moves, int mov, int moveCount) { | |
152 | std::vector<byte> newMoves = moves; | |
153 | if (moveCount % 4 == 0) { // add a new move byte | |
154 | newMoves.push_back(mov); | |
155 | newMoves.shrink_to_fit(); | |
156 | } else { // add moves to the last byte | |
157 | newMoves[newMoves.size() - 1] = 4*newMoves[newMoves.size() - 1] + mov; | |
158 | } | |
159 | return newMoves; | |
160 | } | |
161 | ||
162 | // can we apply this move to this piece? | |
163 | static bool isValid(int move, int n) { | |
164 | if (move == 0) { | |
165 | return (n >= 16); | |
166 | } else if (move == 1) { | |
167 | return (n <= 239); | |
168 | } else if (move == 2) { | |
169 | return (n%16 != 0); | |
170 | } else { | |
171 | return (n%16 != 15); | |
172 | } | |
173 | } | |
174 | ||
175 | // encode a string (to take up less space) | |
176 | static std::vector<byte> encode(std::string input) { | |
177 | std::vector<byte> output; | |
178 | int nEmpty = 0; | |
179 | for (int i=0; i<256; i++) { | |
180 | if (input[i] == '0' || input[i] == '1') { | |
181 | nEmpty++; | |
182 | } else { | |
183 | while (nEmpty >= 64) { | |
184 | output.push_back('9' + 64); | |
185 | nEmpty -= 64; | |
186 | } | |
187 | if (nEmpty > 0) { | |
188 | output.push_back('9' + nEmpty); | |
189 | } | |
190 | nEmpty = 0; | |
191 | output.push_back(input[i]); | |
192 | } | |
193 | } | |
194 | output.shrink_to_fit(); | |
195 | return output; | |
196 | } | |
197 | ||
198 | // decode a string | |
199 | static std::string decode(std::vector<byte> input, std::string walls) { | |
200 | std::string output = ""; | |
201 | int cnt = 0; | |
202 | for (int i=0; i<(int)input.size(); i++) { | |
203 | if (input[i] <= '9') { | |
204 | output += input[i]; | |
205 | cnt++; | |
206 | } else { | |
207 | for (int j=0; j<(int) (input[i] - '9'); j++) { | |
208 | output += walls[cnt]; | |
209 | cnt++; | |
210 | } | |
211 | } | |
212 | } | |
213 | while (cnt < 256) { | |
214 | output += walls[cnt]; | |
215 | cnt++; | |
216 | } | |
217 | return output; | |
218 | } | |
219 | ||
220 | // get pieceGroups and inversePieceGroups | |
221 | static Groups getGroups(std::string position) { | |
222 | Groups groups; | |
223 | ||
224 | // initialize visited array | |
225 | std::vector<int> visited (256, 0); | |
226 | for (int i=0; i<256; i++) { | |
227 | if (position[i] <= '1') { | |
228 | visited[i] = 1; | |
229 | } | |
230 | } | |
231 | ||
232 | // look for piece groups | |
233 | int groupCount = 0; | |
234 | for (int i=0; i<256; i++) { | |
235 | if (visited[i] == 1) continue; | |
236 | std::vector<int> group; | |
237 | int groupI = 0; | |
238 | char correct = position[i]; | |
239 | group.push_back(i); | |
240 | visited[i] = 1; | |
241 | groups.inversePieceGroups[i] = groupCount; | |
242 | while (groupI < (int) group.size()) { | |
243 | int x = group[groupI]; | |
244 | for (int mov=0; mov<4; mov++) { | |
245 | if (isValid(mov, x)) { | |
246 | int add = moveAddition(mov); | |
247 | if (visited[x+add]==0 && position[x+add]==correct) { | |
248 | group.push_back(x+add); | |
249 | visited[x+add] = 1; | |
250 | groups.inversePieceGroups[x+add] = groupCount; | |
251 | } | |
252 | } | |
253 | } | |
254 | groupI++; | |
255 | } | |
256 | #if USING_PAIRS == 1 || USING_SHAPE == 1 | |
257 | std::sort(group.begin(), group.end()); | |
258 | #endif | |
259 | groups.pieceGroups.push_back(group); | |
260 | groupCount++; | |
261 | } | |
262 | ||
263 | return groups; | |
264 | } | |
265 | ||
266 | // determine which piece groups can move | |
267 | static std::vector<int> whatCanMove(int move, Groups groups, std::string position) { | |
268 | // 0 = not processed | |
269 | // 1 = can move | |
270 | // 2 = can't move | |
271 | // 3 = in use, not analyzed | |
272 | // 4 = in use, analyzed | |
273 | ||
274 | int add = moveAddition(move); | |
275 | std::vector<std::vector<int> > *pieceGroups = &groups.pieceGroups; | |
276 | int groupCount = (int) pieceGroups->size(); | |
277 | std::map<int, int> *inversePieceGroups = &groups.inversePieceGroups; | |
278 | ||
279 | std::vector<int> canMove (groupCount, 0); | |
280 | for (int i=0; i<groupCount; i++) { | |
281 | if (canMove[i] > 0) continue; | |
282 | canMove[i] = 3; // in use, not analyzed | |
283 | int curGroup = i; | |
284 | while (curGroup != -1) { | |
285 | // check the position above each piece in the group | |
286 | std::vector<int> *thisGroup = &((*pieceGroups)[curGroup]); | |
287 | for (int j=0; j<(int) thisGroup->size(); j++) { | |
288 | // if at the edge, no good | |
289 | if (!isValid(move, (*thisGroup)[j])) { | |
290 | canMove[i] = 2; | |
291 | break; | |
292 | } | |
293 | char pieceForward = position[(*thisGroup)[j] + add]; | |
294 | // if there's a wall in the way, no good | |
295 | if (pieceForward == '1') { | |
296 | canMove[i] = 2; | |
297 | break; | |
298 | } | |
299 | // if there's another piece in the way... | |
300 | if (pieceForward > '1') { | |
301 | int groupForward = (*inversePieceGroups)[(*thisGroup)[j] + add]; | |
302 | // if that other group can't move, neither can we | |
303 | if (canMove[groupForward] == 2) { | |
304 | canMove[i] = 2; | |
305 | break; | |
306 | } | |
307 | // if that other group isn't processed, work on it | |
308 | if (canMove[groupForward] == 0) { | |
309 | canMove[groupForward] = 3; | |
310 | } | |
311 | } | |
312 | } | |
313 | ||
314 | if (canMove[i] == 2) break; | |
315 | ||
316 | // done with this group | |
317 | canMove[curGroup] = 4; | |
318 | ||
319 | // get another working group | |
320 | curGroup = -1; | |
321 | for (int j=0; j<groupCount; j++) { | |
322 | if (canMove[j] == 3) { | |
323 | curGroup = j; | |
324 | } | |
325 | } | |
326 | } | |
327 | ||
328 | // so could we move this group? | |
329 | if (canMove[i] == 2) { // no | |
330 | for (int j=0; j<groupCount; j++) { | |
331 | if (canMove[j] > 2) { | |
332 | canMove[j] = 0; | |
333 | } | |
334 | } | |
335 | } else { // yes - all intermediate groups can be moved too | |
336 | for (int j=0; j<groupCount; j++) { | |
337 | if (canMove[j] > 2) { | |
338 | canMove[j] = 1; | |
339 | } | |
340 | } | |
341 | } | |
342 | } | |
343 | ||
344 | return canMove; | |
345 | } | |
346 | ||
347 | static std::string applyMove(int mov, Groups groups, std::string position) { | |
348 | // determine what can move | |
349 | std::vector<int> canMove = whatCanMove(mov, groups, position); | |
350 | ||
351 | // move 'em | |
352 | std::string newPosition = position; | |
353 | std::map<int, int> *inversePieceGroups = &groups.inversePieceGroups; | |
354 | if (mov == 0) { // up | |
355 | for (int y=0; y<15; y++) { | |
356 | for (int x=0; x<16; x++) { | |
357 | if (position[y*16 + x + 16] > '1') { | |
358 | if (canMove[(*inversePieceGroups)[y*16 + x + 16]] == 1) { | |
359 | newPosition[y*16 + x] = newPosition[y*16 + x + 16]; | |
360 | newPosition[y*16 + x + 16] = '0'; | |
361 | } | |
362 | } | |
363 | } | |
364 | } | |
365 | } else if (mov == 1) { // down | |
366 | for (int y=15; y>0; y--) { | |
367 | for (int x=0; x<16; x++) { | |
368 | if (position[y*16 + x - 16] > '1') { | |
369 | if (canMove[(*inversePieceGroups)[y*16 + x - 16]] == 1) { | |
370 | newPosition[y*16 + x] = newPosition[y*16 + x - 16]; | |
371 | newPosition[y*16 + x - 16] = '0'; | |
372 | } | |
373 | } | |
374 | } | |
375 | } | |
376 | } else if (mov == 2) { // left | |
377 | for (int y=0; y<16; y++) { | |
378 | for (int x=0; x<15; x++) { | |
379 | if (position[y*16 + x + 1] > '1') { | |
380 | if (canMove[(*inversePieceGroups)[y*16 + x + 1]] == 1) { | |
381 | newPosition[y*16 + x] = newPosition[y*16 + x + 1]; | |
382 | newPosition[y*16 + x + 1] = '0'; | |
383 | } | |
384 | } | |
385 | } | |
386 | } | |
387 | } else { // right | |
388 | for (int y=0; y<16; y++) { | |
389 | for (int x=15; x>0; x--) { | |
390 | if (position[y*16 + x - 1] > '1') { | |
391 | if (canMove[(*inversePieceGroups)[y*16 + x - 1]] == 1) { | |
392 | newPosition[y*16 + x] = newPosition[y*16 + x - 1]; | |
393 | newPosition[y*16 + x - 1] = '0'; | |
394 | } | |
395 | } | |
396 | } | |
397 | } | |
398 | } | |
399 | return newPosition; | |
400 | } | |
401 | ||
402 | // check if solved | |
403 | static int isSolved(std::string position, std::vector<std::vector<int> > pieceGroups) { | |
404 | #if GOAL_GROUP_NUM > 0 | |
405 | // for partial solutions | |
406 | if (pieceGroups.size() <= GOAL_GROUP_NUM) { | |
407 | return 1; | |
408 | } else { | |
409 | return 0; | |
410 | } | |
411 | #endif | |
412 | ||
413 | int group2=0, group3=0, group4=0; // how many of each of the 3 matching colors | |
414 | for (int i=0; i<(int) pieceGroups.size(); i++) { | |
415 | char correct = position[pieceGroups[i][0]]; | |
416 | if (correct == '2') { | |
417 | group2++; | |
418 | } else if (correct == '3') { | |
419 | group3++; | |
420 | } else if (correct == '4') { | |
421 | group4++; | |
422 | } | |
423 | } | |
424 | ||
425 | if (group2<2 && group3<2 && group4<2) { | |
426 | #if USING_PAIRS == 1 | |
427 | // OK, it's solved. but do we have a pair? | |
428 | int only2=-1, only3=-1, only4=-1; | |
429 | for (int i=0; i<(int) pieceGroups.size(); i++) { | |
430 | char correct = position[pieceGroups[i][0]]; | |
431 | if (correct == '2') { | |
432 | only2 = i; | |
433 | } else if (correct == '3') { | |
434 | only3 = i; | |
435 | } else if (correct == '4') { | |
436 | only4 = i; | |
437 | } | |
438 | } | |
439 | #if NO_PAIRS == 1 | |
440 | if (only2 > -1 && only3 > -1 && sameShape(pieceGroups[only2], pieceGroups[only3]) == 1) | |
441 | return -1; | |
442 | if (only2 > -1 && only4 > -1 && sameShape(pieceGroups[only2], pieceGroups[only4]) == 1) | |
443 | return -1; | |
444 | if (only3 > -1 && only4 > -1 && sameShape(pieceGroups[only3], pieceGroups[only4]) == 1) | |
445 | return -1; | |
446 | return 1; | |
447 | #endif | |
448 | #if NO_THREE_KIND == 1 | |
449 | if (only2 > -1 && only3 > -1 && sameShape(pieceGroups[only2], pieceGroups[only3]) != 1) | |
450 | return 1; | |
451 | if (only2 > -1 && only4 > -1 && sameShape(pieceGroups[only2], pieceGroups[only4]) != 1) | |
452 | return 1; | |
453 | if (only3 > -1 && only4 > -1 && sameShape(pieceGroups[only3], pieceGroups[only4]) != 1) | |
454 | return 1; | |
455 | return -1; | |
456 | #endif | |
457 | #if THREE_KIND_ONLY == 1 | |
458 | if (only2 > -1 && only3 > -1 && sameShape(pieceGroups[only2], pieceGroups[only3]) != 1) | |
459 | return -1; | |
460 | if (only2 > -1 && only4 > -1 && sameShape(pieceGroups[only2], pieceGroups[only4]) != 1) | |
461 | return -1; | |
462 | if (only3 > -1 && only4 > -1 && sameShape(pieceGroups[only3], pieceGroups[only4]) != 1) | |
463 | return -1; | |
464 | return 1; | |
465 | #endif | |
466 | #endif | |
467 | return 1; | |
468 | #if ONE_MATCH == 1 | |
469 | } else if ((group2>1 || group3>1 || group4>1) && (group2==1 || group3==1 || group4==1)) { | |
470 | return -1; | |
471 | - | static int minDistance(std::string &position, Groups &groups, std::vector<int> &distances, int groupPenalty) { |
471 | + | #endif |
472 | #if TWO_MATCHES_ONE == 1 | |
473 | // don't allow exactly 1 thing matched | |
474 | } else if ((group2==1 && group3!=1 && group4!=1) || (group2!=1 && group3==1 && group4!=1) || (group2!=1 && group3!=1 && group4==1)) { | |
475 | return -1; | |
476 | #endif | |
477 | #if TWO_MATCHES_TWO == 1 | |
478 | // don't allow exactly 2 things matched | |
479 | } else if ((group2==1 && group3==1 && group4!=1) || (group2==1 && group3!=1 && group4==1) || (group2!=1 && group3==1 && group4==1)) { | |
480 | return -1; | |
481 | #endif | |
482 | } else { | |
483 | return 0; | |
484 | } | |
485 | } | |
486 | ||
487 | // do these two groups have the same shape? | |
488 | // the two groups should be sorted | |
489 | static int sameShape(std::vector<int> group1, std::vector<int> group2) { | |
490 | if (group1.size() != group2.size()) { | |
491 | return 0; | |
492 | } | |
493 | int dx = (group2[0]%16) - (group1[0]%16); | |
494 | int dy = (group2[0]/16) - (group1[0]/16); | |
495 | for (int i=1; i<(int) group1.size(); i++) { | |
496 | if ((group2[i]%16) - (group1[i]%16) != dx) | |
497 | return 0; | |
498 | if ((group2[i]/16) - (group1[i]/16) != dy) | |
499 | return 0; | |
500 | } | |
501 | return 1; | |
502 | } | |
503 | ||
504 | static int moveAddition(int mov) { | |
505 | // -16, 16, -1, 1 | |
506 | if (mov == 0) { | |
507 | return -16; | |
508 | } else if (mov == 1) { | |
509 | return 16; | |
510 | } else if (mov == 2) { | |
511 | return -1; | |
512 | } else if (mov == 3) { | |
513 | return 1; | |
514 | - | // report maximum distance |
514 | + | |
515 | return 0; | |
516 | } | |
517 | } | |
518 | - | if (groupDistances[i*groupSize+j] > distance) { |
518 | + | |
519 | - | distance = groupDistances[i*groupSize+j]; |
519 | + | |
520 | int inTaboo = 0; | |
521 | for (int i=0; i<(int) taboo.size(); i++) { | |
522 | if (taboo[i] == position) { | |
523 | inTaboo = 1; | |
524 | break; | |
525 | } | |
526 | } | |
527 | return inTaboo; | |
528 | } | |
529 | ||
530 | // get distances between every pair of positions | |
531 | // allowed moves: | |
532 | // - move one piece in a direction | |
533 | // - move both pieces in same direction | |
534 | static std::vector<int> getManhattanDistances(std::string walls) { | |
535 | // walls are '0's and '1's | |
536 | std::vector<int> distances (256*256, -1); | |
537 | ||
538 | for (int i=0; i<256; i++) { | |
539 | if (walls[i] == '0') | |
540 | distances[i*257] = 0; | |
541 | } | |
542 | ||
543 | int depth = 0; | |
544 | int foundPositions = 1; | |
545 | // loop by depth | |
546 | while (foundPositions > 0) { | |
547 | foundPositions = 0; | |
548 | // find point pairs of this depth | |
549 | for (int i=0; i<256; i++) { | |
550 | for (int j=0; j<256; j++) { | |
551 | int index = i*256+j; | |
552 | if (distances[index] == depth) { | |
553 | foundPositions++; | |
554 | ||
555 | // what pairs of points are at depth+1? | |
556 | for (int mov=0; mov<4; mov++) { | |
557 | if (isValid(mov,i) && walls[i+moveAddition(mov)] == '0' && distances[index + moveAddition(mov)*256] == -1) { // move i only | |
558 | distances[index + moveAddition(mov)*256] = depth+1; | |
559 | } | |
560 | if (isValid(mov,j) && walls[j+moveAddition(mov)] == '0' && distances[index + moveAddition(mov)] == -1) { // move j only | |
561 | distances[index + moveAddition(mov)] = depth+1; | |
562 | } | |
563 | if (isValid(mov,i) && isValid(mov,j) && walls[i+moveAddition(mov)] == '0' && walls[j+moveAddition(mov)] == '0' && distances[index + moveAddition(mov)*257] == -1) { // move both i and j | |
564 | distances[index + moveAddition(mov)*257] = depth+1; | |
565 | } | |
566 | } | |
567 | } | |
568 | } | |
569 | } | |
570 | depth++; | |
571 | } | |
572 | ||
573 | return distances; | |
574 | } | |
575 | ||
576 | // heuristic for minimum possible moves to solve this position | |
577 | // OK so | |
578 | // right now we are just finding the group that is the furthest from the next group and getting its minimum distance | |
579 | // what we really want is the furthest a set of groups can be from everything else | |
580 | ||
581 | // new idea: find max distance between two groups (using triangle inequality) | |
582 | static int minDistance(std::string &position, Groups &groups, std::vector<int> &distances, int groupPenalty, int alternate) { | |
583 | - | if (minDistance(position, groups, distances, 0) > remaining_depth) return 0; |
583 | + | |
584 | std::vector<std::vector<int> > *pieceGroups = &groups.pieceGroups; | |
585 | int groupSize = (int) pieceGroups->size(); | |
586 | std::vector<int> groupDistances (groupSize*groupSize, -1); | |
587 | for (int i=0; i<groupSize; i++) { | |
588 | groupDistances[i*groupSize+i] = 0; | |
589 | char iColor = position[(*pieceGroups)[i][0]]; | |
590 | if (iColor < '2' || iColor > '4') continue; | |
591 | for (int j=0; j<groupSize; j++) { | |
592 | if (i==j) continue; | |
593 | if (iColor != position[(*pieceGroups)[j][0]]) continue; | |
594 | ||
595 | // minimum distance between piece in group and piece in other group | |
596 | int groupDistance = -1; | |
597 | for (int pi = 0; pi<(int)(*pieceGroups)[i].size(); pi++) { | |
598 | for (int pj = 0; pj<(int)(*pieceGroups)[j].size(); pj++) { | |
599 | int pDistance = distances[256*(*pieceGroups)[i][pi]+(*pieceGroups)[j][pj]]; | |
600 | if (pDistance < groupDistance || groupDistance == -1) { | |
601 | groupDistance = pDistance; | |
602 | } | |
603 | } | |
604 | } | |
605 | groupDistances[i*groupSize+j] = groupDistance - 1; // tiles only need to get to distance 1 to connect | |
606 | } | |
607 | } | |
608 | ||
609 | // Floyd-Warshall? | |
610 | // combining edges of size X and Y is max(X,Y), not X+Y | |
611 | for (int k=0; k<groupSize; k++) { | |
612 | for (int i=0; i<groupSize; i++) { | |
613 | for (int j=0; j<groupSize; j++) { | |
614 | if (groupDistances[i*groupSize+j] > groupDistances[i*groupSize+k] && | |
615 | groupDistances[i*groupSize+k] != -1 && groupDistances[k*groupSize+j] != -1 && | |
616 | groupDistances[i*groupSize+j] > groupDistances[k*groupSize+j]) { | |
617 | groupDistances[i*groupSize+j] = groupDistances[i*groupSize+k]; | |
618 | if (groupDistances[i*groupSize+j] < groupDistances[k*groupSize+j]) | |
619 | groupDistances[i*groupSize+j] = groupDistances[k*groupSize+j]; | |
620 | } | |
621 | } | |
622 | } | |
623 | } | |
624 | ||
625 | int distance = -1; | |
626 | if (alternate == 0) { | |
627 | // report maximum distance | |
628 | for (int i=0; i<groupSize; i++) { | |
629 | for (int j=0; j<groupSize; j++) { | |
630 | if (groupDistances[i*groupSize+j] > distance) { | |
631 | distance = groupDistances[i*groupSize+j]; | |
632 | } | |
633 | } | |
634 | } | |
635 | } else if (alternate == 1) { | |
636 | // report maximum distance | |
637 | int distance2 = 0; | |
638 | int distance3 = 0; | |
639 | int distance4 = 0; | |
640 | for (int i=0; i<groupSize; i++) { | |
641 | if (position[(*pieceGroups)[i][0]] == '2') { | |
642 | for (int j=0; j<groupSize; j++) { | |
643 | if (groupDistances[i*groupSize+j] > distance2) { | |
644 | distance2 = groupDistances[i*groupSize+j]; | |
645 | } | |
646 | } | |
647 | } else if (position[(*pieceGroups)[i][0]] == '3') { | |
648 | - | if (isSolved(position, groups.pieceGroups) == 1) { |
648 | + | for (int j=0; j<groupSize; j++) { |
649 | if (groupDistances[i*groupSize+j] > distance3) { | |
650 | distance3 = groupDistances[i*groupSize+j]; | |
651 | } | |
652 | } | |
653 | } else if (position[(*pieceGroups)[i][0]] == '4') { | |
654 | for (int j=0; j<groupSize; j++) { | |
655 | if (groupDistances[i*groupSize+j] > distance4) { | |
656 | distance4 = groupDistances[i*groupSize+j]; | |
657 | } | |
658 | - | if (minDistance(position, groups, distances, 0) + main_depth >= goalMoves) { |
658 | + | |
659 | } | |
660 | } | |
661 | distance = distance2 + distance3 + distance4; | |
662 | } | |
663 | ||
664 | return distance + groupSize * groupPenalty; | |
665 | } | |
666 | ||
667 | static std::string getWalls(std::string puzzle) { | |
668 | std::string walls = ""; | |
669 | for (int i=0; i<256; i++) { | |
670 | if (puzzle[i] == '1') { | |
671 | walls += '1'; | |
672 | } else { | |
673 | walls += '0'; | |
674 | } | |
675 | } | |
676 | return walls; | |
677 | } | |
678 | ||
679 | // A BUNCH OF WAYS TO SOLVE STUFF | |
680 | // solve with iterative deepening | |
681 | static void iterativeDeepeningSolve(std::string puzzle) { | |
682 | std::string walls = getWalls(puzzle); | |
683 | ||
684 | // start search | |
685 | std::vector<std::string> noTaboo; | |
686 | std::vector<int> distances = getManhattanDistances(walls); | |
687 | int depth = 0; | |
688 | int foundSolutions = 0; | |
689 | clock_t start = clock(); | |
690 | while (foundSolutions == 0) { | |
691 | int solutions = iterativeDeepeningSearch(puzzle, walls, distances, depth, "", noTaboo); | |
692 | clock_t t = clock() - start; | |
693 | if (solutions > 0) { | |
694 | std::cout << "Solutions: " << solutions << " at depth " << depth << " (t = " << t << ")\n"; | |
695 | foundSolutions = 1; | |
696 | } else { | |
697 | std::cout << "Depth " << depth << " done (t = " << t << ")\n"; | |
698 | depth++; | |
699 | } | |
700 | } | |
701 | } | |
702 | ||
703 | // position = position, walls = precomputed walls of position, | |
704 | // remaining_depth = moves left to search, moves = move string so far | |
705 | static int iterativeDeepeningSearch(std::string position, std::string walls, std::vector<int> distances, int remaining_depth, std::string moves, std::vector<std::string> taboo) { | |
706 | //std::cout << moves << "\n"; | |
707 | int solutions = 0; | |
708 | ||
709 | // figure out piece groups | |
710 | Groups groups = getGroups(position); | |
711 | ||
712 | if (looksSolvable(position, groups) == 0) return 0; | |
713 | ||
714 | // check for solved if we are out of moves | |
715 | if (remaining_depth <= 0) { | |
716 | int solved = isSolved(position, groups.pieceGroups); | |
717 | if (solved==1) { | |
718 | std::cout << "Found solution: " << moves << "\n"; | |
719 | } | |
720 | return solved; | |
721 | } | |
722 | ||
723 | if (minDistance(position, groups, distances, 0, 0) > remaining_depth) return 0; | |
724 | ||
725 | - | if (isSolved(position, groups.pieceGroups) == 1) { |
725 | + | |
726 | std::vector<std::string> newTaboo; | |
727 | newTaboo.push_back(position); | |
728 | ||
729 | // try U move | |
730 | std::string uPosition = applyMove(0, groups, position); | |
731 | if (uPosition != position && inTaboo(uPosition, taboo) == 0) solutions += iterativeDeepeningSearch(uPosition, walls, distances, remaining_depth-1, moves+"U", newTaboo); | |
732 | ||
733 | // try D move | |
734 | std::string dPosition = applyMove(1, groups, position); | |
735 | - | if (minDistance(position, groups, distances, 0) + main_depth >= goalMoves) { |
735 | + | |
736 | ||
737 | Groups uGroups = getGroups(uPosition); | |
738 | Groups dGroups = getGroups(dPosition); | |
739 | ||
740 | // try L move (but no LU/LD if equal to UL/DL) | |
741 | std::string lPosition = applyMove(2, groups, position); | |
742 | if (lPosition != position && inTaboo(lPosition, taboo) == 0) { | |
743 | std::vector<std::string> lTaboo = newTaboo; | |
744 | lTaboo.push_back(applyMove(2, uGroups, uPosition)); | |
745 | lTaboo.push_back(applyMove(2, dGroups, dPosition)); | |
746 | solutions += iterativeDeepeningSearch(lPosition, walls, distances, remaining_depth-1, moves+"L", lTaboo); | |
747 | } | |
748 | ||
749 | // try R move (but no RU/RD if equal to UR/DR) | |
750 | std::string rPosition = applyMove(3, groups, position); | |
751 | if (rPosition != position && inTaboo(rPosition, taboo) == 0) { | |
752 | std::vector<std::string> rTaboo = newTaboo; | |
753 | rTaboo.push_back(applyMove(3, uGroups, uPosition)); | |
754 | rTaboo.push_back(applyMove(3, dGroups, dPosition)); | |
755 | solutions += iterativeDeepeningSearch(rPosition, walls, distances, remaining_depth-1, moves+"R", rTaboo); | |
756 | } | |
757 | ||
758 | return solutions; | |
759 | } | |
760 | ||
761 | // solve by enumerating every position in one map | |
762 | static void singleMapSolve(std::string puzzle, int goalMoves, int maxStates) { | |
763 | std::string walls = getWalls(puzzle); | |
764 | ||
765 | // start with empty map (state -> <depth, movestring>) | |
766 | std::map<std::vector<byte>, Moves> positions; | |
767 | // insert starting state at depth 0, set main_depth = 0 | |
768 | std::vector<byte> noMoves; | |
769 | positions[encode(puzzle)] = (Moves) {0, noMoves}; | |
770 | int foundSolution = 0; | |
771 | int main_depth = 0; | |
772 | - | // TEMPORARY - FOR SOLVING GIVEN PUZZLE |
772 | + | |
773 | ||
774 | Groups groups = getGroups(puzzle); | |
775 | ||
776 | while (foundSolution == 0) { | |
777 | // iterate over map to look for depth == main_depth. for each | |
778 | int foundPosition = 0; | |
779 | for (std::map<std::vector<byte>, Moves>::iterator it=positions.begin(); it!=positions.end(); ++it) { | |
780 | if (it->second.nMoves == main_depth) { | |
781 | foundPosition = 1; | |
782 | ||
783 | std::string position = decode(it->first, walls); | |
784 | Groups groups = getGroups(position); | |
785 | ||
786 | if (looksSolvable(position, groups) == 0) continue; | |
787 | ||
788 | int solved = isSolved(position, groups.pieceGroups); | |
789 | if (solved == 1) { | |
790 | foundSolution = 1; | |
791 | std::cout << "Found solution: " << printMoves(it->second.moves, main_depth) << "\n"; | |
792 | break; | |
793 | #if MINIMIZE_MATCHES == 1 | |
794 | } else if (solved == -1) { | |
795 | continue; | |
796 | #endif | |
797 | } | |
798 | ||
799 | if ((int) positions.size() > maxStates) { | |
800 | continue; | |
801 | } | |
802 | ||
803 | if (minDistance(position, groups, distances, 0, 0) + main_depth >= goalMoves) { | |
804 | continue; | |
805 | } | |
806 | ||
807 | for (int mov = 0; mov < 4; mov++) { | |
808 | std::string newPosition = applyMove(mov, groups, position); | |
809 | // add this position into the map if it isn't already there | |
810 | if (positions.count(encode(newPosition)) == 0) { | |
811 | positions[encode(newPosition)] = (Moves) {it->second.nMoves + 1, addMove(it->second.moves, mov, it->second.nMoves)}; | |
812 | } | |
813 | } | |
814 | } | |
815 | } | |
816 | ||
817 | if (foundPosition == 0) { | |
818 | std::cout << "Could not find solution.\n"; | |
819 | foundSolution = 1; | |
820 | } | |
821 | ||
822 | main_depth++; | |
823 | if (foundSolution == 0) { | |
824 | std::cout << "Depth " << main_depth << ", positions " << positions.size() << "\n"; | |
825 | - | if (isSolved(position, groups.pieceGroups) == 1) { |
825 | + | |
826 | } | |
827 | } | |
828 | ||
829 | // solve by enumerating every position with one map per depth | |
830 | static void moduloMapSolve(std::string puzzle, int goalMoves, int nMaps, int maxStates) { | |
831 | - | if (minDistance(position, groups, distances, 0) + main_depth >= goalMoves) { |
831 | + | |
832 | ||
833 | // start with empty maps (state -> <depth, movestring>) | |
834 | std::vector<std::map<std::vector<byte>, std::vector<byte> > > positions; | |
835 | for (int i=0; i<nMaps; i++) { | |
836 | std::map<std::vector<byte>, std::vector<byte> > subPositions; | |
837 | positions.push_back(subPositions); | |
838 | } | |
839 | ||
840 | // insert starting state at depth 0, set main_depth = 0 | |
841 | std::vector<byte> noMoves; | |
842 | positions[0][encode(puzzle)] = noMoves; | |
843 | int foundSolution = 0; | |
844 | int main_depth = 0; | |
845 | std::vector<int> distances = getManhattanDistances(walls); | |
846 | ||
847 | Groups groups = getGroups(puzzle); | |
848 | ||
849 | - | int minDist = minDistance(newPosition, newGroups, distances, penalty); |
849 | + | |
850 | // get current and next position maps | |
851 | std::map<std::vector<byte>, std::vector<byte> > *curPositions = &positions[main_depth % nMaps]; | |
852 | std::map<std::vector<byte>, std::vector<byte> > *nextPositions = &positions[(main_depth + 1) % nMaps]; | |
853 | nextPositions->clear(); | |
854 | ||
855 | int maxPositions = maxStates; | |
856 | for (int i=0; i<nMaps; i++) { | |
857 | maxPositions -= positions[i].size(); | |
858 | } | |
859 | ||
860 | // iterate over map | |
861 | int foundPosition = 0; | |
862 | for (std::map<std::vector<byte>, std::vector<byte> >::iterator it=curPositions->begin(); it!=curPositions->end(); ++it) { | |
863 | foundPosition = 1; | |
864 | ||
865 | std::string position = decode(it->first, walls); | |
866 | Groups groups = getGroups(position); | |
867 | ||
868 | if (looksSolvable(position, groups) == 0) continue; | |
869 | ||
870 | int solved = isSolved(position, groups.pieceGroups); | |
871 | if (solved == 1) { | |
872 | foundSolution = 1; | |
873 | std::cout << "Found solution: " << printMoves(it->second, main_depth) << "\n"; | |
874 | break; | |
875 | #if MINIMIZE_MATCHES == 1 | |
876 | } else if (solved == -1) { | |
877 | continue; | |
878 | #endif | |
879 | } | |
880 | ||
881 | if ((int) nextPositions->size() > maxPositions) { | |
882 | continue; | |
883 | } | |
884 | ||
885 | if (minDistance(position, groups, distances, 0, 0) + main_depth >= goalMoves) { | |
886 | continue; | |
887 | } | |
888 | ||
889 | // loop through moves | |
890 | for (int mov = 0; mov < 4; mov++) { | |
891 | std::string newPosition = applyMove(mov, groups, position); | |
892 | int positionExists = 0; | |
893 | std::vector<byte> encoded = encode(newPosition); | |
894 | for (int i=0; i<nMaps; i++) { | |
895 | if (positions[i].count(encoded) != 0) { | |
896 | positionExists = 1; | |
897 | break; | |
898 | } | |
899 | } | |
900 | if (positionExists == 0) { | |
901 | (*nextPositions)[encoded] = addMove(it->second, mov, main_depth); | |
902 | } | |
903 | } | |
904 | } | |
905 | ||
906 | if (foundPosition == 0) { | |
907 | std::cout << "Could not find solution.\n"; | |
908 | foundSolution = 1; | |
909 | } | |
910 | ||
911 | main_depth++; | |
912 | if (foundSolution == 0) { | |
913 | std::cout << "Depth " << main_depth << ", positions " << nextPositions->size() << "\n"; | |
914 | } | |
915 | ||
916 | if ((int) nextPositions->size() > maxPositions) { | |
917 | break; | |
918 | } | |
919 | } | |
920 | } | |
921 | ||
922 | #if USING_SHAPE == 1 | |
923 | static int allInShape(std::string &position, Groups &groups) { | |
924 | std::vector<int> goalShape; | |
925 | goalShape = GOAL_SHAPE; | |
926 | ||
927 | // want to make sure each group of large-enough size is part of the goal shape | |
928 | int groupCount = groups.pieceGroups.size(); | |
929 | for (int i=0; i<groupCount; i++) { | |
930 | std::vector<int> subShape = groups.pieceGroups[i]; | |
931 | if (position[subShape[0]] COLOR_TEST && subShape.size() >= 2) { | |
932 | // is this group part of the goal shape? | |
933 | int found = 0; | |
934 | for (int ctr = 0; ctr < (int) goalShape.size(); ctr++) { | |
935 | int subPtr = 0; | |
936 | int goalPtr = ctr; | |
937 | while (subPtr < (int)subShape.size() && goalPtr < (int)goalShape.size()) { | |
938 | if (subShape[subPtr]-subShape[0] == goalShape[goalPtr]-goalShape[ctr]) { | |
939 | subPtr++; | |
940 | } | |
941 | goalPtr++; | |
942 | } | |
943 | if (subPtr == (int)subShape.size()) { | |
944 | found = 1; | |
945 | break; | |
946 | } | |
947 | } | |
948 | if (found == 0) { | |
949 | return 0; | |
950 | } | |
951 | } | |
952 | } | |
953 | return 1; | |
954 | } | |
955 | #endif | |
956 | ||
957 | static int looksSolvable(std::string &position, Groups &groups) { | |
958 | #if USING_SHAPE == 1 | |
959 | if (allInShape(position, groups) == 0) return 0; | |
960 | #endif | |
961 | ||
962 | // put any puzzle-specific code here! | |
963 | ||
964 | return 1; | |
965 | } | |
966 | - | if (minDistance(position, groups, distances, 0) > remaining_depth) return 0; |
966 | + | |
967 | ||
968 | ||
969 | // solve by enumerating almost every position with one map per depth | |
970 | // drop states if we need to so we don't have more than maxStatesPer per depth | |
971 | static int moduloMapSuboptimalSolve(std::string puzzle, int goalMoves, int nMaps, int maxStatesPer, int penalty) { | |
972 | std::string walls = getWalls(puzzle); | |
973 | ||
974 | // start with empty maps (state -> <depth, movestring>) | |
975 | std::vector<std::map<std::vector<byte>, std::vector<byte> > > positions; | |
976 | for (int i=0; i<nMaps; i++) { | |
977 | std::map<std::vector<byte>, std::vector<byte> > subPositions; | |
978 | positions.push_back(subPositions); | |
979 | } | |
980 | ||
981 | // insert starting state at depth 0, set main_depth = 0 | |
982 | std::vector<byte> noMoves; | |
983 | positions[0][encode(puzzle)] = noMoves; | |
984 | int foundSolution = 0; | |
985 | int main_depth = 0; | |
986 | std::vector<int> distances = getManhattanDistances(walls); | |
987 | ||
988 | Groups groups = getGroups(puzzle); | |
989 | ||
990 | while (foundSolution == 0) { | |
991 | // get current and next position maps | |
992 | std::map<std::vector<byte>, std::vector<byte> > *curPositions = &positions[main_depth % nMaps]; | |
993 | std::map<std::vector<byte>, std::vector<byte> > *nextPositions = &positions[(main_depth + 1) % nMaps]; | |
994 | nextPositions->clear(); | |
995 | std::priority_queue<Queued> queued; | |
996 | ||
997 | //int printed = 0; | |
998 | ||
999 | // iterate over map | |
1000 | int foundPosition = 0; | |
1001 | for (std::map<std::vector<byte>, std::vector<byte> >::iterator it=curPositions->begin(); it!=curPositions->end(); ++it) { | |
1002 | foundPosition = 1; | |
1003 | ||
1004 | std::string position = decode(it->first, walls); | |
1005 | Groups groups = getGroups(position); | |
1006 | ||
1007 | if (looksSolvable(position, groups) == 0) continue; | |
1008 | ||
1009 | //if (printed == 0) { | |
1010 | // printGrid(position); | |
1011 | // printed = 1; | |
1012 | //} | |
1013 | ||
1014 | int solved = isSolved(position, groups.pieceGroups); | |
1015 | if (solved == 1) { | |
1016 | foundSolution = 1; | |
1017 | std::cout << "Found suboptimal solution: " << printMoves(it->second, main_depth) << "\n"; | |
1018 | return main_depth; | |
1019 | #if MINIMIZE_MATCHES == 1 | |
1020 | } else if (solved == -1) { | |
1021 | continue; | |
1022 | #endif | |
1023 | } | |
1024 | ||
1025 | if (minDistance(position, groups, distances, 0, 0) + main_depth >= goalMoves) { | |
1026 | continue; | |
1027 | } | |
1028 | ||
1029 | // loop through moves | |
1030 | for (int mov = 0; mov < 4; mov++) { | |
1031 | std::string newPosition = applyMove(mov, groups, position); | |
1032 | int positionExists = 0; | |
1033 | std::vector<byte> encoded = encode(newPosition); | |
1034 | for (int i=0; i<nMaps; i++) { | |
1035 | if (positions[i].count(encoded) != 0) { | |
1036 | positionExists = 1; | |
1037 | break; | |
1038 | } | |
1039 | } | |
1040 | if (positionExists == 0) { | |
1041 | // add to both nextPositions and priority queue | |
1042 | Groups newGroups = getGroups(newPosition); | |
1043 | int minDist = minDistance(newPosition, newGroups, distances, penalty, ALTERNATE_HEURISTIC); | |
1044 | (*nextPositions)[encoded] = addMove(it->second, mov, main_depth); | |
1045 | queued.push((Queued) {minDist, encoded}); | |
1046 | - | int newGoal = moduloMapSuboptimalSolve(puzzle, goalMoves, 10, 10000, 5); |
1046 | + | |
1047 | // but if priority queue is too big, delete one | |
1048 | if ((int) nextPositions->size() > maxStatesPer) { | |
1049 | nextPositions->erase(queued.top().key); | |
1050 | queued.pop(); | |
1051 | } | |
1052 | } | |
1053 | - | int newGoal = moduloMapSuboptimalSolve(puzzle, goalMoves, 10, 10000, 1); |
1053 | + | |
1054 | } | |
1055 | ||
1056 | if (foundPosition == 0) { | |
1057 | std::cout << "Could not find solution.\n"; | |
1058 | foundSolution = 1; | |
1059 | return -1; | |
1060 | - | int newGoal = moduloMapSuboptimalSolve(puzzle, goalMoves, 10, 10000, 0); |
1060 | + | |
1061 | ||
1062 | main_depth++; | |
1063 | if (foundSolution == 0) { | |
1064 | std::cout << "Depth " << main_depth << ", positions " << nextPositions->size() << "\n"; | |
1065 | } | |
1066 | ||
1067 | //if ((int) nextPositions->size() > maxStatesPer) { | |
1068 | // break; | |
1069 | //} | |
1070 | } | |
1071 | return -1; | |
1072 | } | |
1073 | ||
1074 | // solve with iterative deepening | |
1075 | static void iterativeDeepeningSolve2(std::string puzzle, int goalMoves, int cacheSize) { | |
1076 | std::string walls = getWalls(puzzle); | |
1077 | ||
1078 | // start search | |
1079 | std::vector<int> distances = getManhattanDistances(walls); | |
1080 | int depth = 0; | |
1081 | int foundSolutions = 0; | |
1082 | clock_t start = clock(); | |
1083 | while (foundSolutions == 0 && depth <= goalMoves) { | |
1084 | std::vector<LruCache*> caches; | |
1085 | for (int i=0; i<=depth; i++) { | |
1086 | LruCache* l = new LruCache(cacheSize); | |
1087 | caches.push_back(l); | |
1088 | } | |
1089 | ||
1090 | int solutions = iterativeDeepeningSearch2(puzzle, walls, distances, depth, "", caches); | |
1091 | clock_t t = clock() - start; | |
1092 | if (solutions > 0) { | |
1093 | std::cout << "Solutions: " << solutions << " at depth " << depth << " (t = " << t << ")\n"; | |
1094 | foundSolutions = 1; | |
1095 | } else { | |
1096 | std::cout << "Depth " << depth << " done (t = " << t << ")\n"; | |
1097 | depth++; | |
1098 | } | |
1099 | ||
1100 | for (std::vector<LruCache*>::iterator it = caches.begin() ; it != caches.end(); ++it) | |
1101 | { | |
1102 | delete (*it); | |
1103 | } | |
1104 | caches.clear(); | |
1105 | } | |
1106 | } | |
1107 | /* | |
1108 | // single DFS run | |
1109 | static void iterativeDeepeningSolve2(std::string puzzle, int goalMoves, int cacheSize) { | |
1110 | std::string walls = getWalls(puzzle); | |
1111 | ||
1112 | // start search | |
1113 | std::vector<int> distances = getManhattanDistances(walls); | |
1114 | int depth = goalMoves - 1; | |
1115 | int foundSolutions = 0; | |
1116 | clock_t start = clock(); | |
1117 | ||
1118 | std::vector<LruCache*> caches; | |
1119 | for (int i=0; i<=depth; i++) { | |
1120 | LruCache* l = new LruCache(cacheSize); | |
1121 | caches.push_back(l); | |
1122 | } | |
1123 | ||
1124 | int solutions = iterativeDeepeningSearch2(puzzle, walls, distances, depth, "", caches); | |
1125 | clock_t t = clock() - start; | |
1126 | if (solutions > 0) { | |
1127 | std::cout << "Solutions: " << solutions << " at depth " << depth << " (t = " << t << ")\n"; | |
1128 | foundSolutions = 1; | |
1129 | } else { | |
1130 | std::cout << "No solutions (t = " << t << ")\n"; | |
1131 | } | |
1132 | ||
1133 | for (std::vector<LruCache*>::iterator it = caches.begin() ; it != caches.end(); ++it) | |
1134 | { | |
1135 | delete (*it); | |
1136 | } | |
1137 | caches.clear(); | |
1138 | }*/ | |
1139 | ||
1140 | // position = position, walls = precomputed walls of position, | |
1141 | // remaining_depth = moves left to search, moves = move string so far | |
1142 | static int iterativeDeepeningSearch2(std::string position, std::string walls, std::vector<int> distances, int remaining_depth, std::string moves, std::vector<LruCache*> caches) { | |
1143 | //std::cout << moves << "\n"; | |
1144 | int solutions = 0; | |
1145 | ||
1146 | // figure out piece groups | |
1147 | Groups groups = getGroups(position); | |
1148 | ||
1149 | if (looksSolvable(position, groups) == 0) return 0; | |
1150 | ||
1151 | // check for solved if we are out of moves | |
1152 | if (remaining_depth <= 0) { | |
1153 | int solved = isSolved(position, groups.pieceGroups); | |
1154 | if (solved==1) { | |
1155 | std::cout << "Found solution: " << moves << "\n"; | |
1156 | } | |
1157 | return solved; | |
1158 | } | |
1159 | ||
1160 | if (minDistance(position, groups, distances, 0, 0) > remaining_depth) return 0; | |
1161 | ||
1162 | // check if this position is in the cache(s) | |
1163 | bool cached = false; | |
1164 | for (int i=remaining_depth; (i<=(remaining_depth + 3) && i<(int)caches.size()); i++) { | |
1165 | if (caches[i]->contains(position)) { | |
1166 | cached = true; | |
1167 | break; | |
1168 | } | |
1169 | } | |
1170 | if (cached) { | |
1171 | return 0; | |
1172 | } else { | |
1173 | caches[remaining_depth]->insert(position); | |
1174 | } | |
1175 | ||
1176 | // loop through moves UDLR | |
1177 | std::string newPosition = applyMove(0, groups, position); | |
1178 | solutions += iterativeDeepeningSearch2(newPosition, walls, distances, remaining_depth-1, moves+"U", caches); | |
1179 | newPosition = applyMove(1, groups, position); | |
1180 | solutions += iterativeDeepeningSearch2(newPosition, walls, distances, remaining_depth-1, moves+"D", caches); | |
1181 | newPosition = applyMove(2, groups, position); | |
1182 | solutions += iterativeDeepeningSearch2(newPosition, walls, distances, remaining_depth-1, moves+"L", caches); | |
1183 | newPosition = applyMove(3, groups, position); | |
1184 | solutions += iterativeDeepeningSearch2(newPosition, walls, distances, remaining_depth-1, moves+"R", caches); | |
1185 | /*for (int mov = 0; mov < 4; mov++) { | |
1186 | std::string newPosition = applyMove(mov, groups, position); | |
1187 | ||
1188 | solutions += iterativeDeepeningSearch2(newPosition, walls, distances, remaining_depth-1, moves+moveName[mov], caches); | |
1189 | }*/ | |
1190 | ||
1191 | return solutions; | |
1192 | } | |
1193 | ||
1194 | ||
1195 | // todo: | |
1196 | // - build a shape | |
1197 | ||
1198 | ||
1199 | ||
1200 | static int solve() { | |
1201 | while (true) { | |
1202 | // get puzzle | |
1203 | int goalMoves = 100; | |
1204 | std::string puzzle; | |
1205 | std::string totalInput = ""; | |
1206 | ||
1207 | while (totalInput.length() < 256) { | |
1208 | std::string input = ""; | |
1209 | getline(std::cin, input); | |
1210 | if (input == "") { | |
1211 | return EXIT_SUCCESS; | |
1212 | } | |
1213 | totalInput += input; | |
1214 | } | |
1215 | ||
1216 | // process puzzle (and possibly a |number) | |
1217 | puzzle = totalInput.substr(0, 256); | |
1218 | if (totalInput.length() > 256) { | |
1219 | goalMoves = 0; | |
1220 | for (int i=256; i<(int)totalInput.length(); i++) { | |
1221 | if (totalInput[i] >= '0' && totalInput[i] <= '9') { | |
1222 | goalMoves = goalMoves*10 + (totalInput[i] - '0'); | |
1223 | } | |
1224 | } | |
1225 | } | |
1226 | ||
1227 | // print puzzle | |
1228 | printGrid(puzzle); | |
1229 | ||
1230 | //iterativeDeepeningSolve(puzzle); | |
1231 | //iterativeDeepeningSolve2(puzzle, goalMoves, 10000); | |
1232 | ||
1233 | //terminate called after throwing an instance of 'std::bad_alloc' | |
1234 | // what(): std::bad_alloc | |
1235 | ||
1236 | //moduloMapSolve(puzzle, goalMoves, 4, 10000000); | |
1237 | ||
1238 | std::cout << "(Penalty 5)" << std::endl; | |
1239 | while (true) { | |
1240 | int newGoal = moduloMapSuboptimalSolve(puzzle, goalMoves, 100, 10000, 5); | |
1241 | if (newGoal == -1 || newGoal >= goalMoves) break; | |
1242 | goalMoves = newGoal; | |
1243 | std::cout << "Goal changed to " << goalMoves << " moves" << std::endl; | |
1244 | } | |
1245 | std::cout << "(Penalty 1)" << std::endl; | |
1246 | while (true) { | |
1247 | int newGoal = moduloMapSuboptimalSolve(puzzle, goalMoves, 100, 10000, 1); | |
1248 | if (newGoal == -1 || newGoal >= goalMoves) break; | |
1249 | goalMoves = newGoal; | |
1250 | std::cout << "Goal changed to " << goalMoves << " moves" << std::endl; | |
1251 | } | |
1252 | std::cout << "(Penalty 0)" << std::endl; | |
1253 | while (true) { | |
1254 | int newGoal = moduloMapSuboptimalSolve(puzzle, goalMoves, 100, 10000, 0); | |
1255 | if (newGoal == -1 || newGoal >= goalMoves) break; | |
1256 | goalMoves = newGoal; | |
1257 | std::cout << "Goal changed to " << goalMoves << " moves" << std::endl; | |
1258 | } | |
1259 | } | |
1260 | ||
1261 | return EXIT_SUCCESS; | |
1262 | } | |
1263 | }; | |
1264 | ||
1265 | int main(int argc, char *argv[]) { | |
1266 | DenkiSolver::solve(); | |
1267 | } |