// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // 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 // /** * Check if the cube definition string s represents a solvable cube. * * @param s is the cube definition string , see {@link Facelet} * @return 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 */ public static int verify(String s) { int[] count = new int[6]; try { for (int i = 0; i < 54; i++) { Color col; if (!Enum.TryParse(s.Substring(i, 1), out col)) { throw new Exception("Invalid color"); } count[(int)col]++; } } catch (Exception e) { 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()); }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Generate a CoordCube from a CubieCube public CoordCube(CubieCube c) { 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 }
/** * Generates a random cube. * @return A random cube in the string representation. Each cube of the cube space has the same probability. */ public static String randomCube() { CubieCube cc = new CubieCube(); Random gen = new 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_String()); }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Gives CubieCube representation of a faceletcube public CubieCube toCubieCube() { sbyte 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 } Color col1, col2; foreach (Corner i in Enum.GetValues(typeof(Corner))) { // get the colors of the cubie at corner i, starting with U/D for (ori = 0; ori < 3; ori++) { if (f[(int)cornerFacelet[(int)i][ori]] == Color.U || f[(int)cornerFacelet[(int)i][ori]] == Color.D) { break; } } col1 = f[(int)cornerFacelet[(int)i][(ori + 1) % 3]]; col2 = f[(int)cornerFacelet[(int)i][(ori + 2) % 3]]; foreach (Corner j in 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] = (sbyte)(ori % 3); break; } } } foreach (Edge i in Enum.GetValues(typeof(Edge))) { foreach (Edge j in 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); }
static CoordCube() { if (LoadPrunData()) { return; } // ******************************************Phase 1 move tables***************************************************** // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Move table for the twists of the corners // twist < 2187 in phase 2. // twist = 0 in phase 2. CubieCube a = new CubieCube(); for (short i = 0; i < N_TWIST; i++) { a.setTwist(i); for (int j = 0; j < 6; j++) { for (int k = 0; k < 3; k++) { a.cornerMultiply(CubieCube.moveCube[j]); twistMove[i, 3 * j + k] = a.getTwist(); } a.cornerMultiply(CubieCube.moveCube[j]); // 4. faceturn restores // a } } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Move table for the flips of the edges // flip < 2048 in phase 1 // flip = 0 in phase 2. a = new CubieCube(); for (short i = 0; i < N_FLIP; i++) { a.setFlip(i); for (int j = 0; j < 6; j++) { for (int k = 0; k < 3; k++) { a.edgeMultiply(CubieCube.moveCube[j]); flipMove[i, 3 * j + k] = a.getFlip(); } a.edgeMultiply(CubieCube.moveCube[j]); // a } } // ***********************************Phase 1 and 2 movetable******************************************************** // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Move table for the four UD-slice edges FR, FL, Bl and BR // FRtoBRMove < 11880 in phase 1 // FRtoBRMove < 24 in phase 2 // FRtoBRMove = 0 for solved cube a = new CubieCube(); for (short i = 0; i < N_FRtoBR; i++) { a.setFRtoBR(i); for (int j = 0; j < 6; j++) { for (int k = 0; k < 3; k++) { a.edgeMultiply(CubieCube.moveCube[j]); FRtoBR_Move[i, 3 * j + k] = a.getFRtoBR(); } a.edgeMultiply(CubieCube.moveCube[j]); } } // *******************************************Phase 1 and 2 movetable************************************************ // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Move table for permutation of six corners. The positions of the DBL and DRB corners are determined by the parity. // URFtoDLF < 20160 in phase 1 // URFtoDLF < 20160 in phase 2 // URFtoDLF = 0 for solved cube. a = new CubieCube(); for (short i = 0; i < N_URFtoDLF; i++) { a.setURFtoDLF(i); for (int j = 0; j < 6; j++) { for (int k = 0; k < 3; k++) { a.cornerMultiply(CubieCube.moveCube[j]); URFtoDLF_Move[i, 3 * j + k] = a.getURFtoDLF(); } a.cornerMultiply(CubieCube.moveCube[j]); } } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Move table for the permutation of six U-face and D-face edges in phase2. The positions of the DL and DB edges are // determined by the parity. // URtoDF < 665280 in phase 1 // URtoDF < 20160 in phase 2 // URtoDF = 0 for solved cube. a = new CubieCube(); for (short i = 0; i < N_URtoDF; i++) { a.setURtoDF(i); for (int j = 0; j < 6; j++) { for (int k = 0; k < 3; k++) { a.edgeMultiply(CubieCube.moveCube[j]); URtoDF_Move[i, 3 * j + k] = (short)a.getURtoDF(); // Table values are only valid for phase 2 moves! // For phase 1 moves, casting to short is not possible. } a.edgeMultiply(CubieCube.moveCube[j]); } } // **************************helper move tables to compute URtoDF for the beginning of phase2************************ // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Move table for the three edges UR,UF and UL in phase1. a = new CubieCube(); for (short i = 0; i < N_URtoUL; i++) { a.setURtoUL(i); for (int j = 0; j < 6; j++) { for (int k = 0; k < 3; k++) { a.edgeMultiply(CubieCube.moveCube[j]); URtoUL_Move[i, 3 * j + k] = a.getURtoUL(); } a.edgeMultiply(CubieCube.moveCube[j]); } } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Move table for the three edges UB,DR and DF in phase1. a = new CubieCube(); for (short i = 0; i < N_UBtoDF; i++) { a.setUBtoDF(i); for (int j = 0; j < 6; j++) { for (int k = 0; k < 3; k++) { a.edgeMultiply(CubieCube.moveCube[j]); UBtoDF_Move[i, 3 * j + k] = a.getUBtoDF(); } a.edgeMultiply(CubieCube.moveCube[j]); } } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Table to merge the coordinates of the UR,UF,UL and UB,DR,DF edges at the beginning of phase2 // for i, j <336 the six edges UR,UF,UL,UB,DR,DF are not in the // UD-slice and the index is <20160 for (short uRtoUL = 0; uRtoUL < 336; uRtoUL++) { for (short uBtoDF = 0; uBtoDF < 336; uBtoDF++) { MergeURtoULandUBtoDF[uRtoUL, uBtoDF] = (short)CubieCube.getURtoDF(uRtoUL, uBtoDF); } } // ****************************************Pruning tables for the search********************************************* // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Pruning table for the permutation of the corners and the UD-slice edges in phase2. // The pruning table entries give a lower estimation for the number of moves to reach the solved cube. for (int i = 0; i < N_SLICE2 * N_URFtoDLF * N_PARITY / 2; i++) { Slice_URFtoDLF_Parity_Prun[i] = -1; } int depth = 0; setPruning(Slice_URFtoDLF_Parity_Prun, 0, 0); int done = 1; while (done != N_SLICE2 * N_URFtoDLF * N_PARITY) { for (int i = 0; i < N_SLICE2 * N_URFtoDLF * N_PARITY; i++) { int parity = i % 2; int URFtoDLF = (i / 2) / N_SLICE2; int slice = (i / 2) % N_SLICE2; if (((i % 2 == 0) ? (Slice_URFtoDLF_Parity_Prun[i >> 1] & 0x0f) : ((Slice_URFtoDLF_Parity_Prun[i >> 1] & 0xf0) >> 4)) == depth) { for (int j = 0; j < 18; j++) { switch (j) { case 3: case 5: case 6: case 8: case 12: case 14: case 15: case 17: continue; default: int newSlice = FRtoBR_Move[slice, j]; int newURFtoDLF = URFtoDLF_Move[URFtoDLF, j]; int newParity = parityMove[parity][j]; int index = (N_SLICE2 * newURFtoDLF + newSlice) * 2 + newParity; if (((index % 2 == 0) ? (Slice_URFtoDLF_Parity_Prun[index >> 1] & 0x0f) : ((Slice_URFtoDLF_Parity_Prun[index >> 1] & 0xf0) >> 4)) == 0x0f) { setPruning(Slice_URFtoDLF_Parity_Prun, (N_SLICE2 * newURFtoDLF + newSlice) * 2 + newParity, (sbyte)(depth + 1)); done++; } break; } } } } depth++; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Pruning table for the permutation of the edges in phase2. // The pruning table entries give a lower estimation for the number of moves to reach the solved cube. for (int i = 0; i < N_SLICE2 * N_URtoDF * N_PARITY / 2; i++) { Slice_URtoDF_Parity_Prun[i] = -1; } depth = 0; setPruning(Slice_URtoDF_Parity_Prun, 0, 0); done = 1; while (done != N_SLICE2 * N_URtoDF * N_PARITY) { for (int i = 0; i < N_SLICE2 * N_URtoDF * N_PARITY; i++) { int parity = i % 2; int URtoDF = (i / 2) / N_SLICE2; int slice = (i / 2) % N_SLICE2; if (((i % 2 == 0) ? (Slice_URtoDF_Parity_Prun[i >> 1] & 0x0f) : ((Slice_URtoDF_Parity_Prun[i >> 1] & 0xf0) >> 4)) == depth) { for (int j = 0; j < 18; j++) { switch (j) { case 3: case 5: case 6: case 8: case 12: case 14: case 15: case 17: continue; default: int newSlice = FRtoBR_Move[slice, j]; int newURtoDF = URtoDF_Move[URtoDF, j]; int newParity = parityMove[parity][j]; int index = (N_SLICE2 * newURtoDF + newSlice) * 2 + newParity; if (((index % 2 == 0) ? (Slice_URtoDF_Parity_Prun[index >> 1] & 0x0f) : ((Slice_URtoDF_Parity_Prun[index >> 1] & 0xf0) >> 4)) == 0x0f) { setPruning(Slice_URtoDF_Parity_Prun, (N_SLICE2 * newURtoDF + newSlice) * 2 + newParity, (sbyte)(depth + 1)); done++; } break; } } } } depth++; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Pruning table for the twist of the corners and the position (not permutation) of the UD-slice edges in phase1 // The pruning table entries give a lower estimation for the number of moves to reach the H-subgroup. for (int i = 0; i < N_SLICE1 * N_TWIST / 2 + 1; i++) { Slice_Twist_Prun[i] = -1; } depth = 0; setPruning(Slice_Twist_Prun, 0, (sbyte)0); done = 1; while (done != N_SLICE1 * N_TWIST) { for (int i = 0; i < N_SLICE1 * N_TWIST; i++) { int twist = i / N_SLICE1, slice = i % N_SLICE1; if (((i % 2 == 0) ? (Slice_Twist_Prun[i >> 1] & 0x0f) : ((Slice_Twist_Prun[i >> 1] & 0xf0) >> 4)) == depth) { for (int j = 0; j < 18; j++) { int newSlice = FRtoBR_Move[slice * 24, j] / 24; int newTwist = twistMove[twist, j]; int index = N_SLICE1 * newTwist + newSlice; if (((index % 2 == 0) ? (Slice_Twist_Prun[index >> 1] & 0x0f) : ((Slice_Twist_Prun[index >> 1] & 0xf0) >> 4)) == 0x0f) { setPruning(Slice_Twist_Prun, N_SLICE1 * newTwist + newSlice, (sbyte)(depth + 1)); done++; } } } } depth++; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Pruning table for the flip of the edges and the position (not permutation) of the UD-slice edges in phase1 // The pruning table entries give a lower estimation for the number of moves to reach the H-subgroup. for (int i = 0; i < N_SLICE1 * N_FLIP / 2; i++) { Slice_Flip_Prun[i] = -1; } depth = 0; setPruning(Slice_Flip_Prun, 0, 0); done = 1; while (done != N_SLICE1 * N_FLIP) { for (int i = 0; i < N_SLICE1 * N_FLIP; i++) { int flip = i / N_SLICE1, slice = i % N_SLICE1; if (((i % 2 == 0) ? (Slice_Flip_Prun[i >> 1] & 0x0f) : ((Slice_Flip_Prun[i >> 1] & 0xf0) >> 4)) == depth) { for (int j = 0; j < 18; j++) { int newSlice = FRtoBR_Move[slice * 24, j] / 24; int newFlip = flipMove[flip, j]; int index = N_SLICE1 * newFlip + newSlice; if (((index % 2 == 0) ? (Slice_Flip_Prun[index >> 1] & 0x0f) : ((Slice_Flip_Prun[index >> 1] & 0xf0) >> 4)) == 0x0f) { setPruning(Slice_Flip_Prun, N_SLICE1 * newFlip + newSlice, (sbyte)(depth + 1)); done++; } } } } depth++; } SavePrunData(); }
/** * 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, int maxDepth, bool useSeparator) { int s; // +++++++++++++++++++++check for wrong input +++++++++++++++++++++++++++++ int[] count = new int[6]; try { for (int i = 0; i < 54; i++) { Color col; if (!Enum.TryParse(facelets.Substring(i, 1), out col)) { throw new Exception("Invalid color"); } count[(int)col]++; } } catch (Exception e) { 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 +++++++++++++++++++++++++++++++++ CoordCube c = new 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; // +++++++++++++++++++ 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 (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; sbyte[] table = CoordCube.Slice_Flip_Prun; int index = CoordCube.N_SLICE1 * flip[n + 1] + slice[n + 1]; sbyte[] table1 = CoordCube.Slice_Twist_Prun; int index1 = CoordCube.N_SLICE1 * twist[n + 1] + slice[n + 1]; minDistPhase1[n + 1] = Math.Max((index % 2 == 0) ? (table[index >> 1] & 0x0f) : ((table[index >> 1] & 0xf0) >> 4), (index1 % 2 == 0) ? (table1[index1 >> 1] & 0x0f) : ((table1[index1 >> 1] & 0xf0) >> 4)); // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 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); }