// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Compute the inverse CubieCube private void invCubieCube(CubieCube c) { foreach (Edge edge in Enums.Edges) { c.ep[(int)ep[(int)edge]] = edge; } foreach (Edge edge in Enums.Edges) { c.eo[(int)edge] = eo[(int)c.ep[(int)edge]]; } foreach (Corner corn in Enums.Corners) { c.cp[(int)cp[(int)corn]] = corn; } foreach (Corner corn in Enums.Corners) { sbyte 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] = (sbyte)-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 // /** * 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()); }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // 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]; sbyte[] cOri = new sbyte[8]; foreach (Corner corn in Enums.Corners) { cPerm[(int)corn] = cp[(int)b.cp[(int)corn]]; sbyte oriA = co[(int)b.cp[(int)corn]]; sbyte oriB = b.co[(int)corn]; sbyte ori = 0; ; if (oriA < 3 && oriB < 3) // if both cubes are regular cubes... { ori = (sbyte)(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 = (sbyte)(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 = (sbyte)(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 = (sbyte)(oriA - oriB); if (ori < 0) { ori += 3; // the composition is a regular cube } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ } cOri[(int)corn] = ori; } foreach (Corner c in Enums.Corners) { int cornerIdx = (int)c; cp[cornerIdx] = cPerm[cornerIdx]; co[cornerIdx] = cOri[cornerIdx]; } }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // 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(); }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Multiply this CubieCube with another cubiecube b, restricted to the edges. public void edgeMultiply(CubieCube b) { Edge[] ePerm = new Edge[12]; sbyte[] eOri = new sbyte[12]; foreach (Edge edge in Enums.Edges) { int edgeIdx = (int)edge; ePerm[edgeIdx] = ep[(int)b.ep[edgeIdx]]; eOri[edgeIdx] = (sbyte)((b.eo[edgeIdx] + eo[(int)b.ep[edgeIdx]]) % 2); } foreach (Edge e in Enums.Edges) { ep[(int)e] = ePerm[(int)e]; eo[(int)e] = eOri[(int)e]; } }
/** * 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()); }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // 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()); }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // 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; }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // 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(); }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Compute the inverse CubieCube private void invCubieCube(CubieCube c) { foreach (Edge edge in Enums.Edges) c.ep[(int) ep[(int) edge]] = edge; foreach (Edge edge in Enums.Edges) c.eo[(int) edge] = eo[(int) c.ep[(int) edge]]; foreach (Corner corn in Enums.Corners) c.cp[(int) cp[(int) corn]] = corn; foreach (Corner corn in Enums.Corners) { sbyte 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] = (sbyte) -ori; if (c.co[(int) corn] < 0) c.co[(int) corn] += 3; } } }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Multiply this CubieCube with another CubieCube b. public void multiply(CubieCube b) { cornerMultiply(b); // edgeMultiply(b); }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Multiply this CubieCube with another cubiecube b, restricted to the edges. public void edgeMultiply(CubieCube b) { Edge[] ePerm = new Edge[12]; sbyte[] eOri = new sbyte[12]; foreach (Edge edge in Enums.Edges) { int edgeIdx = (int)edge; ePerm[edgeIdx] = ep[(int)b.ep[edgeIdx]]; eOri[edgeIdx] = (sbyte)((b.eo[edgeIdx] + eo[(int)b.ep[edgeIdx]]) % 2); } foreach (Edge e in Enums.Edges) { ep[(int) e] = ePerm[(int) e]; eo[(int) e] = eOri[(int) e]; } }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // 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 }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // 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); }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Multiply this CubieCube with another CubieCube b. public void multiply(CubieCube b) { cornerMultiply(b); // edgeMultiply(b); }
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); }
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(); }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // 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]; sbyte[] cOri = new sbyte[8]; foreach (Corner corn in Enums.Corners) { cPerm[(int) corn] = cp[(int) b.cp[(int) corn]]; sbyte oriA = co[(int) b.cp[(int) corn]]; sbyte oriB = b.co[(int) corn]; sbyte ori = 0; ; if (oriA < 3 && oriB < 3) // if both cubes are regular cubes... { ori = (sbyte) (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 = (sbyte) (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 = (sbyte) (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 = (sbyte) (oriA - oriB); if (ori < 0) ori += 3; // the composition is a regular cube // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ } cOri[(int) corn] = ori; } foreach (Corner c in Enums.Corners) { int cornerIdx = (int) c; cp[cornerIdx] = cPerm[cornerIdx]; co[cornerIdx] = cOri[cornerIdx]; } }