string Search::solution(string facelets, int maxDepth, long timeOut, bool useSeparator) { int s = 0; // +++++++++++++++++++++check for wrong input +++++++++++++++++++++++++++++ int count[6] = {0}; try { for (int i = 0; i < 54; i++) { switch (facelets[i]) { case 'U': count[U]++; break; case 'R': count[R]++; break; case 'F': count[F]++; break; case 'D': count[D]++; break; case 'L': count[L]++; break; case 'B': count[B]++; break; default: break; } } } catch (exception e) { return "Error 1"; } for (int i = 0; i < 6; i++) if (count[i] != 9) return "Error 1"; FaceCube fc = FaceCube(facelets); auto_ptr<CubieCube> cc = fc.toCubieCube(); if ((s = cc->verify()) != 0){ string result = "Error "; result += (char)(abs(s)+'0'); return result; } // +++++++++++++++++++++++ initialization +++++++++++++++++++++++++++++++++ //--------------------------- CoordCube c = CoordCube(*cc); po[0] = 0; ax[0] = 0; flip[0] = c.flip; twist[0] = c.twist; parity[0] = c.parity; slice[0] = c.FRtoBR / 24; URFtoDLF[0] = c.URFtoDLF; FRtoBR[0] = c.FRtoBR; URtoUL[0] = c.URtoUL; UBtoDF[0] = c.UBtoDF; minDistPhase1[1] = 1;// else failure for depth=1, n=0 int mv = 0, n = 0; bool busy = false; int depthPhase1 = 1; struct timeval tv; gettimeofday(&tv,NULL); long tStart = tv.tv_sec * 1000 + tv.tv_usec / 1000; // +++++++++++++++++++ Main loop ++++++++++++++++++++++++++++++++++++++++++ do { do { if ((depthPhase1 - n > minDistPhase1[n + 1]) && !busy) { if (ax[n] == 0 || ax[n] == 3)// Initialize next move ax[++n] = 1; else ax[++n] = 0; po[n] = 1; } else if (++po[n] > 3) { do {// increment axis if (++ax[n] > 5) { gettimeofday(&tv,NULL); if ((tv.tv_sec * 1000 + tv.tv_usec / 1000) - tStart > timeOut << 10) return "Error 8"; if (n == 0) { if (depthPhase1 >= maxDepth) return "Error 7"; else { depthPhase1++; ax[n] = 0; po[n] = 1; busy = false; break; } } else { n--; busy = true; break; } } else { po[n] = 1; busy = false; } } while (n != 0 && (ax[n - 1] == ax[n] || ax[n - 1] - 3 == ax[n])); } else busy = false; } while (busy); // +++++++++++++ compute new coordinates and new minDistPhase1 ++++++++++ // if minDistPhase1 =0, the H subgroup is reached mv = 3 * ax[n] + po[n] - 1; flip[n + 1] = CoordCube::flipMove[flip[n]][mv]; twist[n + 1] = CoordCube::twistMove[twist[n]][mv]; slice[n + 1] = CoordCube::FRtoBR_Move[slice[n] * 24][mv] / 24; minDistPhase1[n + 1] = max(CoordCube::getPruning(CoordCube::Slice_Flip_Prun, CoordCube::N_SLICE1 * flip[n + 1] + slice[n + 1]), CoordCube::getPruning(CoordCube::Slice_Twist_Prun, CoordCube::N_SLICE1 * twist[n + 1] + slice[n + 1])); // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ if (minDistPhase1[n + 1] == 0 && n >= depthPhase1 - 5) { minDistPhase1[n + 1] = 10;// instead of 10 any value >5 is possible if (n == depthPhase1 - 1 && (s = totalDepth(depthPhase1, maxDepth)) >= 0) { if (s == depthPhase1 || (ax[depthPhase1 - 1] != ax[depthPhase1] && ax[depthPhase1 - 1] != ax[depthPhase1] + 3)) return useSeparator ? solutionToString(s, depthPhase1) : solutionToString(s); } } } while (true); }
/** * Computes the solver string for a given cube. * * @param facelets * is the cube definition string, see {@link Facelet} for the format. * * @param maxDepth * defines the maximal allowed maneuver length. For random cubes, a maxDepth of 21 usually will return a * solution in less than 0.5 seconds. With a maxDepth of 20 it takes a few seconds on average to find a * solution, but it may take much longer for specific cubes. * *@param timeOut * defines the maximum computing time of the method in seconds. If it does not return with a solution, it returns with * an error code. * * @param useSeparator * determines if a " . " separates the phase1 and phase2 parts of the solver string like in F' R B R L2 F . * U2 U D for example.<br> * @return The solution string or an error code:<br> * Error 1: There is not exactly one facelet of each colour<br> * Error 2: Not all 12 edges exist exactly once<br> * Error 3: Flip error: One edge has to be flipped<br> * Error 4: Not all corners exist exactly once<br> * Error 5: Twist error: One corner has to be twisted<br> * Error 6: Parity error: Two corners or two edges have to be exchanged<br> * Error 7: No solution exists for the given maxDepth<br> * Error 8: Timeout, no solution within given time */ std::string Search::solution(std::string facelets, int maxDepth, long timeOut, bool useSeparator) { int s; // +++++++++++++++++++++check for wrong input +++++++++++++++++++++++++++++ int count[6]; for (int i = 0; i < 6; ++i) count[i] = 0; try { for (int i = 0; i < 54; i++) ++count[FaceCube::charToColor(facelets[i])]; } catch (...) { return "Error 1"; } for (int i = 0; i < 6; i++) if (count[i] != 9) return "Error 1"; FaceCube fc(facelets); CubieCube cc(fc.toCubieCube()); if ((s = cc.verify()) != 0) { char e[40]; sprintf(e, "Error %d", abs(s)); return e; } // +++++++++++++++++++++++ initialization +++++++++++++++++++++++++++++++++ CoordCube c(cc); po[0] = 0; ax[0] = 0; flip[0] = c.flip; twist[0] = c.twist; parity[0] = c.parity; slice[0] = c.FRtoBR / 24; URFtoDLF[0] = c.URFtoDLF; FRtoBR[0] = c.FRtoBR; URtoUL[0] = c.URtoUL; UBtoDF[0] = c.UBtoDF; minDistPhase1[1] = 1;// else failure for depth=1, n=0 int mv = 1, n = 0; bool busy = false; // gets set when a move is popped from the stack int depthPhase1 = 1; clock_t tStart = clock(); timeOut *= CLOCKS_PER_SEC; // +++++++++++++++++++ Main loop ++++++++++++++++++++++++++++++++++++++++++ do { /* Find the next node to test * This is an Iterative Deepening A* search (IDA*) * which means we are doing a depth first search over and over again * with increasing depth limit while pruning branches which would * give too many total moves. * * n is current depth * depthPhase1 is current max depth * minDistPhase1[n + 1] is the pruning value which gives a lower bound * on the moves necessary after the current move * * the sequence of moves are stored as ax[] and po[] which together represents each move. * ax[n] is the axis, 0 to 2 * po[n] is the power, 1 to 15 (which combination of moves on the axis) */ do { // loop for choice of node while busy if ((n + minDistPhase1[n + 1] < depthPhase1) && !busy) { // Dig deeper // Initialize next move, avoid previous axis if (ax[n] != 0) ax[++n] = 0; else ax[++n] = 1; po[n] = 1; } else if (++po[n] > 15) { // increment power do {// increment axis if (++ax[n] > 2) { if (clock() - tStart > timeOut) return "Error 8"; if (n == 0) { if (depthPhase1 >= maxDepth) return "Error 7"; else { // Start over with greater depth depthPhase1++; ax[n] = 0; po[n] = 1; busy = false; break; } } else { // Decrease current depth n--; busy = true; break; } } else { po[n] = 1; busy = false; } } while (n != 0 && (ax[n - 1] == ax[n])); } else busy = false; } while (busy); // +++++++++++++ compute new coordinates and new minDistPhase1 ++++++++++ // if minDistPhase1 =0, the H subgroup is reached mv = 16 * ax[n] + po[n]; if (mv > CoordCube::N_MOVE) printf("n: %d ax[n]: %d po[n]: %d mv: %d ", n, ax[n], po[n], mv); flip[n + 1] = CoordCube::flipMove[flip[n]][mv]; twist[n + 1] = CoordCube::twistMove[twist[n]][mv]; slice[n + 1] = CoordCube::FRtoBR_Move[slice[n] * 24][mv] / 24; minDistPhase1[n + 1] = std::max(CoordCube::getPruning(CoordCube::Slice_Flip_Prun, CoordCube::N_SLICE1 * flip[n + 1] + slice[n + 1]), CoordCube::getPruning(CoordCube::Slice_Twist_Prun, CoordCube::N_SLICE1 * twist[n + 1] + slice[n + 1])); // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ if (minDistPhase1[n + 1] == 0 && n >= depthPhase1 - 5) { minDistPhase1[n + 1] = 10;// instead of 10 any value >5 is possible if (n == depthPhase1 - 1 && (s = totalDepth(depthPhase1, maxDepth)) >= 0) { if (s == depthPhase1 || (ax[depthPhase1 - 1] != ax[depthPhase1] && ax[depthPhase1 - 1] != ax[depthPhase1] + 3)) return useSeparator ? solutionToString(s, depthPhase1) : solutionToString(s); } } } while (true); }