// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Apply phase2 of algorithm and return the combined phase1 and phase2 depth. In phase2, only the moves // U,D,R2,F2,L2 and B2 are allowed. internal static int totalDepth(int depthPhase1, int maxDepth) { int mv = 0, d1 = 0, d2 = 0; int maxDepthPhase2 = Math.Min(10, maxDepth - depthPhase1); // Allow only max 10 moves in phase2 for (int i = 0; i < depthPhase1; i++) { mv = 3 * ax[i] + po[i] - 1; URFtoDLF[i + 1] = CoordCubeBuildTables.URFtoDLF_Move[URFtoDLF[i], mv]; FRtoBR[i + 1] = CoordCubeBuildTables.FRtoBR_Move[FRtoBR[i], mv]; parity[i + 1] = CoordCubeBuildTables.parityMove[parity[i]][mv]; } if ((d1 = CoordCubeBuildTables.getPruning(CoordCubeBuildTables.Slice_URFtoDLF_Parity_Prun, (CoordCubeBuildTables.N_SLICE2 * URFtoDLF[depthPhase1] + FRtoBR[depthPhase1]) * 2 + parity[depthPhase1])) > maxDepthPhase2) { return(-1); } for (int i = 0; i < depthPhase1; i++) { mv = 3 * ax[i] + po[i] - 1; URtoUL[i + 1] = CoordCubeBuildTables.URtoUL_Move[URtoUL[i], mv]; UBtoDF[i + 1] = CoordCubeBuildTables.UBtoDF_Move[UBtoDF[i], mv]; } URtoDF[depthPhase1] = CoordCubeBuildTables.MergeURtoULandUBtoDF[URtoUL[depthPhase1], UBtoDF[depthPhase1]]; if ((d2 = CoordCubeBuildTables.getPruning(CoordCubeBuildTables.Slice_URtoDF_Parity_Prun, (CoordCubeBuildTables.N_SLICE2 * URtoDF[depthPhase1] + FRtoBR[depthPhase1]) * 2 + parity[depthPhase1])) > maxDepthPhase2) { return(-1); } if ((minDistPhase2[depthPhase1] = Math.Max(d1, d2)) == 0) // already solved { return(depthPhase1); } // now set up search int depthPhase2 = 1; int n = depthPhase1; bool busy = false; po[depthPhase1] = 0; ax[depthPhase1] = 0; minDistPhase2[n + 1] = 1; // else failure for depthPhase2=1, n=0 // +++++++++++++++++++ end initialization +++++++++++++++++++++++++++++++++ do { do { if ((depthPhase1 + depthPhase2 - n > minDistPhase2[n + 1]) && !busy) { if (ax[n] == 0 || ax[n] == 3) // Initialize next move { ax[++n] = 1; po[n] = 2; } else { ax[++n] = 0; po[n] = 1; } } else if ((ax[n] == 0 || ax[n] == 3) ? (++po[n] > 3) : ((po[n] = po[n] + 2) > 3)) { do { // increment axis if (++ax[n] > 5) { if (n == depthPhase1) { if (depthPhase2 >= maxDepthPhase2) { return(-1); } else { depthPhase2++; ax[n] = 0; po[n] = 1; busy = false; break; } } else { n--; busy = true; break; } } else { if (ax[n] == 0 || ax[n] == 3) { po[n] = 1; } else { po[n] = 2; } busy = false; } } while (n != depthPhase1 && (ax[n - 1] == ax[n] || ax[n - 1] - 3 == ax[n])); } else { busy = false; } } while (busy); // +++++++++++++ compute new coordinates and new minDist ++++++++++ mv = 3 * ax[n] + po[n] - 1; URFtoDLF[n + 1] = CoordCubeBuildTables.URFtoDLF_Move[URFtoDLF[n], mv]; FRtoBR[n + 1] = CoordCubeBuildTables.FRtoBR_Move[FRtoBR[n], mv]; parity[n + 1] = CoordCubeBuildTables.parityMove[parity[n]][mv]; URtoDF[n + 1] = CoordCubeBuildTables.URtoDF_Move[URtoDF[n], mv]; minDistPhase2[n + 1] = Math.Max(CoordCubeBuildTables.getPruning(CoordCubeBuildTables.Slice_URtoDF_Parity_Prun, (CoordCubeBuildTables.N_SLICE2 * URtoDF[n + 1] + FRtoBR[n + 1]) * 2 + parity[n + 1]), CoordCubeBuildTables.getPruning(CoordCubeBuildTables.Slice_URFtoDLF_Parity_Prun, (CoordCubeBuildTables.N_SLICE2 * URFtoDLF[n + 1] + FRtoBR[n + 1]) * 2 + parity[n + 1])); // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ } while (minDistPhase2[n + 1] != 0); return(depthPhase1 + depthPhase2); }
/** * 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 */ public static string solution(string facelets, out string info, int maxDepth = 50, long timeOut = 6000, bool useSeparator = false, bool buildTables = false) { info = "Warning, this solution builds tables at run time which is very slow. This will find a solution, however it is reccomended to use the K_SearchRunTime class only to create a local copy of the tables, then use the K_Search class to search for solutions instead."; if (facelets == "UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB") { return(""); } int s; // +++++++++++++++++++++check for wrong input +++++++++++++++++++++++++++++ int[] count = new int[6]; try { for (int i = 0; i < 54; i++) { count[(int)CubeColor.Parse(typeof(CubeColor), facelets.Substring(i, 1))]++; } } catch (Exception) { return("Error 1"); } for (int i = 0; i < 6; i++) { if (count[i] != 9) { return("Error 1"); } } FaceCube fc = new FaceCube(facelets); CubieCube cc = fc.toCubieCube(); if ((s = cc.verify()) != 0) { return("Error " + Math.Abs(s)); } // +++++++++++++++++++++++ initialization +++++++++++++++++++++++++++++++++ CoordCubeBuildTables c = new CoordCubeBuildTables(cc, buildTables); //return "lol"; 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; long tStart = DateTimeHelper.CurrentUnixTimeMillis(); // +++++++++++++++++++ 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) { if (DateTimeHelper.CurrentUnixTimeMillis() - 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] = CoordCubeBuildTables.flipMove[flip[n], mv]; twist[n + 1] = CoordCubeBuildTables.twistMove[twist[n], mv]; slice[n + 1] = CoordCubeBuildTables.FRtoBR_Move[slice[n] * 24, mv] / 24; minDistPhase1[n + 1] = Math.Max(CoordCubeBuildTables.getPruning(CoordCubeBuildTables.Slice_Flip_Prun, CoordCubeBuildTables.N_SLICE1 * flip[n + 1] + slice[n + 1]), CoordCubeBuildTables.getPruning(CoordCubeBuildTables.Slice_Twist_Prun, CoordCubeBuildTables.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); }