// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Compute the inverse CubieCube void invCubieCube(CubieCube c) { foreach (Edge edge in (Edge[])Enum.GetValues(typeof(Edge))) { c.ep[(int)ep[(int)edge]] = edge; } foreach (Edge edge in (Edge[])Enum.GetValues(typeof(Edge))) { c.eo[(int)edge] = eo[(int)c.ep[(int)edge]]; } foreach (Corner corn in (Corner[])Enum.GetValues(typeof(Corner))) { c.cp[(int)cp[(int)corn]] = corn; } foreach (Corner corn in (Corner[])Enum.GetValues(typeof(Corner))) { byte ori = co[(int)c.cp[(int)corn]]; if (ori >= 3) // Just for completeness. We do not invert mirrored // cubes in the program. { c.co[(int)corn] = ori; } else {// the standard case c.co[(int)corn] = (byte)-ori; if (c.co[(int)corn] < 0) { c.co[(int)corn] += 3; } } } }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Check if the cube string s represents a solvable cube. // 0: Cube is solvable // -1: There is not exactly one facelet of each colour // -2: Not all 12 edges exist exactly once // -3: Flip error: One edge has to be flipped // -4: Not all corners exist exactly once // -5: Twist error: One corner has to be twisted // -6: Parity error: Two corners or two edges have to be exchanged // /// <summary> /// Check if the cube definition string s represents a solvable cube. /// </summary> /// <param name="s"> is the cube definition string , see <seealso cref="Facelet"/> </param> /// <returns> 0: Cube is solvable<br> /// -1: There is not exactly one facelet of each colour<br> /// -2: Not all 12 edges exist exactly once<br> /// -3: Flip error: One edge has to be flipped<br> /// -4: Not all 8 corners exist exactly once<br> /// -5: Twist error: One corner has to be twisted<br> /// -6: Parity error: Two corners or two edges have to be exchanged </returns> public static int verify(string s) { int[] count = new int[6]; try { for (int i = 0; i < 54; i++) { count[(int)CubeColor.Parse(typeof(CubeColor), i.ToString())]++; } } catch (Exception) { return(-1); } for (int i = 0; i < 6; i++) { if (count[i] != 9) { return(-1); } } FaceCube fc = new FaceCube(s); CubieCube cc = fc.toCubieCube(); return(cc.verify()); }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Multiply this CubieCube with another cubiecube b, restricted to the corners.<br> // Because we also describe reflections of the whole cube by permutations, we get a complication with the corners. The // orientations of mirrored corners are described by the numbers 3, 4 and 5. The composition of the orientations // cannot // be computed by addition modulo three in the cyclic group C3 any more. Instead the rules below give an addition in // the dihedral group D3 with 6 elements.<br> // // NOTE: Because we do not use symmetry reductions and hence no mirrored cubes in this simple implementation of the // Two-Phase-Algorithm, some code is not necessary here. // public void cornerMultiply(CubieCube b) { Corner[] cPerm = new Corner[8]; byte[] cOri = new byte[8]; foreach (Corner corn in (Corner[])Enum.GetValues(typeof(Corner))) { cPerm[(int)corn] = cp[(int)b.cp[(int)corn]]; byte oriA = co[(int)b.cp[(int)corn]]; byte oriB = b.co[(int)corn]; byte ori = 0; ; if (oriA < 3 && oriB < 3) // if both cubes are regular cubes... { ori = (byte)(oriA + oriB); // just do an addition modulo 3 here if (ori >= 3) { ori -= 3; // the composition is a regular cube } // +++++++++++++++++++++not used in this implementation +++++++++++++++++++++++++++++++++++ } else if (oriA < 3 && oriB >= 3) // if cube b is in a mirrored // state... { ori = (byte)(oriA + oriB); if (ori >= 6) { ori -= 3; // the composition is a mirrored cube } } else if (oriA >= 3 && oriB < 3) // if cube a is an a mirrored // state... { ori = (byte)(oriA - oriB); if (ori < 3) { ori += 3; // the composition is a mirrored cube } } else if (oriA >= 3 && oriB >= 3) // if both cubes are in mirrored // states... { ori = (byte)(oriA - oriB); if (ori < 0) { ori += 3; // the composition is a regular cube } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ } cOri[(int)corn] = ori; } foreach (Corner c in (Corner[])Enum.GetValues(typeof(Corner))) { cp[(int)c] = cPerm[(int)c]; co[(int)c] = cOri[(int)c]; } }
static CubieCube SetMoveU() { CubieCube move = new CubieCube(); move.cp = cpU; move.co = coU; move.ep = epU; move.eo = eoU; return(move); }
static CubieCube SetMoveD() { CubieCube move = new CubieCube(); move.cp = cpD; move.co = coD; move.ep = epD; move.eo = eoD; return(move); }
static CubieCube SetMoveL() { CubieCube move = new CubieCube(); move.cp = cpL; move.co = coL; move.ep = epL; move.eo = eoL; return(move); }
static CubieCube SetMoveB() { CubieCube move = new CubieCube(); move.cp = cpB; move.co = coB; move.ep = epB; move.eo = eoB; return(move); }
static CubieCube SetMoveF() { CubieCube move = new CubieCube(); move.cp = cpF; move.co = coF; move.ep = epF; move.eo = eoF; return(move); }
static CubieCube SetMoveR() { CubieCube move = new CubieCube(); move.cp = cpR; move.co = coR; move.ep = epR; move.eo = eoR; return(move); }
public static string fromScramble(int[] scramble) { CubieCube c1 = new CubieCube(); CubieCube c2 = new CubieCube(); CubieCube tmp; for (int i = 0; i < scramble.Length; i++) { c1.cornerMultiply(CubieCube.moveCube[scramble[i]]); c2.cornerMultiply(CubieCube.moveCube[scramble[i]]); tmp = c1; c1 = c2; c2 = tmp; } return(c1.toFaceCube().to_fc_String()); }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Generate a CoordCube from a CubieCube internal CoordCube(CubieCube c, DateTime startTime, string currentTime, out string info) { info = currentTime; twist = c.getTwist(); flip = c.getFlip(); parity = c.cornerParity(); FRtoBR = c.getFRtoBR(); URFtoDLF = c.getURFtoDLF(); URtoUL = c.getURtoUL(); UBtoDF = c.getUBtoDF(); URtoDF = c.getURtoDF();// only needed in phase2 info += "[ Finished Initialiation: " + String.Format(@"{0:mm\:ss\.ffff}", (DateTime.Now - startTime)) + " ] "; }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Multiply this CubieCube with another cubiecube b, restricted to the edges. public void edgeMultiply(CubieCube b) { Edge[] ePerm = new Edge[12]; byte[] eOri = new byte[12]; foreach (Edge edge in (Edge[])Enum.GetValues(typeof(Edge))) { ePerm[(int)edge] = ep[(int)b.ep[(int)edge]]; eOri[(int)edge] = (byte)((b.eo[(int)edge] + eo[(int)b.ep[(int)edge]]) % 2); } foreach (Edge e in (Edge[])Enum.GetValues(typeof(Edge))) { ep[(int)e] = ePerm[(int)e]; eo[(int)e] = eOri[(int)e]; } }
/// <summary> /// Generates a random cube. </summary> /// <returns> A random cube in the string representation. Each cube of the cube space has the same probability. </returns> public static string randomCube() { CubieCube cc = new CubieCube(); System.Random gen = new System.Random(); cc.setFlip((short)gen.Next(CoordCube.N_FLIP)); cc.setTwist((short)gen.Next(CoordCube.N_TWIST)); do { cc.setURFtoDLB(gen.Next(CoordCube.N_URFtoDLB)); cc.setURtoBR(gen.Next(CoordCube.N_URtoBR)); } while ((cc.edgeParity() ^ cc.cornerParity()) != 0); FaceCube fc = cc.toFaceCube(); return(fc.to_fc_String()); }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Permutation of the six edges UR,UF,UL,UB,DR,DF public static int getURtoDF(short idx1, short idx2) { CubieCube a = new CubieCube(); CubieCube b = new CubieCube(); a.setURtoUL(idx1); b.setUBtoDF(idx2); for (int i = 0; i < 8; i++) { if (a.ep[i] != Edge.BR) { if (b.ep[i] != Edge.BR)// collision { return(-1); } else { b.ep[i] = a.ep[i]; } } } return(b.getURtoDF()); }
/** * 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); }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Gives CubieCube representation of a faceletcube public CubieCube toCubieCube() { byte ori; CubieCube ccRet = new CubieCube(); for (int i = 0; i < 8; i++) { ccRet.cp[i] = Corner.URF;// invalidate corners } for (int i = 0; i < 12; i++) { ccRet.ep[i] = Edge.UR;// and edges } CubeColor col1, col2; foreach (Corner i in (Corner[])Enum.GetValues(typeof(Corner))) { // get the CubeColors of the cubie at corner i, starting with U/D for (ori = 0; ori < 3; ori++) { if (f[(int)cornerFacelet[(int)i][ori]] == CubeColor.U || f[(int)cornerFacelet[(int)i][ori]] == CubeColor.D) { break; } } col1 = f[(int)cornerFacelet[(int)i][(ori + 1) % 3]]; col2 = f[(int)cornerFacelet[(int)i][(ori + 2) % 3]]; foreach (Corner j in (Corner[])Enum.GetValues(typeof(Corner))) { if (col1 == cornerColor[(int)j][1] && col2 == cornerColor[(int)j][2]) { // in cornerposition i we have cornercubie j ccRet.cp[(int)i] = j; ccRet.co[(int)i] = (byte)(ori % 3); break; } } } foreach (Edge i in (Edge[])Enum.GetValues(typeof(Edge))) { foreach (Edge j in (Edge[])Enum.GetValues(typeof(Edge))) { if (f[(int)edgeFacelet[(int)i][0]] == edgeColor[(int)j][0] && f[(int)edgeFacelet[(int)i][1]] == edgeColor[(int)j][1]) { ccRet.ep[(int)i] = j; ccRet.eo[(int)i] = 0; break; } if (f[(int)edgeFacelet[(int)i][0]] == edgeColor[(int)j][1] && f[(int)edgeFacelet[(int)i][1]] == edgeColor[(int)j][0]) { ccRet.ep[(int)i] = j; ccRet.eo[(int)i] = 1; break; } } } return(ccRet); }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Multiply this CubieCube with another CubieCube b. void multiply(CubieCube b) { cornerMultiply(b); // edgeMultiply(b); }
/** * 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 = 22, long timeOut = 6000, bool useSeparator = false) { if (facelets == "UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB") { info = "Already Solved"; return(""); } DateTime startTime = DateTime.Now; info = ""; 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 +++++++++++++++++++++++++++++++++ string currentTime = "[ a: " + String.Format(@"{0:mm\:ss\.ffff}", (DateTime.Now - startTime)) + " ] "; CoordCube c = new CoordCube(cc, startTime, currentTime, out info); //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] = 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] = Math.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); }