package cs.min2phase; import java.util.Random; /** * Some useful functions. */ public class Tools /*implements Runnable*/ { static final boolean USE_TWIST_FLIP_PRUN = true; private static boolean inited = false; private static int[] initState = new int[2]; private static int[] require = {0x0, 0x1, 0x2, 0x2, 0x2, 0x7, 0xa, 0x3, 0x13, 0x13, 0x3, 0x6e, 0xca, 0xa6, 0x612, 0x512}; private static void initIdx(int idx) { switch (idx) { case 0 : CubieCube.initMove(); break;//- case 1 : CubieCube.initSym(); break;//0 case 2 : CubieCube.initFlipSym2Raw(); break;//1 case 3 : CubieCube.initTwistSym2Raw(); break;//1 case 4 : CubieCube.initPermSym2Raw(); break;//1 case 5 : CoordCube.initFlipMove(); break;//0, 1, 2 case 6 : CoordCube.initTwistMove(); break;//0, 1, 3 case 7 : CoordCube.initUDSliceMoveConj(); break;//0, 1 case 8 : CoordCube.initCPermMove(); break;//0, 1, 4 case 9 : CoordCube.initEPermMove(); break;//0, 1, 4 case 10 : CoordCube.initMPermMoveConj(); break;//0, 1 case 11 : if (USE_TWIST_FLIP_PRUN) {CoordCube.initTwistFlipPrun();} break;//1, 2, 3, 5, 6 case 12 : CoordCube.initSliceTwistPrun(); break;//1, 3, 6, 7 case 13 : CoordCube.initSliceFlipPrun(); break;//1, 2, 5, 7 case 14 : CoordCube.initMEPermPrun(); break;//1, 4, 9, 10 case 15 : CoordCube.initMCPermPrun(); break;//1, 4, 8, 10 } } protected Tools() {} /** * Main Initialization Function, can be ignored. */ /* public void run() { while (true) { int choice = -1; synchronized (initState) { if (initState[0] == 0xffff) { return; } for (int i=0; i<16; i++) { if (((initState[0]>>i)&1)==0 && ((initState[1]&require[i])==require[i])) { choice = i; initState[0] |= 1 << choice; break; } } if (choice == -1) { try { initState.wait(); continue; } catch (InterruptedException e) { e.printStackTrace(); System.exit(1); } } } long t = System.nanoTime(); initIdx(choice); System.out.println(choice + "\t" + (System.nanoTime() - t)); synchronized (initState) { initState[1] |= 1 << choice; initState.notifyAll(); } } } private static void initParallel(int N_thread) { Thread[] initThreads = new Thread[N_thread-1]; for (int i=0; i<N_thread-1; i++) { initThreads[i] = new Thread(new Tools()); initThreads[i].start(); } new Tools().run(); try { for (int i=0; i<N_thread-1; i++) { initThreads[i].join(); } } catch (Exception e) { e.printStackTrace(); System.exit(0); } } */ /** * Initialization of the Solver.<br> * Always below 0.5 seconds with Multiple-Thread.<br> * call it before first solving, or it will be called by {@link cs.min2phase.Search#solution(java.lang.String facelets, int maxDepth, long timeOut, long timeMin, int verbose)} at first solving. */ public synchronized static void init() { if (inited) { return; } /** * Can be replaced by: * new Tools().run(); */ //initParallel(Runtime.getRuntime().availableProcessors()); //initParallel(1); // This linear init is something gwt can deal with, // unlike the threading madness above. for(int i = 0; i <= 15; i++) { initIdx(i); } inited = true; } /** * @return whether the package is initialized. */ public static boolean isInited() { return inited; } /** * initializing from cached tables(move table, pruning table, etc.) * * @param in * Where to read tables. * * @see cs.min2phase.Tools#saveTo(java.io.DataOutput) */ /* Yeee vestigial code, I'm sorry --jfly public static void initFrom(DataInput in) throws IOException { if (inited) { return; } read(CubieCube.FlipS2R, in); read(CubieCube.TwistS2R, in); read(CubieCube.EPermS2R, in); read(CubieCube.MtoEPerm, in); read(CoordCube.TwistMove, in); read(CoordCube.FlipMove, in); read(CoordCube.UDSliceMove, in); read(CoordCube.UDSliceConj, in); read(CoordCube.CPermMove, in); read(CoordCube.EPermMove, in); read(CoordCube.MPermMove, in); read(CoordCube.MPermConj, in); read(CoordCube.UDSliceTwistPrun, in); read(CoordCube.UDSliceFlipPrun, in); read(CoordCube.MCPermPrun, in); read(CoordCube.MEPermPrun, in); if (USE_TWIST_FLIP_PRUN) { read(CoordCube.TwistFlipPrun, in); } CubieCube.initMove(); CubieCube.initSym(); inited = true; } private static void read(char[] arr, DataInput in) throws IOException { for (int i=0, len=arr.length; i<len; i++) { arr[i] = in.readChar(); } } private static void read(int[] arr, DataInput in) throws IOException { for (int i=0, len=arr.length; i<len; i++) { arr[i] = in.readInt(); } } private static void read(char[][] arr, DataInput in) throws IOException { for (int i=0, leng=arr.length; i<leng; i++) { for (int j=0, len=arr[i].length; j<len; j++) { arr[i][j] = in.readChar(); } } } private static void write(char[] arr, DataOutput out) throws IOException { for (int i=0, len=arr.length; i<len; i++) { out.writeChar(arr[i]); } } private static void write(int[] arr, DataOutput out) throws IOException { for (int i=0, len=arr.length; i<len; i++) { out.writeInt(arr[i]); } } private static void write(char[][] arr, DataOutput out) throws IOException { for (int i=0, leng=arr.length; i<leng; i++) { for (int j=0, len=arr[i].length; j<len; j++) { out.writeChar(arr[i][j]); } } } */ /** * cache tables (move tables, pruning table, etc.), and read it while initializing. * * @param out * Where to cache tables. * * @see cs.min2phase.Tools#initFrom(java.io.DataInput) */ /* public static void saveTo(DataOutput out) throws IOException { init(); write(CubieCube.FlipS2R, out); // 672 Bytes write(CubieCube.TwistS2R, out); // + 648 Bytes write(CubieCube.EPermS2R, out); // + 5, 536 Bytes write(CubieCube.MtoEPerm, out); // + 80, 640 Bytes write(CoordCube.TwistMove, out); // + 11, 664 Bytes write(CoordCube.FlipMove, out); // + 12, 096 Bytes write(CoordCube.UDSliceMove, out); // + 17, 820 Bytes write(CoordCube.UDSliceConj, out); // + 7, 920 Bytes write(CoordCube.CPermMove, out); // + 99, 648 Bytes write(CoordCube.EPermMove, out); // + 55, 360 Bytes write(CoordCube.MPermMove, out); // + 480 Bytes write(CoordCube.MPermConj, out); // + 768 Bytes write(CoordCube.UDSliceTwistPrun, out); // + 80, 192 Bytes write(CoordCube.UDSliceFlipPrun, out); // + 83, 160 Bytes write(CoordCube.MCPermPrun, out); // + 33, 216 Bytes write(CoordCube.MEPermPrun, out); // + 33, 216 Bytes // = 523, 036 Bytes if (USE_TWIST_FLIP_PRUN) { write(CoordCube.TwistFlipPrun, out);// + 435, 456 Bytes } // = 958, 492 Bytes } */ private static final Random r = new Random(); public static String randomCube() { return randomCube(r); } /** * Generates a random cube.<br> * * The random source can be set by {@link cs.min2phase.Tools#setRandomSource(java.util.Random)} * * @return A random cube in the string representation. Each cube of the cube space has almost (depends on randomSource) the same probability. * * @see cs.min2phase.Tools#setRandomSource(java.util.Random) * @see cs.min2phase.Search#solution(java.lang.String facelets, int maxDepth, long timeOut, long timeMin, int verbose) */ public static String randomCube(Random gen) { return randomState(STATE_RANDOM, STATE_RANDOM, STATE_RANDOM, STATE_RANDOM, gen); } private static int resolveOri(byte[] arr, int base, Random gen) { int sum = 0, idx = 0, lastUnknown = -1; for (int i=0; i<arr.length; i++) { if (arr[i] == -1) { arr[i] = (byte) gen.nextInt(base); lastUnknown = i; } sum += arr[i]; } if (sum % base != 0 && lastUnknown != -1) { arr[lastUnknown] = (byte) ((30 + arr[lastUnknown] - sum) % base); } for (int i=0; i<arr.length-1; i++) { idx *= base; idx += arr[i]; } return idx; } private static int countUnknown(byte[] arr) { if (arr == STATE_SOLVED) { return 0; } int cnt = 0; for (int i=0; i<arr.length; i++) { if (arr[i] == -1) { cnt++; } } return cnt; } private static int resolvePerm(byte[] arr, int cntU, int parity, Random gen) { if (arr == STATE_SOLVED) { return 0; } else if (arr == STATE_RANDOM) { return parity == -1 ? gen.nextInt(2) : parity; } byte[] val = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; for (int i=0; i<arr.length; i++) { if (arr[i] != -1) { val[arr[i]] = -1; } } int idx = 0; for (int i=0; i<arr.length; i++) { if (val[i] != -1) { int j = gen.nextInt(idx + 1); byte temp = val[i]; val[idx++] = val[j]; val[j] = temp; } } int last = -1; for (idx=0; idx<arr.length && cntU>0; idx++) { if (arr[idx] == -1) { if (cntU == 2) { last = idx; } arr[idx] = val[--cntU]; } } int p = Util.getNParity(Util.getNPerm(arr, arr.length), arr.length); if (p == 1-parity && last != -1) { byte temp = arr[idx-1]; arr[idx-1] = arr[last]; arr[last] = temp; } return p; } public static final byte[] STATE_RANDOM = null; public static final byte[] STATE_SOLVED = new byte[0]; protected static String randomState(byte[] cp, byte[] co, byte[] ep, byte[] eo, Random gen) { int parity; int cntUE = ep == STATE_RANDOM ? 12 : countUnknown(ep); int cntUC = cp == STATE_RANDOM ? 8 : countUnknown(cp); int cpVal, epVal; if (cntUE < 2) { //ep != STATE_RANDOM if (ep == STATE_SOLVED) { epVal = parity = 0; } else { parity = resolvePerm(ep, cntUE, -1, gen); epVal = Util.getNPerm(ep, 12); } if (cp == STATE_SOLVED) { cpVal = 0; } else if (cp == STATE_RANDOM) { do { cpVal = gen.nextInt(40320); } while (Util.getNParity(cpVal, 8) != parity); } else { resolvePerm(cp, cntUC, parity, gen); cpVal = Util.getNPerm(cp, 8); } } else { //ep != STATE_SOLVED if (cp == STATE_SOLVED) { cpVal = parity = 0; } else if (cp == STATE_RANDOM) { cpVal = gen.nextInt(40320); parity = Util.getNParity(cpVal, 8); } else { parity = resolvePerm(cp, cntUC, -1, gen); cpVal = Util.getNPerm(cp, 8); } if (ep == STATE_RANDOM) { do { epVal = gen.nextInt(479001600); } while (Util.getNParity(epVal, 12) != parity); } else { resolvePerm(ep, cntUE, parity, gen); epVal = Util.getNPerm(ep, 12); } } return Util.toFaceCube(new CubieCube( cpVal, co == STATE_RANDOM ? gen.nextInt(2187) : (co == STATE_SOLVED ? 0 : resolveOri(co, 3, gen)), epVal, eo == STATE_RANDOM ? gen.nextInt(2048) : (eo == STATE_SOLVED ? 0 : resolveOri(eo, 2, gen)))); } public static String randomLastLayer() { return randomLastLayer(r); } public static String randomLastLayer(Random gen) { return randomState( new byte[]{-1, -1, -1, -1, 4, 5, 6, 7}, new byte[]{-1, -1, -1, -1, 0, 0, 0, 0}, new byte[]{-1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11}, new byte[]{-1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0}, gen); } public static String randomLastSlot() { return randomLastSlot(r); } public static String randomLastSlot(Random gen) { return randomState( new byte[]{-1, -1, -1, -1, -1, 5, 6, 7}, new byte[]{-1, -1, -1, -1, -1, 0, 0, 0}, new byte[]{-1, -1, -1, -1, 4, 5, 6, 7, -1, 9, 10, 11}, new byte[]{-1, -1, -1, -1, 0, 0, 0, 0, -1, 0, 0, 0}, gen); } public static String randomZBLastLayer() { return randomZBLastLayer(r); } public static String randomZBLastLayer(Random gen) { return randomState( new byte[]{-1, -1, -1, -1, 4, 5, 6, 7}, new byte[]{-1, -1, -1, -1, 0, 0, 0, 0}, new byte[]{-1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11}, STATE_SOLVED, gen); } public static String randomCornerOfLastLayer() { return randomCornerOfLastLayer(r); } public static String randomCornerOfLastLayer(Random gen) { return randomState( new byte[]{-1, -1, -1, -1, 4, 5, 6, 7}, new byte[]{-1, -1, -1, -1, 0, 0, 0, 0}, STATE_SOLVED, STATE_SOLVED, gen); } public static String randomEdgeOfLastLayer() { return randomEdgeOfLastLayer(r); } public static String randomEdgeOfLastLayer(Random gen) { return randomState( STATE_SOLVED, STATE_SOLVED, new byte[]{-1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11}, new byte[]{-1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0}, gen); } public static String randomCrossSolved() { return randomCrossSolved(r); } public static String randomCrossSolved(Random gen) { return randomState( STATE_RANDOM, STATE_RANDOM, new byte[]{-1, -1, -1, -1, 4, 5, 6, 7, -1, -1, -1, -1}, new byte[]{-1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1}, gen); } public static String randomEdgeSolved() { return randomEdgeSolved(r); } public static String randomEdgeSolved(Random gen) { return randomState( STATE_RANDOM, STATE_RANDOM, STATE_SOLVED, STATE_SOLVED, gen); } public static String randomCornerSolved() { return randomCornerSolved(r); } public static String randomCornerSolved(Random gen) { return randomState( STATE_SOLVED, STATE_SOLVED, STATE_RANDOM, STATE_RANDOM, gen); } public static String superFlip() { return Util.toFaceCube(new CubieCube(0, 0, 0, 2047)); } /** * Check whether the cube definition string s represents a solvable cube. * * @param facelets is the cube definition string , see {@link cs.min2phase.Search#solution(java.lang.String facelets, int maxDepth, long timeOut, long timeMin, int verbose)} * @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 facelets) { return new Search().verify(facelets); } }