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 | } |