diff --git a/src/llminx/LLMinx.java b/src/llminx/LLMinx.java new file mode 100644 index 0000000..3e85418 --- /dev/null +++ b/src/llminx/LLMinx.java @@ -0,0 +1,1095 @@ +package llminx; + +/** + * + */ +public class LLMinx implements Comparable { + + public static final byte UC1_POSITION = 0; + public static final byte UC2_POSITION = 1; + public static final byte UC3_POSITION = 2; + public static final byte UC4_POSITION = 3; + public static final byte UC5_POSITION = 4; + public static final byte RC1_POSITION = 5; + public static final byte RC5_POSITION = 6; + public static final byte FC5_POSITION = 7; + public static final byte FC1_POSITION = 8; + public static final byte FC2_POSITION = 9; + public static final byte LC1_POSITION = 10; + public static final byte LC2_POSITION = 11; + public static final byte BC1_POSITION = 12; + public static final byte BC2_POSITION = 13; + public static final byte UE1_POSITION = 0; + public static final byte UE2_POSITION = 1; + public static final byte UE3_POSITION = 2; + public static final byte UE4_POSITION = 3; + public static final byte UE5_POSITION = 4; + public static final byte RE2_POSITION = 5; + public static final byte RE3_POSITION = 6; + public static final byte RE4_POSITION = 7; + public static final byte FE2_POSITION = 8; + public static final byte FE3_POSITION = 9; + public static final byte FE4_POSITION = 10; + public static final byte FE5_POSITION = 11; + public static final byte LE3_POSITION = 12; + public static final byte LE4_POSITION = 13; + public static final byte LE5_POSITION = 14; + public static final byte BE3_POSITION = 15; + public static final byte BE4_POSITION = 16; + public static final byte BE5_POSITION = 17; + + public static final byte NEUTRAL_ORIENTATION = 0; + public static final byte POSITIVE_ORIENTATION = 1; + public static final byte NEGATIVE_ORIENTATION = 2; + public static final byte IGNORE_ORIENTATION = 3; + + public static final byte R_MOVE = 0; + public static final byte Ri_MOVE = 1; + public static final byte R2_MOVE = 2; + public static final byte R2i_MOVE = 3; + public static final byte L_MOVE = 4; + public static final byte Li_MOVE = 5; + public static final byte L2_MOVE = 6; + public static final byte L2i_MOVE = 7; + public static final byte U_MOVE = 8; + public static final byte Ui_MOVE = 9; + public static final byte U2_MOVE = 10; + public static final byte U2i_MOVE = 11; + public static final byte F_MOVE = 12; + public static final byte Fi_MOVE = 13; + public static final byte F2_MOVE = 14; + public static final byte F2i_MOVE = 15; + public static final byte B_MOVE = 16; + public static final byte Bi_MOVE = 17; + public static final byte B2_MOVE = 18; + public static final byte B2i_MOVE = 19; + + private static byte[] CORNER_ORIENTATION_CW = new byte[]{ + POSITIVE_ORIENTATION, + NEGATIVE_ORIENTATION, + NEUTRAL_ORIENTATION, + IGNORE_ORIENTATION, + }; + + private static byte[] CORNER_ORIENTATION_ANTI_CW = new byte[]{ + NEGATIVE_ORIENTATION, + NEUTRAL_ORIENTATION, + POSITIVE_ORIENTATION, + IGNORE_ORIENTATION + }; + + public static byte[] INVERSE_MOVES = new byte[]{ + Ri_MOVE, + R_MOVE, + R2i_MOVE, + R2_MOVE, + Li_MOVE, + L_MOVE, + L2i_MOVE, + L2_MOVE, + Ui_MOVE, + U_MOVE, + U2i_MOVE, + U2_MOVE, + Fi_MOVE, + F_MOVE, + F2i_MOVE, + F2_MOVE, + Bi_MOVE, + B_MOVE, + B2i_MOVE, + B2_MOVE + }; + + public static final String[] POSITION_STRINGS = {}; + public static final String[] MOVE_STRINGS = { + "R ", + "R\' ", + "R2 ", + "R2\' ", + "L ", + "L\' ", + "L2 ", + "L2\' ", + "U ", + "U\' ", + "U2 ", + "U2\' ", + "F ", + "F\' ", + "F2 ", + "F2\' ", + "B ", + "B\' ", + "B2 ", + "B2\' ", + }; + + private static int sMaxDepth = 100; + private static boolean sKeepMoves = true; + + private byte[] fCornerPositions; + private boolean[] fIgnoreCornerPositions; + private byte[] fEdgePositions; + private boolean[] fIgnoreEdgePositions; + private int fCornerOrientations; + private boolean[] fIgnoreCornerOrientations; + private int fEdgeOrientations; + private boolean[] fIgnoreEdgeOrientations; + private byte[] fMoves; + private int fDepth; + private byte fLastMove = -1; + + public LLMinx() { + fCornerPositions = new byte[14]; + fIgnoreCornerPositions = new boolean[14]; + fIgnoreCornerOrientations = new boolean[14]; + for (int i = 0; i < fCornerPositions.length; i++) { + fCornerPositions[i] = (byte) i; + fIgnoreCornerPositions[i] = false; + fIgnoreCornerOrientations[i] = false; + } + fEdgePositions = new byte[18]; + fIgnoreEdgePositions = new boolean[18]; + fIgnoreEdgeOrientations = new boolean[18]; + for (int i = 0; i < fEdgePositions.length; i++) { + fEdgePositions[i] = (byte) i; + fIgnoreEdgePositions[i] = false; + fIgnoreEdgeOrientations[i] = false; + } + fCornerOrientations = 0; + fEdgeOrientations = 0; + fMoves = new byte[sMaxDepth]; + fDepth = 0; + } + + public LLMinx(byte[] aCornerPositions, + byte[] aEdgePositions, + int aCornerOrientations, + int aEdgeOrientation, + boolean[] aIgnoreCornerPieces, + boolean[] aIgnoreEdgePieces, + boolean[] aIgnoreCornerOrientations, + boolean[] aIgnoreEdgeOrientations, + byte[] aMoves, + int aDepth) { + fCornerPositions = aCornerPositions; + fEdgePositions = aEdgePositions; + fCornerOrientations = aCornerOrientations; + fEdgeOrientations = aEdgeOrientation; + fIgnoreCornerPositions = aIgnoreCornerPieces; + fIgnoreEdgePositions = aIgnoreEdgePieces; + fIgnoreCornerOrientations = aIgnoreCornerOrientations; + fIgnoreEdgeOrientations = aIgnoreEdgeOrientations; + fMoves = aMoves; + fDepth = aDepth; + } + + public static int getMaxDepth() { + return sMaxDepth; + } + + public static void setMaxDepth(int aMaxDepth) { + LLMinx.sMaxDepth = aMaxDepth; + } + + public static boolean isKeepMoves() { + return sKeepMoves; + } + + public static void setKeepMoves(boolean keepMoves) { + sKeepMoves = keepMoves; + } + + public byte[] getMoves() { + return fMoves; + } + + public String getGeneratingMoves() { + StringBuffer move_string = new StringBuffer(fDepth * 4); + byte[] moves = new byte[fDepth]; + System.arraycopy(fMoves, 0, moves, 0, fDepth); + while (simplifyMoves(moves)) ; + for (int i = 0; i < fDepth && moves[i] != -1; i++) { + move_string.append(MOVE_STRINGS[moves[i]]); + } + return move_string.toString(); + } + + private boolean simplifyMoves(byte[] aMoves) { + boolean simplified = false; + for (int i = 1; i < aMoves.length && aMoves[i] != -1; i++) { + if ((aMoves[i] == aMoves[i - 1]) && (aMoves[i] % 4 < 2)) { + aMoves[i - 1] += 2; + simplified = true; + } + if (simplified) { + if (i < aMoves.length - 1) { + System.arraycopy(aMoves, i + 1, aMoves, i, aMoves.length - i - 1); + } + aMoves[aMoves.length - 1] = -1; + break; + } + } + return simplified; + } + + public String getSolvingMoves() { + StringBuffer moves = new StringBuffer(fDepth * 4); + for (int i = fDepth - 1; i >= 0; i--) { + moves.append(MOVE_STRINGS[INVERSE_MOVES[fMoves[i]]]); + } + return moves.toString(); + } + + public byte getLastMove() { + return fLastMove; + } + + public int getDepth() { + return fDepth; + } + + public byte[] getCornerPositions() { + return fCornerPositions; + } + + public byte[] getEdgePositions() { + return fEdgePositions; + } + + public int getCornerOrientations() { + return fCornerOrientations; + } + + public int getCornerOrientation(byte aPiece) { + return (fCornerOrientations >> (aPiece * 2)) & 3; + } + + public void setCornerOrientation(byte aPiece, int aOrientation) { + fCornerOrientations = (fCornerOrientations & (~(3 << (aPiece * 2)))) | (aOrientation << (aPiece * 2)); + } + + public int getEdgeOrientations() { + return fEdgeOrientations; + } + + public int getEdgeOrientation(byte aPiece) { + return (fEdgeOrientations >> aPiece) & 1; + } + + public void setEdgeOrientation(byte aPiece, int aOrientation) { + fEdgeOrientations = (fEdgeOrientations & (~(1 << aPiece))) | (aOrientation << aPiece); + } + + public void setCornerOrientations(int cornerOrientations) { + fCornerOrientations = cornerOrientations; + } + + public void setEdgeOrientations(int edgeOrientations) { + fEdgeOrientations = edgeOrientations; + } + + public boolean[] getIgnoreCornerPositions() { + return fIgnoreCornerPositions; + } + + public void setIgnoreCornerPositions(boolean[] ignoreCornerPieces) { + fIgnoreCornerPositions = ignoreCornerPieces; + } + + public boolean[] getIgnoreEdgePositions() { + return fIgnoreEdgePositions; + } + + public void setIgnoreEdgePositions(boolean[] ignoreEdgePieces) { + fIgnoreEdgePositions = ignoreEdgePieces; + } + + public boolean[] getIgnoreCornerOrientations() { + return fIgnoreCornerOrientations; + } + + public void setIgnoreCornerOrientations(boolean[] aIgnoreCornerOrientations) { + fIgnoreCornerOrientations = aIgnoreCornerOrientations; + } + + public boolean[] getIgnoreEdgeOrientations() { + return fIgnoreEdgeOrientations; + } + + public void setIgnoreEdgeOrientations(boolean[] aIgnoreEdgeOrientations) { + fIgnoreEdgeOrientations = aIgnoreEdgeOrientations; + } + + public void move(byte aMove) { + switch (aMove) { + case R_MOVE: + moveR(); + break; + case Ri_MOVE: + moveRi(); + break; + case R2_MOVE: + moveR2(); + break; + case R2i_MOVE: + moveR2i(); + break; + case L_MOVE: + moveL(); + break; + case Li_MOVE: + moveLi(); + break; + case L2_MOVE: + moveL2(); + break; + case L2i_MOVE: + moveL2i(); + break; + case U_MOVE: + moveU(); + break; + case Ui_MOVE: + moveUi(); + break; + case U2_MOVE: + moveU2(); + break; + case U2i_MOVE: + moveU2i(); + break; + case F_MOVE: + moveF(); + break; + case Fi_MOVE: + moveFi(); + break; + case F2_MOVE: + moveF2(); + break; + case F2i_MOVE: + moveF2i(); + break; + case B_MOVE: + moveB(); + break; + case Bi_MOVE: + moveBi(); + break; + case B2_MOVE: + moveB2(); + break; + case B2i_MOVE: + moveB2i(); + break; + } + } + + public byte undoMove() { + byte last_move = fLastMove; + move(INVERSE_MOVES[fLastMove]); + fDepth -= 2; + if (fDepth > 0) { + fLastMove = fMoves[fDepth - 1]; + } + else { + fLastMove = -1; + } + return last_move; + } + + public int getQTMLength() { + int length = 0; + for (int i = 0; i < fDepth; i++) { + length += (fMoves[i] % 4) / 2 + 1; + + } + return length; + } + + public int getHTMLength() { + int length = fDepth; + for (int i = 1; i < fDepth; i++) { + if ((fMoves[i] / 4) == (fMoves[i - 1] / 4)) { + length--; + } + + } + return length; + } + + public void moveU() { + byte old_uc1_pos = fCornerPositions[UC1_POSITION]; + byte old_ue1_pos = fEdgePositions[UE1_POSITION]; + int old_uc1_or = getCornerOrientation(UC1_POSITION); + int old_ue1_or = getEdgeOrientation(UE1_POSITION); + fCornerPositions[UC1_POSITION] = fCornerPositions[UC5_POSITION]; + fCornerPositions[UC5_POSITION] = fCornerPositions[UC4_POSITION]; + fCornerPositions[UC4_POSITION] = fCornerPositions[UC3_POSITION]; + fCornerPositions[UC3_POSITION] = fCornerPositions[UC2_POSITION]; + fCornerPositions[UC2_POSITION] = old_uc1_pos; + fEdgePositions[UE1_POSITION] = fEdgePositions[UE5_POSITION]; + fEdgePositions[UE5_POSITION] = fEdgePositions[UE4_POSITION]; + fEdgePositions[UE4_POSITION] = fEdgePositions[UE3_POSITION]; + fEdgePositions[UE3_POSITION] = fEdgePositions[UE2_POSITION]; + fEdgePositions[UE2_POSITION] = old_ue1_pos; + setCornerOrientation(UC1_POSITION, getCornerOrientation(UC5_POSITION)); + setCornerOrientation(UC5_POSITION, getCornerOrientation(UC4_POSITION)); + setCornerOrientation(UC4_POSITION, getCornerOrientation(UC3_POSITION)); + setCornerOrientation(UC3_POSITION, getCornerOrientation(UC2_POSITION)); + setCornerOrientation(UC2_POSITION, old_uc1_or); + setEdgeOrientation(UE1_POSITION, getEdgeOrientation(UE5_POSITION)); + setEdgeOrientation(UE5_POSITION, getEdgeOrientation(UE4_POSITION)); + setEdgeOrientation(UE4_POSITION, getEdgeOrientation(UE3_POSITION)); + setEdgeOrientation(UE3_POSITION, getEdgeOrientation(UE2_POSITION)); + setEdgeOrientation(UE2_POSITION, old_ue1_or); + if (sKeepMoves) fMoves[fDepth] = U_MOVE; + fDepth++; + fLastMove = U_MOVE; + } + + public void moveUi() { + byte old_uc1_pos = fCornerPositions[UC1_POSITION]; + byte old_ue1_pos = fEdgePositions[UE1_POSITION]; + int old_uc1_or = getCornerOrientation(UC1_POSITION); + int old_ue1_or = getEdgeOrientation(UE1_POSITION); + fCornerPositions[UC1_POSITION] = fCornerPositions[UC2_POSITION]; + fCornerPositions[UC2_POSITION] = fCornerPositions[UC3_POSITION]; + fCornerPositions[UC3_POSITION] = fCornerPositions[UC4_POSITION]; + fCornerPositions[UC4_POSITION] = fCornerPositions[UC5_POSITION]; + fCornerPositions[UC5_POSITION] = old_uc1_pos; + fEdgePositions[UE1_POSITION] = fEdgePositions[UE2_POSITION]; + fEdgePositions[UE2_POSITION] = fEdgePositions[UE3_POSITION]; + fEdgePositions[UE3_POSITION] = fEdgePositions[UE4_POSITION]; + fEdgePositions[UE4_POSITION] = fEdgePositions[UE5_POSITION]; + fEdgePositions[UE5_POSITION] = old_ue1_pos; + setCornerOrientation(UC1_POSITION, getCornerOrientation(UC2_POSITION)); + setCornerOrientation(UC2_POSITION, getCornerOrientation(UC3_POSITION)); + setCornerOrientation(UC3_POSITION, getCornerOrientation(UC4_POSITION)); + setCornerOrientation(UC4_POSITION, getCornerOrientation(UC5_POSITION)); + setCornerOrientation(UC5_POSITION, old_uc1_or); + setEdgeOrientation(UE1_POSITION, getEdgeOrientation(UE2_POSITION)); + setEdgeOrientation(UE2_POSITION, getEdgeOrientation(UE3_POSITION)); + setEdgeOrientation(UE3_POSITION, getEdgeOrientation(UE4_POSITION)); + setEdgeOrientation(UE4_POSITION, getEdgeOrientation(UE5_POSITION)); + setEdgeOrientation(UE5_POSITION, old_ue1_or); + if (sKeepMoves) fMoves[fDepth] = Ui_MOVE; + fDepth++; + fLastMove = Ui_MOVE; + } + + public void moveU2() { + byte old_uc1_pos = fCornerPositions[UC1_POSITION]; + byte old_ue1_pos = fEdgePositions[UE1_POSITION]; + int old_uc1_or = getCornerOrientation(UC1_POSITION); + int old_ue1_or = getEdgeOrientation(UE1_POSITION); + fCornerPositions[UC1_POSITION] = fCornerPositions[UC4_POSITION]; + fCornerPositions[UC4_POSITION] = fCornerPositions[UC2_POSITION]; + fCornerPositions[UC2_POSITION] = fCornerPositions[UC5_POSITION]; + fCornerPositions[UC5_POSITION] = fCornerPositions[UC3_POSITION]; + fCornerPositions[UC3_POSITION] = old_uc1_pos; + fEdgePositions[UE1_POSITION] = fEdgePositions[UE4_POSITION]; + fEdgePositions[UE4_POSITION] = fEdgePositions[UE2_POSITION]; + fEdgePositions[UE2_POSITION] = fEdgePositions[UE5_POSITION]; + fEdgePositions[UE5_POSITION] = fEdgePositions[UE3_POSITION]; + fEdgePositions[UE3_POSITION] = old_ue1_pos; + setCornerOrientation(UC1_POSITION, getCornerOrientation(UC4_POSITION)); + setCornerOrientation(UC4_POSITION, getCornerOrientation(UC2_POSITION)); + setCornerOrientation(UC2_POSITION, getCornerOrientation(UC5_POSITION)); + setCornerOrientation(UC5_POSITION, getCornerOrientation(UC3_POSITION)); + setCornerOrientation(UC3_POSITION, old_uc1_or); + setEdgeOrientation(UE1_POSITION, getEdgeOrientation(UE4_POSITION)); + setEdgeOrientation(UE4_POSITION, getEdgeOrientation(UE2_POSITION)); + setEdgeOrientation(UE2_POSITION, getEdgeOrientation(UE5_POSITION)); + setEdgeOrientation(UE5_POSITION, getEdgeOrientation(UE3_POSITION)); + setEdgeOrientation(UE3_POSITION, old_ue1_or); + if (sKeepMoves) fMoves[fDepth] = U2_MOVE; + fDepth++; + fLastMove = U2_MOVE; + } + + public void moveU2i() { + byte old_uc1_pos = fCornerPositions[UC1_POSITION]; + byte old_ue1_pos = fEdgePositions[UE1_POSITION]; + int old_uc1_or = getCornerOrientation(UC1_POSITION); + int old_ue1_or = getEdgeOrientation(UE1_POSITION); + fCornerPositions[UC1_POSITION] = fCornerPositions[UC3_POSITION]; + fCornerPositions[UC3_POSITION] = fCornerPositions[UC5_POSITION]; + fCornerPositions[UC5_POSITION] = fCornerPositions[UC2_POSITION]; + fCornerPositions[UC2_POSITION] = fCornerPositions[UC4_POSITION]; + fCornerPositions[UC4_POSITION] = old_uc1_pos; + fEdgePositions[UE1_POSITION] = fEdgePositions[UE3_POSITION]; + fEdgePositions[UE3_POSITION] = fEdgePositions[UE5_POSITION]; + fEdgePositions[UE5_POSITION] = fEdgePositions[UE2_POSITION]; + fEdgePositions[UE2_POSITION] = fEdgePositions[UE4_POSITION]; + fEdgePositions[UE4_POSITION] = old_ue1_pos; + setCornerOrientation(UC1_POSITION, getCornerOrientation(UC3_POSITION)); + setCornerOrientation(UC3_POSITION, getCornerOrientation(UC5_POSITION)); + setCornerOrientation(UC5_POSITION, getCornerOrientation(UC2_POSITION)); + setCornerOrientation(UC2_POSITION, getCornerOrientation(UC4_POSITION)); + setCornerOrientation(UC4_POSITION, old_uc1_or); + setEdgeOrientation(UE1_POSITION, getEdgeOrientation(UE3_POSITION)); + setEdgeOrientation(UE3_POSITION, getEdgeOrientation(UE5_POSITION)); + setEdgeOrientation(UE5_POSITION, getEdgeOrientation(UE2_POSITION)); + setEdgeOrientation(UE2_POSITION, getEdgeOrientation(UE4_POSITION)); + setEdgeOrientation(UE4_POSITION, old_ue1_or); + if (sKeepMoves) fMoves[fDepth] = U2i_MOVE; + fDepth++; + fLastMove = U2i_MOVE; + } + + public void moveL() { + byte old_lc1_pos = fCornerPositions[LC1_POSITION]; + byte old_ue2 = fEdgePositions[UE2_POSITION]; + int old_lc1_or = getCornerOrientation(LC1_POSITION); + int old_ue2_or = getEdgeOrientation(UE2_POSITION); + fCornerPositions[LC1_POSITION] = fCornerPositions[FC2_POSITION]; + fCornerPositions[FC2_POSITION] = fCornerPositions[UC4_POSITION]; + fCornerPositions[UC4_POSITION] = fCornerPositions[UC5_POSITION]; + fCornerPositions[UC5_POSITION] = fCornerPositions[LC2_POSITION]; + fCornerPositions[LC2_POSITION] = old_lc1_pos; + fEdgePositions[UE2_POSITION] = fEdgePositions[LE5_POSITION]; + fEdgePositions[LE5_POSITION] = fEdgePositions[LE4_POSITION]; + fEdgePositions[LE4_POSITION] = fEdgePositions[LE3_POSITION]; + fEdgePositions[LE3_POSITION] = fEdgePositions[FE5_POSITION]; + fEdgePositions[FE5_POSITION] = old_ue2; + setCornerOrientation(LC1_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(FC2_POSITION)]); + setCornerOrientation(FC2_POSITION, getCornerOrientation(UC4_POSITION)); + setCornerOrientation(UC4_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(UC5_POSITION)]); + setCornerOrientation(UC5_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(LC2_POSITION)]); + setCornerOrientation(LC2_POSITION, old_lc1_or); + setEdgeOrientation(UE2_POSITION, getEdgeOrientation(LE5_POSITION)); + setEdgeOrientation(LE5_POSITION, getEdgeOrientation(LE4_POSITION)); + setEdgeOrientation(LE4_POSITION, getEdgeOrientation(LE3_POSITION)); + setEdgeOrientation(LE3_POSITION, getEdgeOrientation(FE5_POSITION)); + setEdgeOrientation(FE5_POSITION, old_ue2_or); + if (sKeepMoves) fMoves[fDepth] = L_MOVE; + fDepth++; + fLastMove = L_MOVE; + } + + public void moveLi() { + byte old_lc1_pos = fCornerPositions[LC1_POSITION]; + byte old_ue2 = fEdgePositions[UE2_POSITION]; + int old_lc1_or = getCornerOrientation(LC1_POSITION); + int old_ue2_or = getEdgeOrientation(UE2_POSITION); + fCornerPositions[LC1_POSITION] = fCornerPositions[LC2_POSITION]; + fCornerPositions[LC2_POSITION] = fCornerPositions[UC5_POSITION]; + fCornerPositions[UC5_POSITION] = fCornerPositions[UC4_POSITION]; + fCornerPositions[UC4_POSITION] = fCornerPositions[FC2_POSITION]; + fCornerPositions[FC2_POSITION] = old_lc1_pos; + fEdgePositions[UE2_POSITION] = fEdgePositions[FE5_POSITION]; + fEdgePositions[FE5_POSITION] = fEdgePositions[LE3_POSITION]; + fEdgePositions[LE3_POSITION] = fEdgePositions[LE4_POSITION]; + fEdgePositions[LE4_POSITION] = fEdgePositions[LE5_POSITION]; + fEdgePositions[LE5_POSITION] = old_ue2; + setCornerOrientation(LC1_POSITION, getCornerOrientation(LC2_POSITION)); + setCornerOrientation(LC2_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(UC5_POSITION)]); + setCornerOrientation(UC5_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(UC4_POSITION)]); + setCornerOrientation(UC4_POSITION, getCornerOrientation(FC2_POSITION)); + setCornerOrientation(FC2_POSITION, CORNER_ORIENTATION_ANTI_CW[old_lc1_or]); + setEdgeOrientation(UE2_POSITION, getEdgeOrientation(FE5_POSITION)); + setEdgeOrientation(FE5_POSITION, getEdgeOrientation(LE3_POSITION)); + setEdgeOrientation(LE3_POSITION, getEdgeOrientation(LE4_POSITION)); + setEdgeOrientation(LE4_POSITION, getEdgeOrientation(LE5_POSITION)); + setEdgeOrientation(LE5_POSITION, old_ue2_or); + if (sKeepMoves) fMoves[fDepth] = Li_MOVE; + fDepth++; + fLastMove = Li_MOVE; + } + + public void moveL2() { + byte old_lc1_pos = fCornerPositions[LC1_POSITION]; + byte old_ue2 = fEdgePositions[UE2_POSITION]; + int old_lc1_or = getCornerOrientation(LC1_POSITION); + int old_ue2_or = getEdgeOrientation(UE2_POSITION); + fCornerPositions[LC1_POSITION] = fCornerPositions[UC4_POSITION]; + fCornerPositions[UC4_POSITION] = fCornerPositions[LC2_POSITION]; + fCornerPositions[LC2_POSITION] = fCornerPositions[FC2_POSITION]; + fCornerPositions[FC2_POSITION] = fCornerPositions[UC5_POSITION]; + fCornerPositions[UC5_POSITION] = old_lc1_pos; + fEdgePositions[UE2_POSITION] = fEdgePositions[LE4_POSITION]; + fEdgePositions[LE4_POSITION] = fEdgePositions[FE5_POSITION]; + fEdgePositions[FE5_POSITION] = fEdgePositions[LE5_POSITION]; + fEdgePositions[LE5_POSITION] = fEdgePositions[LE3_POSITION]; + fEdgePositions[LE3_POSITION] = old_ue2; + setCornerOrientation(LC1_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(UC4_POSITION)]); + setCornerOrientation(UC4_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(LC2_POSITION)]); + setCornerOrientation(LC2_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(FC2_POSITION)]); + setCornerOrientation(FC2_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(UC5_POSITION)]); + setCornerOrientation(UC5_POSITION, CORNER_ORIENTATION_CW[old_lc1_or]); + setEdgeOrientation(UE2_POSITION, getEdgeOrientation(LE4_POSITION)); + setEdgeOrientation(LE4_POSITION, getEdgeOrientation(FE5_POSITION)); + setEdgeOrientation(FE5_POSITION, getEdgeOrientation(LE5_POSITION)); + setEdgeOrientation(LE5_POSITION, getEdgeOrientation(LE3_POSITION)); + setEdgeOrientation(LE3_POSITION, old_ue2_or); + if (sKeepMoves) fMoves[fDepth] = L2_MOVE; + fDepth++; + fLastMove = L2_MOVE; + } + + public void moveL2i() { + byte old_lc1_pos = fCornerPositions[LC1_POSITION]; + byte old_ue2 = fEdgePositions[UE2_POSITION]; + int old_lc1_or = getCornerOrientation(LC1_POSITION); + int old_ue2_or = getEdgeOrientation(UE2_POSITION); + fCornerPositions[LC1_POSITION] = fCornerPositions[UC5_POSITION]; + fCornerPositions[UC5_POSITION] = fCornerPositions[FC2_POSITION]; + fCornerPositions[FC2_POSITION] = fCornerPositions[LC2_POSITION]; + fCornerPositions[LC2_POSITION] = fCornerPositions[UC4_POSITION]; + fCornerPositions[UC4_POSITION] = old_lc1_pos; + fEdgePositions[UE2_POSITION] = fEdgePositions[LE3_POSITION]; + fEdgePositions[LE3_POSITION] = fEdgePositions[LE5_POSITION]; + fEdgePositions[LE5_POSITION] = fEdgePositions[FE5_POSITION]; + fEdgePositions[FE5_POSITION] = fEdgePositions[LE4_POSITION]; + fEdgePositions[LE4_POSITION] = old_ue2; + setCornerOrientation(LC1_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(UC5_POSITION)]); + setCornerOrientation(UC5_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(FC2_POSITION)]); + setCornerOrientation(FC2_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(LC2_POSITION)]); + setCornerOrientation(LC2_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(UC4_POSITION)]); + setCornerOrientation(UC4_POSITION, CORNER_ORIENTATION_ANTI_CW[old_lc1_or]); + setEdgeOrientation(UE2_POSITION, getEdgeOrientation(LE3_POSITION)); + setEdgeOrientation(LE3_POSITION, getEdgeOrientation(LE5_POSITION)); + setEdgeOrientation(LE5_POSITION, getEdgeOrientation(FE5_POSITION)); + setEdgeOrientation(FE5_POSITION, getEdgeOrientation(LE4_POSITION)); + setEdgeOrientation(LE4_POSITION, old_ue2_or); + if (sKeepMoves) fMoves[fDepth] = L2i_MOVE; + fDepth++; + fLastMove = L2i_MOVE; + } + + public void moveR() { + byte old_rc1_pos = fCornerPositions[RC1_POSITION]; + byte old_eu5_pos = fEdgePositions[UE5_POSITION]; + int old_rc1_or = getCornerOrientation(RC1_POSITION); + int old_ue5_or = getEdgeOrientation(UE5_POSITION); + fCornerPositions[RC1_POSITION] = fCornerPositions[RC5_POSITION]; + fCornerPositions[RC5_POSITION] = fCornerPositions[UC2_POSITION]; + fCornerPositions[UC2_POSITION] = fCornerPositions[UC3_POSITION]; + fCornerPositions[UC3_POSITION] = fCornerPositions[FC5_POSITION]; + fCornerPositions[FC5_POSITION] = old_rc1_pos; + fEdgePositions[UE5_POSITION] = fEdgePositions[FE2_POSITION]; + fEdgePositions[FE2_POSITION] = fEdgePositions[RE4_POSITION]; + fEdgePositions[RE4_POSITION] = fEdgePositions[RE3_POSITION]; + fEdgePositions[RE3_POSITION] = fEdgePositions[RE2_POSITION]; + fEdgePositions[RE2_POSITION] = old_eu5_pos; + setCornerOrientation(RC1_POSITION, getCornerOrientation(RC5_POSITION)); + setCornerOrientation(RC5_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(UC2_POSITION)]); + setCornerOrientation(UC2_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(UC3_POSITION)]); + setCornerOrientation(UC3_POSITION, getCornerOrientation(FC5_POSITION)); + setCornerOrientation(FC5_POSITION, CORNER_ORIENTATION_CW[old_rc1_or]); + setEdgeOrientation(UE5_POSITION, getEdgeOrientation(FE2_POSITION)); + setEdgeOrientation(FE2_POSITION, getEdgeOrientation(RE4_POSITION)); + setEdgeOrientation(RE4_POSITION, getEdgeOrientation(RE3_POSITION)); + setEdgeOrientation(RE3_POSITION, getEdgeOrientation(RE2_POSITION)); + setEdgeOrientation(RE2_POSITION, old_ue5_or); + if (sKeepMoves) fMoves[fDepth] = R_MOVE; + fDepth++; + fLastMove = R_MOVE; + } + + public void moveRi() { + byte old_rc1_pos = fCornerPositions[RC1_POSITION]; + byte old_eu5_pos = fEdgePositions[UE5_POSITION]; + int old_rc1_or = getCornerOrientation(RC1_POSITION); + int old_ue5_or = getEdgeOrientation(UE5_POSITION); + fCornerPositions[RC1_POSITION] = fCornerPositions[FC5_POSITION]; + fCornerPositions[FC5_POSITION] = fCornerPositions[UC3_POSITION]; + fCornerPositions[UC3_POSITION] = fCornerPositions[UC2_POSITION]; + fCornerPositions[UC2_POSITION] = fCornerPositions[RC5_POSITION]; + fCornerPositions[RC5_POSITION] = old_rc1_pos; + fEdgePositions[UE5_POSITION] = fEdgePositions[RE2_POSITION]; + fEdgePositions[RE2_POSITION] = fEdgePositions[RE3_POSITION]; + fEdgePositions[RE3_POSITION] = fEdgePositions[RE4_POSITION]; + fEdgePositions[RE4_POSITION] = fEdgePositions[FE2_POSITION]; + fEdgePositions[FE2_POSITION] = old_eu5_pos; + setCornerOrientation(RC1_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(FC5_POSITION)]); + setCornerOrientation(FC5_POSITION, getCornerOrientation(UC3_POSITION)); + setCornerOrientation(UC3_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(UC2_POSITION)]); + setCornerOrientation(UC2_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(RC5_POSITION)]); + setCornerOrientation(RC5_POSITION, old_rc1_or); + setEdgeOrientation(UE5_POSITION, getEdgeOrientation(RE2_POSITION)); + setEdgeOrientation(RE2_POSITION, getEdgeOrientation(RE3_POSITION)); + setEdgeOrientation(RE3_POSITION, getEdgeOrientation(RE4_POSITION)); + setEdgeOrientation(RE4_POSITION, getEdgeOrientation(FE2_POSITION)); + setEdgeOrientation(FE2_POSITION, old_ue5_or); + if (sKeepMoves) fMoves[fDepth] = Ri_MOVE; + fDepth++; + fLastMove = Ri_MOVE; + } + + public void moveR2() { + byte old_rc1_pos = fCornerPositions[RC1_POSITION]; + byte old_eu5_pos = fEdgePositions[UE5_POSITION]; + int old_rc1_or = getCornerOrientation(RC1_POSITION); + int old_ue5_or = getEdgeOrientation(UE5_POSITION); + fCornerPositions[RC1_POSITION] = fCornerPositions[UC2_POSITION]; + fCornerPositions[UC2_POSITION] = fCornerPositions[FC5_POSITION]; + fCornerPositions[FC5_POSITION] = fCornerPositions[RC5_POSITION]; + fCornerPositions[RC5_POSITION] = fCornerPositions[UC3_POSITION]; + fCornerPositions[UC3_POSITION] = old_rc1_pos; + fEdgePositions[UE5_POSITION] = fEdgePositions[RE4_POSITION]; + fEdgePositions[RE4_POSITION] = fEdgePositions[RE2_POSITION]; + fEdgePositions[RE2_POSITION] = fEdgePositions[FE2_POSITION]; + fEdgePositions[FE2_POSITION] = fEdgePositions[RE3_POSITION]; + fEdgePositions[RE3_POSITION] = old_eu5_pos; + setCornerOrientation(RC1_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(UC2_POSITION)]); + setCornerOrientation(UC2_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(FC5_POSITION)]); + setCornerOrientation(FC5_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(RC5_POSITION)]); + setCornerOrientation(RC5_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(UC3_POSITION)]); + setCornerOrientation(UC3_POSITION, CORNER_ORIENTATION_CW[old_rc1_or]); + setEdgeOrientation(UE5_POSITION, getEdgeOrientation(RE4_POSITION)); + setEdgeOrientation(RE4_POSITION, getEdgeOrientation(RE2_POSITION)); + setEdgeOrientation(RE2_POSITION, getEdgeOrientation(FE2_POSITION)); + setEdgeOrientation(FE2_POSITION, getEdgeOrientation(RE3_POSITION)); + setEdgeOrientation(RE3_POSITION, old_ue5_or); + if (sKeepMoves) fMoves[fDepth] = R2_MOVE; + fDepth++; + fLastMove = R2_MOVE; + } + + public void moveR2i() { + byte old_rc1_pos = fCornerPositions[RC1_POSITION]; + byte old_eu5_pos = fEdgePositions[UE5_POSITION]; + int old_rc1_or = getCornerOrientation(RC1_POSITION); + int old_ue5_or = getEdgeOrientation(UE5_POSITION); + fCornerPositions[RC1_POSITION] = fCornerPositions[UC3_POSITION]; + fCornerPositions[UC3_POSITION] = fCornerPositions[RC5_POSITION]; + fCornerPositions[RC5_POSITION] = fCornerPositions[FC5_POSITION]; + fCornerPositions[FC5_POSITION] = fCornerPositions[UC2_POSITION]; + fCornerPositions[UC2_POSITION] = old_rc1_pos; + fEdgePositions[UE5_POSITION] = fEdgePositions[RE3_POSITION]; + fEdgePositions[RE3_POSITION] = fEdgePositions[FE2_POSITION]; + fEdgePositions[FE2_POSITION] = fEdgePositions[RE2_POSITION]; + fEdgePositions[RE2_POSITION] = fEdgePositions[RE4_POSITION]; + fEdgePositions[RE4_POSITION] = old_eu5_pos; + setCornerOrientation(RC1_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(UC3_POSITION)]); + setCornerOrientation(UC3_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(RC5_POSITION)]); + setCornerOrientation(RC5_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(FC5_POSITION)]); + setCornerOrientation(FC5_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(UC2_POSITION)]); + setCornerOrientation(UC2_POSITION, CORNER_ORIENTATION_ANTI_CW[old_rc1_or]); + setEdgeOrientation(UE5_POSITION, getEdgeOrientation(RE3_POSITION)); + setEdgeOrientation(RE3_POSITION, getEdgeOrientation(FE2_POSITION)); + setEdgeOrientation(FE2_POSITION, getEdgeOrientation(RE2_POSITION)); + setEdgeOrientation(RE2_POSITION, getEdgeOrientation(RE4_POSITION)); + setEdgeOrientation(RE4_POSITION, old_ue5_or); + if (sKeepMoves) fMoves[fDepth] = R2i_MOVE; + fDepth++; + fLastMove = R2i_MOVE; + } + + public void moveF() { + byte old_fc1_pos = fCornerPositions[FC1_POSITION]; + byte old_ue1_pos = fEdgePositions[UE1_POSITION]; + int old_fc1_or = getCornerOrientation(FC1_POSITION); + int old_ue1_or = getEdgeOrientation(UE1_POSITION); + fCornerPositions[FC1_POSITION] = fCornerPositions[FC5_POSITION]; + fCornerPositions[FC5_POSITION] = fCornerPositions[UC3_POSITION]; + fCornerPositions[UC3_POSITION] = fCornerPositions[UC4_POSITION]; + fCornerPositions[UC4_POSITION] = fCornerPositions[FC2_POSITION]; + fCornerPositions[FC2_POSITION] = old_fc1_pos; + fEdgePositions[UE1_POSITION] = fEdgePositions[FE5_POSITION]; + fEdgePositions[FE5_POSITION] = fEdgePositions[FE4_POSITION]; + fEdgePositions[FE4_POSITION] = fEdgePositions[FE3_POSITION]; + fEdgePositions[FE3_POSITION] = fEdgePositions[FE2_POSITION]; + fEdgePositions[FE2_POSITION] = old_ue1_pos; + setCornerOrientation(FC1_POSITION, getCornerOrientation(FC5_POSITION)); + setCornerOrientation(FC5_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(UC3_POSITION)]); + setCornerOrientation(UC3_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(UC4_POSITION)]); + setCornerOrientation(UC4_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(FC2_POSITION)]); + setCornerOrientation(FC2_POSITION, old_fc1_or); + setEdgeOrientation(UE1_POSITION, (~getEdgeOrientation(FE5_POSITION)) & 1); + setEdgeOrientation(FE5_POSITION, getEdgeOrientation(FE4_POSITION)); + setEdgeOrientation(FE4_POSITION, getEdgeOrientation(FE3_POSITION)); + setEdgeOrientation(FE3_POSITION, getEdgeOrientation(FE2_POSITION)); + setEdgeOrientation(FE2_POSITION, (~old_ue1_or) & 1); + if (sKeepMoves) fMoves[fDepth] = F_MOVE; + fDepth++; + fLastMove = F_MOVE; + } + + public void moveFi() { + byte old_fc1_pos = fCornerPositions[FC1_POSITION]; + byte old_ue1_pos = fEdgePositions[UE1_POSITION]; + int old_fc1_or = getCornerOrientation(FC1_POSITION); + int old_ue1_or = getEdgeOrientation(UE1_POSITION); + fCornerPositions[FC1_POSITION] = fCornerPositions[FC2_POSITION]; + fCornerPositions[FC2_POSITION] = fCornerPositions[UC4_POSITION]; + fCornerPositions[UC4_POSITION] = fCornerPositions[UC3_POSITION]; + fCornerPositions[UC3_POSITION] = fCornerPositions[FC5_POSITION]; + fCornerPositions[FC5_POSITION] = old_fc1_pos; + fEdgePositions[UE1_POSITION] = fEdgePositions[FE2_POSITION]; + fEdgePositions[FE2_POSITION] = fEdgePositions[FE3_POSITION]; + fEdgePositions[FE3_POSITION] = fEdgePositions[FE4_POSITION]; + fEdgePositions[FE4_POSITION] = fEdgePositions[FE5_POSITION]; + fEdgePositions[FE5_POSITION] = old_ue1_pos; + setCornerOrientation(FC1_POSITION, getCornerOrientation(FC2_POSITION)); + setCornerOrientation(FC2_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(UC4_POSITION)]); + setCornerOrientation(UC4_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(UC3_POSITION)]); + setCornerOrientation(UC3_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(FC5_POSITION)]); + setCornerOrientation(FC5_POSITION, old_fc1_or); + setEdgeOrientation(UE1_POSITION, (~getEdgeOrientation(FE2_POSITION)) & 1); + setEdgeOrientation(FE2_POSITION, getEdgeOrientation(FE3_POSITION)); + setEdgeOrientation(FE3_POSITION, getEdgeOrientation(FE4_POSITION)); + setEdgeOrientation(FE4_POSITION, getEdgeOrientation(FE5_POSITION)); + setEdgeOrientation(FE5_POSITION, (~old_ue1_or) & 1); + if (sKeepMoves) fMoves[fDepth] = Fi_MOVE; + fDepth++; + fLastMove = Fi_MOVE; + } + + public void moveF2() { + byte old_fc1_pos = fCornerPositions[FC1_POSITION]; + byte old_ue1_pos = fEdgePositions[UE1_POSITION]; + int old_fc1_or = getCornerOrientation(FC1_POSITION); + int old_ue1_or = getEdgeOrientation(UE1_POSITION); + fCornerPositions[FC1_POSITION] = fCornerPositions[UC3_POSITION]; + fCornerPositions[UC3_POSITION] = fCornerPositions[FC2_POSITION]; + fCornerPositions[FC2_POSITION] = fCornerPositions[FC5_POSITION]; + fCornerPositions[FC5_POSITION] = fCornerPositions[UC4_POSITION]; + fCornerPositions[UC4_POSITION] = old_fc1_pos; + fEdgePositions[UE1_POSITION] = fEdgePositions[FE4_POSITION]; + fEdgePositions[FE4_POSITION] = fEdgePositions[FE2_POSITION]; + fEdgePositions[FE2_POSITION] = fEdgePositions[FE5_POSITION]; + fEdgePositions[FE5_POSITION] = fEdgePositions[FE3_POSITION]; + fEdgePositions[FE3_POSITION] = old_ue1_pos; + setCornerOrientation(FC1_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(UC3_POSITION)]); + setCornerOrientation(UC3_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(FC2_POSITION)]); + setCornerOrientation(FC2_POSITION, getCornerOrientation(FC5_POSITION)); + setCornerOrientation(FC5_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(UC4_POSITION)]); + setCornerOrientation(UC4_POSITION, CORNER_ORIENTATION_CW[old_fc1_or]); + setEdgeOrientation(UE1_POSITION, (~getEdgeOrientation(FE4_POSITION)) & 1); + setEdgeOrientation(FE4_POSITION, getEdgeOrientation(FE2_POSITION)); + setEdgeOrientation(FE2_POSITION, getEdgeOrientation(FE5_POSITION)); + setEdgeOrientation(FE5_POSITION, getEdgeOrientation(FE3_POSITION)); + setEdgeOrientation(FE3_POSITION, (~old_ue1_or) & 1); + if (sKeepMoves) fMoves[fDepth] = F2_MOVE; + fDepth++; + fLastMove = F2_MOVE; + } + + public void moveF2i() { + byte old_fc1_pos = fCornerPositions[FC1_POSITION]; + byte old_ue1_pos = fEdgePositions[UE1_POSITION]; + int old_fc1_or = getCornerOrientation(FC1_POSITION); + int old_ue1_or = getEdgeOrientation(UE1_POSITION); + fCornerPositions[FC1_POSITION] = fCornerPositions[UC4_POSITION]; + fCornerPositions[UC4_POSITION] = fCornerPositions[FC5_POSITION]; + fCornerPositions[FC5_POSITION] = fCornerPositions[FC2_POSITION]; + fCornerPositions[FC2_POSITION] = fCornerPositions[UC3_POSITION]; + fCornerPositions[UC3_POSITION] = old_fc1_pos; + fEdgePositions[UE1_POSITION] = fEdgePositions[FE3_POSITION]; + fEdgePositions[FE3_POSITION] = fEdgePositions[FE5_POSITION]; + fEdgePositions[FE5_POSITION] = fEdgePositions[FE2_POSITION]; + fEdgePositions[FE2_POSITION] = fEdgePositions[FE4_POSITION]; + fEdgePositions[FE4_POSITION] = old_ue1_pos; + setCornerOrientation(FC1_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(UC4_POSITION)]); + setCornerOrientation(UC4_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(FC5_POSITION)]); + setCornerOrientation(FC5_POSITION, getCornerOrientation(FC2_POSITION)); + setCornerOrientation(FC2_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(UC3_POSITION)]); + setCornerOrientation(UC3_POSITION, CORNER_ORIENTATION_ANTI_CW[old_fc1_or]); + setEdgeOrientation(UE1_POSITION, (~getEdgeOrientation(FE3_POSITION)) & 1); + setEdgeOrientation(FE3_POSITION, getEdgeOrientation(FE5_POSITION)); + setEdgeOrientation(FE5_POSITION, getEdgeOrientation(FE2_POSITION)); + setEdgeOrientation(FE2_POSITION, getEdgeOrientation(FE4_POSITION)); + setEdgeOrientation(FE4_POSITION, (~old_ue1_or) & 1); + if (sKeepMoves) fMoves[fDepth] = F2i_MOVE; + fDepth++; + fLastMove = F2i_MOVE; + } + + public void moveB() { + byte old_bc1_pos = fCornerPositions[BC1_POSITION]; + byte old_ue3_pos = fEdgePositions[UE3_POSITION]; + int old_bc1_or = getCornerOrientation(BC1_POSITION); + int old_ue3_or = getEdgeOrientation(UE3_POSITION); + fCornerPositions[BC1_POSITION] = fCornerPositions[LC2_POSITION]; + fCornerPositions[LC2_POSITION] = fCornerPositions[UC5_POSITION]; + fCornerPositions[UC5_POSITION] = fCornerPositions[UC1_POSITION]; + fCornerPositions[UC1_POSITION] = fCornerPositions[BC2_POSITION]; + fCornerPositions[BC2_POSITION] = old_bc1_pos; + fEdgePositions[UE3_POSITION] = fEdgePositions[BE5_POSITION]; + fEdgePositions[BE5_POSITION] = fEdgePositions[BE4_POSITION]; + fEdgePositions[BE4_POSITION] = fEdgePositions[BE3_POSITION]; + fEdgePositions[BE3_POSITION] = fEdgePositions[LE5_POSITION]; + fEdgePositions[LE5_POSITION] = old_ue3_pos; + setCornerOrientation(BC1_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(LC2_POSITION)]); + setCornerOrientation(LC2_POSITION, getCornerOrientation(UC5_POSITION)); + setCornerOrientation(UC5_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(UC1_POSITION)]); + setCornerOrientation(UC1_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(BC2_POSITION)]); + setCornerOrientation(BC2_POSITION, old_bc1_or); + setEdgeOrientation(UE3_POSITION, (~getEdgeOrientation(BE5_POSITION)) & 1); + setEdgeOrientation(BE5_POSITION, getEdgeOrientation(BE4_POSITION)); + setEdgeOrientation(BE4_POSITION, getEdgeOrientation(BE3_POSITION)); + setEdgeOrientation(BE3_POSITION, getEdgeOrientation(LE5_POSITION)); + setEdgeOrientation(LE5_POSITION, (~old_ue3_or) & 1); + if (sKeepMoves) fMoves[fDepth] = B_MOVE; + fDepth++; + fLastMove = B_MOVE; + } + + public void moveBi() { + byte old_bc1_pos = fCornerPositions[BC1_POSITION]; + byte old_ue3_pos = fEdgePositions[UE3_POSITION]; + int old_bc1_or = getCornerOrientation(BC1_POSITION); + int old_ue3_or = getEdgeOrientation(UE3_POSITION); + fCornerPositions[BC1_POSITION] = fCornerPositions[BC2_POSITION]; + fCornerPositions[BC2_POSITION] = fCornerPositions[UC1_POSITION]; + fCornerPositions[UC1_POSITION] = fCornerPositions[UC5_POSITION]; + fCornerPositions[UC5_POSITION] = fCornerPositions[LC2_POSITION]; + fCornerPositions[LC2_POSITION] = old_bc1_pos; + fEdgePositions[UE3_POSITION] = fEdgePositions[LE5_POSITION]; + fEdgePositions[LE5_POSITION] = fEdgePositions[BE3_POSITION]; + fEdgePositions[BE3_POSITION] = fEdgePositions[BE4_POSITION]; + fEdgePositions[BE4_POSITION] = fEdgePositions[BE5_POSITION]; + fEdgePositions[BE5_POSITION] = old_ue3_pos; + setCornerOrientation(BC1_POSITION, getCornerOrientation(BC2_POSITION)); + setCornerOrientation(BC2_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(UC1_POSITION)]); + setCornerOrientation(UC1_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(UC5_POSITION)]); + setCornerOrientation(UC5_POSITION, getCornerOrientation(LC2_POSITION)); + setCornerOrientation(LC2_POSITION, CORNER_ORIENTATION_ANTI_CW[old_bc1_or]); + setEdgeOrientation(UE3_POSITION, (~getEdgeOrientation(LE5_POSITION)) & 1); + setEdgeOrientation(LE5_POSITION, getEdgeOrientation(BE3_POSITION)); + setEdgeOrientation(BE3_POSITION, getEdgeOrientation(BE4_POSITION)); + setEdgeOrientation(BE4_POSITION, getEdgeOrientation(BE5_POSITION)); + setEdgeOrientation(BE5_POSITION, (~old_ue3_or) & 1); + if (sKeepMoves) fMoves[fDepth] = Bi_MOVE; + fDepth++; + fLastMove = Bi_MOVE; + } + + public void moveB2() { + byte old_bc1_pos = fCornerPositions[BC1_POSITION]; + byte old_ue3_pos = fEdgePositions[UE3_POSITION]; + int old_bc1_or = getCornerOrientation(BC1_POSITION); + int old_ue3_or = getEdgeOrientation(UE3_POSITION); + fCornerPositions[BC1_POSITION] = fCornerPositions[UC5_POSITION]; + fCornerPositions[UC5_POSITION] = fCornerPositions[BC2_POSITION]; + fCornerPositions[BC2_POSITION] = fCornerPositions[LC2_POSITION]; + fCornerPositions[LC2_POSITION] = fCornerPositions[UC1_POSITION]; + fCornerPositions[UC1_POSITION] = old_bc1_pos; + fEdgePositions[UE3_POSITION] = fEdgePositions[BE4_POSITION]; + fEdgePositions[BE4_POSITION] = fEdgePositions[LE5_POSITION]; + fEdgePositions[LE5_POSITION] = fEdgePositions[BE5_POSITION]; + fEdgePositions[BE5_POSITION] = fEdgePositions[BE3_POSITION]; + fEdgePositions[BE3_POSITION] = old_ue3_pos; + setCornerOrientation(BC1_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(UC5_POSITION)]); + setCornerOrientation(UC5_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(BC2_POSITION)]); + setCornerOrientation(BC2_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(LC2_POSITION)]); + setCornerOrientation(LC2_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(UC1_POSITION)]); + setCornerOrientation(UC1_POSITION, CORNER_ORIENTATION_CW[old_bc1_or]); + setEdgeOrientation(UE3_POSITION, (~getEdgeOrientation(BE4_POSITION)) & 1); + setEdgeOrientation(BE4_POSITION, getEdgeOrientation(LE5_POSITION)); + setEdgeOrientation(LE5_POSITION, getEdgeOrientation(BE5_POSITION)); + setEdgeOrientation(BE5_POSITION, getEdgeOrientation(BE3_POSITION)); + setEdgeOrientation(BE3_POSITION, (~old_ue3_or) & 1); + if (sKeepMoves) fMoves[fDepth] = B2_MOVE; + fDepth++; + fLastMove = B2_MOVE; + } + + public void moveB2i() { + byte old_bc1_pos = fCornerPositions[BC1_POSITION]; + byte old_ue3_pos = fEdgePositions[UE3_POSITION]; + int old_bc1_or = getCornerOrientation(BC1_POSITION); + int old_ue3_or = getEdgeOrientation(UE3_POSITION); + fCornerPositions[BC1_POSITION] = fCornerPositions[UC1_POSITION]; + fCornerPositions[UC1_POSITION] = fCornerPositions[LC2_POSITION]; + fCornerPositions[LC2_POSITION] = fCornerPositions[BC2_POSITION]; + fCornerPositions[BC2_POSITION] = fCornerPositions[UC5_POSITION]; + fCornerPositions[UC5_POSITION] = old_bc1_pos; + fEdgePositions[UE3_POSITION] = fEdgePositions[BE3_POSITION]; + fEdgePositions[BE3_POSITION] = fEdgePositions[BE5_POSITION]; + fEdgePositions[BE5_POSITION] = fEdgePositions[LE5_POSITION]; + fEdgePositions[LE5_POSITION] = fEdgePositions[BE4_POSITION]; + fEdgePositions[BE4_POSITION] = old_ue3_pos; + setCornerOrientation(BC1_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(UC1_POSITION)]); + setCornerOrientation(UC1_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(LC2_POSITION)]); + setCornerOrientation(LC2_POSITION, CORNER_ORIENTATION_ANTI_CW[getCornerOrientation(BC2_POSITION)]); + setCornerOrientation(BC2_POSITION, CORNER_ORIENTATION_CW[getCornerOrientation(UC5_POSITION)]); + setCornerOrientation(UC5_POSITION, CORNER_ORIENTATION_ANTI_CW[old_bc1_or]); + setEdgeOrientation(UE3_POSITION, (~getEdgeOrientation(BE3_POSITION)) & 1); + setEdgeOrientation(BE3_POSITION, getEdgeOrientation(BE5_POSITION)); + setEdgeOrientation(BE5_POSITION, getEdgeOrientation(LE5_POSITION)); + setEdgeOrientation(LE5_POSITION, getEdgeOrientation(BE4_POSITION)); + setEdgeOrientation(BE4_POSITION, (~old_ue3_or) & 1); + if (sKeepMoves) fMoves[fDepth] = B2i_MOVE; + fDepth++; + fLastMove = B2i_MOVE; + } + + public boolean equals(Object aObject) { + LLMinx minx = (LLMinx) aObject; + byte piece; + for (byte i = 0; i < fCornerPositions.length; i++) { + piece = fCornerPositions[i]; + if (fCornerPositions[i] != minx.fCornerPositions[i] && !fIgnoreCornerPositions[piece]) return false; + if (getCornerOrientation(i) != minx.getCornerOrientation(i) && !fIgnoreCornerOrientations[piece]) return false; + } + for (byte i = 0; i < fEdgePositions.length; i++) { + piece = fEdgePositions[i]; + if (fEdgePositions[i] != minx.fEdgePositions[i] && !fIgnoreEdgePositions[piece]) return false; + if (getEdgeOrientation(i) != minx.getEdgeOrientation(i) && !fIgnoreEdgeOrientations[piece]) return false; + } + return true; +// return (this.fEdgeOrientations == minx.fEdgeOrientations) && +// (this.fCornerOrientations == minx.fCornerOrientations); + } + + public LLMinx clone() { + byte[] corner_positions = new byte[fCornerPositions.length]; + boolean[] ignore_corner_positions = new boolean[fIgnoreCornerPositions.length]; + boolean[] ignore_corner_orientations = new boolean[fIgnoreCornerOrientations.length]; + byte[] edge_positions = new byte[fEdgePositions.length]; + boolean[] ignore_edge_positions = new boolean[fIgnoreEdgePositions.length]; + boolean[] ignore_edge_orientations = new boolean[fIgnoreEdgeOrientations.length]; + byte[] moves = new byte[fMoves.length]; + System.arraycopy(fCornerPositions, 0, corner_positions, 0, fCornerPositions.length); + System.arraycopy(fIgnoreCornerPositions, 0, ignore_corner_positions, 0, fIgnoreCornerPositions.length); + System.arraycopy(fIgnoreCornerOrientations, 0, ignore_corner_orientations, 0, fIgnoreCornerOrientations.length); + System.arraycopy(fEdgePositions, 0, edge_positions, 0, fEdgePositions.length); + System.arraycopy(fIgnoreEdgePositions, 0, ignore_edge_positions, 0, fIgnoreEdgePositions.length); + System.arraycopy(fIgnoreEdgeOrientations, 0, ignore_edge_orientations, 0, fIgnoreEdgeOrientations.length); + System.arraycopy(fMoves, 0, moves, 0, fMoves.length); + return new LLMinx( + corner_positions, + edge_positions, + fCornerOrientations, + fEdgeOrientations, + ignore_corner_positions, + ignore_edge_positions, + ignore_corner_orientations, + ignore_edge_orientations, + moves, + fDepth + ); + } + + public int compareTo(LLMinx aObject) { + int diff = fCornerOrientations - aObject.fCornerOrientations; + if (diff != 0) { + return diff; + } + else { + diff = fEdgeOrientations - aObject.fEdgeOrientations; + if (diff != 0) { + for (int i = 0; i < fCornerPositions.length; i++) { + diff = fCornerPositions[i] - aObject.fCornerPositions[i]; + if (diff != 0) { + return diff; + } + } + for (int i = 0; i < fEdgePositions.length; i++) { + diff = fEdgePositions[i] - aObject.fEdgePositions[i]; + if (diff != 0) { + return diff; + } + } + } + } + return 0; + } + +} diff --git a/src/llminx/LLMinxProfiler.java b/src/llminx/LLMinxProfiler.java new file mode 100644 index 0000000..9414210 --- /dev/null +++ b/src/llminx/LLMinxProfiler.java @@ -0,0 +1,146 @@ +package llminx; + +/** + * + */ + +public class LLMinxProfiler { + + private static int MOVE_COUNT = 10000000; + + public static void main(String[] args) { + LLMinx.setMaxDepth(MOVE_COUNT); + LLMinx.setKeepMoves(false ); + LLMinx minx = new LLMinx(); + long time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveF(); + } + System.out.println("Performed " + MOVE_COUNT + " F moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveFi(); + } + System.out.println("Performed " + MOVE_COUNT + " Fi moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveF2(); + } + System.out.println("Performed " + MOVE_COUNT + " F2 moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveF2i(); + } + System.out.println("Performed " + MOVE_COUNT + " F2i moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveR(); + } + System.out.println("Performed " + MOVE_COUNT + " R moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveRi(); + } + System.out.println("Performed " + MOVE_COUNT + " Ri moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveR2(); + } + System.out.println("Performed " + MOVE_COUNT + " R2 moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveR2i(); + } + System.out.println("Performed " + MOVE_COUNT + " R2i moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveU(); + } + System.out.println("Performed " + MOVE_COUNT + " U moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveUi(); + } + System.out.println("Performed " + MOVE_COUNT + " Ui moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveU2(); + } + System.out.println("Performed " + MOVE_COUNT + " U2 moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveU2i(); + } + System.out.println("Performed " + MOVE_COUNT + " U2i moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveL(); + } + System.out.println("Performed " + MOVE_COUNT + " L moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveLi(); + } + System.out.println("Performed " + MOVE_COUNT + " Li moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveL2(); + } + System.out.println("Performed " + MOVE_COUNT + " L2 moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveL2i(); + } + System.out.println("Performed " + MOVE_COUNT + " L2i moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveB(); + } + System.out.println("Performed " + MOVE_COUNT + " B moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveBi(); + } + System.out.println("Performed " + MOVE_COUNT + " Bi moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveB2(); + } + System.out.println("Performed " + MOVE_COUNT + " B2 moves in " + (System.currentTimeMillis() - time) + "ms"); + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int i = 0; i < MOVE_COUNT; i++) { + minx.moveB2i(); + } + System.out.println("Performed " + MOVE_COUNT + " B2i moves in " + (System.currentTimeMillis() - time) + "ms"); + + for (byte i = 0; i < LLMinx.MOVE_STRINGS.length; i++) { + minx = new LLMinx(); + time = System.currentTimeMillis(); + for (int move = 0; move < MOVE_COUNT; move++) { + minx.move(i); + } + System.out.println("Performed " + MOVE_COUNT + " " + LLMinx.MOVE_STRINGS[i] + " moves using switch in " + (System.currentTimeMillis() - time) + "ms"); + } + + } + +} diff --git a/src/llminx/LLMinxTest.java b/src/llminx/LLMinxTest.java new file mode 100644 index 0000000..b3ff9f0 --- /dev/null +++ b/src/llminx/LLMinxTest.java @@ -0,0 +1,194 @@ +package llminx; + +/** + * + */ + +public class LLMinxTest { + + public static void main(String[] args) { + int move_count = 5; + LLMinx solved = new LLMinx(); + LLMinx minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveF(); + } + System.out.println("Move F implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveFi(); + } + System.out.println("Move Fi implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveF2(); + } + System.out.println("Move F2 implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveF2i(); + } + System.out.println("Move F2i implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveR(); + } + System.out.println("Move R implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveRi(); + } + System.out.println("Move Ri implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveR2(); + } + System.out.println("Move R2 implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveR2i(); + } + System.out.println("Move R2i implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveU(); + } + System.out.println("Move U implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveUi(); + } + System.out.println("Move Ui implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveU2(); + } + System.out.println("Move U2 implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveU2i(); + } + System.out.println("Move U2i implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveL(); + } + System.out.println("Move L implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveLi(); + } + System.out.println("Move Li implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveL2(); + } + System.out.println("Move L2 implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveL2i(); + } + System.out.println("Move L2i implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveB(); + } + System.out.println("Move B implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveBi(); + } + System.out.println("Move Bi implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveB2(); + } + System.out.println("Move B2 implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + for (int i = 0; i < move_count; i++) { + minx.moveB2i(); + } + System.out.println("Move B2i implemented " + (solved.equals(minx) ? "correctly." : "wrongly.")); + minx = new LLMinx(); + minx.moveF(); + minx.moveFi(); + System.out.println("Move F and Fi " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveFi(); + minx.moveF(); + System.out.println("Move Fi and F " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveF2(); + minx.moveF2i(); + System.out.println("Move F2 and F2i " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveF2i(); + minx.moveF2(); + System.out.println("Move F2i and F2 " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveR(); + minx.moveRi(); + System.out.println("Move R and Ri " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveRi(); + minx.moveR(); + System.out.println("Move Ri and R " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveR2(); + minx.moveR2i(); + System.out.println("Move R2 and R2i " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveR2i(); + minx.moveR2(); + System.out.println("Move R2i and R2 " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveL(); + minx.moveLi(); + System.out.println("Move L and Li " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveLi(); + minx.moveL(); + System.out.println("Move Li and L " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveL2(); + minx.moveL2i(); + System.out.println("Move L2 and L2i " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveL2i(); + minx.moveL2(); + System.out.println("Move L2i and L2 " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveU(); + minx.moveUi(); + System.out.println("Move U and Ui " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveUi(); + minx.moveU(); + System.out.println("Move Ui and U " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveU2(); + minx.moveU2i(); + System.out.println("Move U2 and U2i " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveU2i(); + minx.moveU2(); + System.out.println("Move U2i and U2 " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveB(); + minx.moveBi(); + System.out.println("Move B and Bi " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveBi(); + minx.moveB(); + System.out.println("Move Bi and B " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveB2(); + minx.moveB2i(); + System.out.println("Move B2 and B2i " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + minx = new LLMinx(); + minx.moveB2i(); + minx.moveB2(); + System.out.println("Move B2i and B2 " + (solved.equals(minx) ? "cancel out." : "don't cancel out.")); + } + +} diff --git a/src/llminx/gui/LLMinxCenterSticker.java b/src/llminx/gui/LLMinxCenterSticker.java new file mode 100644 index 0000000..d3901ca --- /dev/null +++ b/src/llminx/gui/LLMinxCenterSticker.java @@ -0,0 +1,32 @@ +package llminx.gui; + +import llminx.LLMinx; + +import java.awt.*; + +/** + * + */ +public class LLMinxCenterSticker extends LLMinxSticker { + + public LLMinxCenterSticker(LLMinxCubie cubie, byte orientation) { + super(cubie, orientation); + } + + public void paint(Graphics aGraphics, boolean aSelected, LLMinx aMinx) { + applyFillStyle(aGraphics,0); + aGraphics.fillPolygon(this); + applyLineStyle(aGraphics, false); + aGraphics.drawPolygon(this); + } + + public boolean interactsWith(LLMinxSticker aCubie, LLMinx aMinx) { + return false; + } + + public void paintInteraction(Graphics aGraphics, LLMinxSticker aCubie, LLMinx aMinx) { + } + + public void performInteraction(LLMinxSticker aCubie, LLMinx aMinx) { + } +} diff --git a/src/llminx/gui/LLMinxCornerSticker.java b/src/llminx/gui/LLMinxCornerSticker.java new file mode 100644 index 0000000..10f3147 --- /dev/null +++ b/src/llminx/gui/LLMinxCornerSticker.java @@ -0,0 +1,100 @@ +package llminx.gui; + +import llminx.LLMinx; + +import java.awt.*; + +/** + * + */ +public class LLMinxCornerSticker extends LLMinxSticker { + + private static final int[][] COLORS = new int[][]{ + {0, 3, 4}, + {0, 4, 5}, + {0, 5, 1}, + {0, 1, 2}, + {0, 2, 3}, + }; + + public LLMinxCornerSticker(LLMinxCubie cubie, byte orientation) { + super(cubie, orientation); + } + + public void paint(Graphics aGraphics, boolean aSelected, LLMinx aMinx) { + if (aSelected) { + applyHighlightStyle(aGraphics); + aGraphics.fillPolygon(getCubie()); + applyLineStyle(aGraphics, aSelected); + aGraphics.drawPolygon(getCubie()); + } + else { + byte corner_cubie = getCubie().getCubie(); + int orientation = (getOrientation() - aMinx.getCornerOrientation(corner_cubie) + 3) % 3; + if (((orientation != 0 || aMinx.getIgnoreCornerOrientations()[corner_cubie]) && aMinx.getIgnoreCornerPositions()[corner_cubie])) { + applyFillStyle(aGraphics, -1); + } + else { + if (aMinx.getIgnoreCornerOrientations()[corner_cubie]) { + applyFillStyle(aGraphics, + COLORS[aMinx.getCornerPositions()[corner_cubie]][0], + COLORS[aMinx.getCornerPositions()[corner_cubie]][1], + COLORS[aMinx.getCornerPositions()[corner_cubie]][2]); + } + else { + applyFillStyle(aGraphics, COLORS[aMinx.getCornerPositions()[corner_cubie]][orientation]); + } + } + aGraphics.fillPolygon(this); + applyLineStyle(aGraphics, aSelected); + aGraphics.drawPolygon(this); + } + } + + public boolean interactsWith(LLMinxSticker sticker, LLMinx aMinx) { + return sticker instanceof LLMinxCornerSticker && sticker != this; + } + + public void paintInteraction(Graphics aGraphics, LLMinxSticker aSticker, LLMinx aMinx) { + if (aSticker.getCubie() == getCubie()) { + Point start = new Point(aSticker.getCenterX(), aSticker.getCenterY()); + Point end = new Point(getCenterX(), getCenterY()); + applyLineStyleIneraction(aGraphics); + aGraphics.drawLine(start.x, start.y, end.x, end.y); + applyLineStyle(aGraphics, true); + paintArrow(aGraphics, start, end, 10); + } + else { + applyHighlightStyle(aGraphics); + aGraphics.fillPolygon(getCubie()); + applyLineStyleIneraction(aGraphics); + aGraphics.drawPolygon(getCubie()); + Point start = new Point((int) aSticker.getCubie().getBounds().getCenterX(), + (int) aSticker.getCubie().getBounds().getCenterY()); + Point end = new Point((int) getCubie().getBounds().getCenterX(), + (int) getCubie().getBounds().getCenterY()); + aGraphics.drawLine(start.x, start.y, end.x, end.y); + applyLineStyle(aGraphics, true); + paintArrow(aGraphics, start, end, 10); + paintArrow(aGraphics, end, start, 10); + } + } + + public void performInteraction(LLMinxSticker aSticker, LLMinx aMinx) { + byte target_corner_cubie = getCubie().getCubie(); + if (aSticker.getCubie() == getCubie()) { + int orientation = (getOrientation() - aSticker.getOrientation() + aMinx.getCornerOrientation(target_corner_cubie) + 3) % 3; + aMinx.setCornerOrientation(target_corner_cubie, orientation); + } + else { + byte source_corner_cubie = aSticker.getCubie().getCubie(); + byte[] corners = aMinx.getCornerPositions(); + byte corner = corners[source_corner_cubie]; + corners[source_corner_cubie] = corners[target_corner_cubie]; + corners[target_corner_cubie] = corner; + int orientation = aMinx.getCornerOrientation(target_corner_cubie); + aMinx.setCornerOrientation(target_corner_cubie, aMinx.getCornerOrientation(source_corner_cubie)); + aMinx.setCornerOrientation(source_corner_cubie, orientation); + } + } +} diff --git a/src/llminx/gui/LLMinxCubie.java b/src/llminx/gui/LLMinxCubie.java new file mode 100644 index 0000000..83aa0cf --- /dev/null +++ b/src/llminx/gui/LLMinxCubie.java @@ -0,0 +1,20 @@ +package llminx.gui; + +import java.awt.*; + +/** + * + */ +public class LLMinxCubie extends Polygon { + + private byte fCubie; + + public LLMinxCubie(byte cubie) { + fCubie = cubie; + } + + public byte getCubie() { + return fCubie; + } + +} diff --git a/src/llminx/gui/LLMinxCustomizerPanel.java b/src/llminx/gui/LLMinxCustomizerPanel.java new file mode 100644 index 0000000..30eede3 --- /dev/null +++ b/src/llminx/gui/LLMinxCustomizerPanel.java @@ -0,0 +1,267 @@ +package llminx.gui; + +import llminx.LLMinx; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.util.Collection; +import java.util.Vector; +import java.util.Iterator; + +/** + * + */ +public class LLMinxCustomizerPanel extends JPanel implements ComponentListener { + + private LLMinx fMinx; + private Collection fHotSpots; + private LLMinxSticker fSelection; + private LLMinxSticker fInteraction; + + public LLMinxCustomizerPanel(LLMinx minx) { + fMinx = minx; + fHotSpots = new Vector(); + addComponentListener(this); + addMouseMotionListener(new MouseMotionAdapter() { + public void mouseMoved(MouseEvent mouseEvent) { + if (!isEnabled()) return; + performSelection(mouseEvent); + } + + public void mouseDragged(MouseEvent mouseEvent) { + if (!isEnabled()) return; + LLMinxSticker sticker_under_mouse = getStickerUnderMouse(mouseEvent); + if (fSelection != null && + sticker_under_mouse != null && + sticker_under_mouse.interactsWith(fSelection, fMinx)) { + fInteraction = sticker_under_mouse; + } + else { + fInteraction = null; + } + repaint(); + } + }); + addMouseListener(new MouseAdapter() { + public void mouseReleased(MouseEvent mouseEvent) { + if (!isEnabled()) return; + if (fSelection != null && fInteraction != null) { + fInteraction.performInteraction(fSelection, fMinx); + } + performSelection(mouseEvent); + fInteraction = null; + repaint(); + } + }); + } + + protected void paintComponent(Graphics graphics) { + super.paintComponent(graphics); + if (fHotSpots.isEmpty()) updateShapes(); + Graphics2D g = (Graphics2D) graphics; + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + Iterator hot_spots = fHotSpots.iterator(); + while (hot_spots.hasNext()) { + hot_spots.next().paint(g, false, fMinx); + } + if (fSelection != null) { + fSelection.paint(g, true, fMinx); + if (fInteraction != null) { + fInteraction.paintInteraction(g, fSelection, fMinx); + } + } + } + + private void updateShapes() { + fHotSpots.clear(); + double half_width = getWidth() / 2; + double half_height = getHeight() / 2; + double outer_radius = Math.min(half_height, half_width) - 10; + double middle_radius = 3 * outer_radius / 4; + double inner_radius = outer_radius / 3; + Point2D.Double center = new Point2D.Double(half_width, half_height); + Point2D.Double corner = new Point2D.Double(); + Point2D.Double[] inner_corners = new Point2D.Double[5]; + Point2D.Double[] middle_corners = new Point2D.Double[5]; + Point2D.Double[] outer_corners = new Point2D.Double[5]; + LLMinxCenterSticker center_piece = new LLMinxCenterSticker(null, (byte) 0); + for (int i = 0; i < 5; i++) { + double angle = -Math.PI / 2 + (double) i / 5 * Math.PI * 2; + pointAt(center, inner_radius, angle, corner); + inner_corners[i] = new Point2D.Double(corner.getX(), corner.getY()); + center_piece.addPoint((int) Math.round(corner.getX()), (int) Math.round(corner.getY())); + pointAt(center, middle_radius, angle, corner); + middle_corners[i] = new Point2D.Double(corner.getX(), corner.getY()); + pointAt(center, outer_radius, angle, corner); + outer_corners[i] = new Point2D.Double(corner.getX(), corner.getY()); + } + int previous_corner, next_corner; + Line2D.Double inner_line = new Line2D.Double(); + Line2D.Double outer_line = new Line2D.Double(); + Point2D.Double intersection = new Point2D.Double(); + Point2D.Double[] middle_edges_left = new Point2D.Double[5]; + Point2D.Double[] middle_edges_right = new Point2D.Double[5]; + for (int i = 0; i < 5; i++) { + previous_corner = (i + 4) % 5; + next_corner = (i + 1) % 5; + inner_line.setLine(inner_corners[previous_corner], inner_corners[i]); + outer_line.setLine(middle_corners[i], middle_corners[next_corner]); + getLineLineIntersection(inner_line, outer_line, intersection); + middle_edges_right[i] = new Point2D.Double(intersection.getX(), intersection.getY()); + inner_line.setLine(inner_corners[i], inner_corners[next_corner]); + outer_line.setLine(middle_corners[previous_corner], middle_corners[i]); + getLineLineIntersection(inner_line, outer_line, intersection); + middle_edges_left[previous_corner] = new Point2D.Double(intersection.getX(), intersection.getY()); + } + for (int i = 0; i < 5; i++) { + previous_corner = (i + 4) % 5; + next_corner = (i + 1) % 5; + double fraction = middle_edges_left[previous_corner].distance(inner_corners[i]) / middle_edges_left[previous_corner].distance(middle_edges_right[next_corner]); + Point2D.Double left_outer_corner = lerp(outer_corners[i], outer_corners[next_corner], fraction, new Point2D.Double()); + Point2D.Double right_outer_corner = lerp(outer_corners[i], outer_corners[previous_corner], fraction, new Point2D.Double()); + Point2D.Double left_outer_edge = lerp(outer_corners[next_corner], outer_corners[i], fraction, new Point2D.Double()); + + // edge cubie. + LLMinxCubie edge_cubie = new LLMinxCubie((byte) ((i + 3) % 5)); + edge_cubie.addPoint((int) Math.round(inner_corners[i].getX()), (int) Math.round(inner_corners[i].getY())); + edge_cubie.addPoint((int) Math.round(inner_corners[next_corner].getX()), (int) Math.round(inner_corners[next_corner].getY())); + edge_cubie.addPoint((int) Math.round(middle_edges_left[i].getX()), (int) Math.round(middle_edges_left[i].getY())); + edge_cubie.addPoint((int) Math.round(left_outer_edge.getX()), (int) Math.round(left_outer_edge.getY())); + edge_cubie.addPoint((int) Math.round(left_outer_corner.getX()), (int) Math.round(left_outer_corner.getY())); + edge_cubie.addPoint((int) Math.round(middle_edges_right[i].getX()), (int) Math.round(middle_edges_right[i].getY())); + + // top edge sticker. + LLMinxEdgeSticker edge_sticker = new LLMinxEdgeSticker(edge_cubie, (byte) 0); + edge_sticker.addPoint((int) Math.round(inner_corners[i].getX()), (int) Math.round(inner_corners[i].getY())); + edge_sticker.addPoint((int) Math.round(inner_corners[next_corner].getX()), (int) Math.round(inner_corners[next_corner].getY())); + edge_sticker.addPoint((int) Math.round(middle_edges_left[i].getX()), (int) Math.round(middle_edges_left[i].getY())); + edge_sticker.addPoint((int) Math.round(middle_edges_right[i].getX()), (int) Math.round(middle_edges_right[i].getY())); + fHotSpots.add(edge_sticker); + + // bottom edge sticker. + edge_sticker = new LLMinxEdgeSticker(edge_cubie, (byte) 1); + edge_sticker.addPoint((int) Math.round(left_outer_corner.getX()), (int) Math.round(left_outer_corner.getY())); + edge_sticker.addPoint((int) Math.round(middle_edges_right[i].getX()), (int) Math.round(middle_edges_right[i].getY())); + edge_sticker.addPoint((int) Math.round(middle_edges_left[i].getX()), (int) Math.round(middle_edges_left[i].getY())); + edge_sticker.addPoint((int) Math.round(left_outer_edge.getX()), (int) Math.round(left_outer_edge.getY())); + fHotSpots.add(edge_sticker); + + // corner cubie. + LLMinxCubie corner_cubie = new LLMinxCubie((byte) i); + corner_cubie.addPoint((int) Math.round(inner_corners[i].getX()), (int) Math.round(inner_corners[i].getY())); + corner_cubie.addPoint((int) Math.round(middle_edges_right[i].getX()), (int) Math.round(middle_edges_right[i].getY())); + corner_cubie.addPoint((int) Math.round(left_outer_corner.getX()), (int) Math.round(left_outer_corner.getY())); + corner_cubie.addPoint((int) Math.round(outer_corners[i].getX()), (int) Math.round(outer_corners[i].getY())); + corner_cubie.addPoint((int) Math.round(right_outer_corner.getX()), (int) Math.round(right_outer_corner.getY())); + corner_cubie.addPoint((int) Math.round(middle_edges_left[previous_corner].getX()), (int) Math.round(middle_edges_left[previous_corner].getY())); + + // top corner sticker. + LLMinxCornerSticker corner_sticker = new LLMinxCornerSticker(corner_cubie, LLMinx.NEUTRAL_ORIENTATION); + corner_sticker.addPoint((int) Math.round(inner_corners[i].getX()), (int) Math.round(inner_corners[i].getY())); + corner_sticker.addPoint((int) Math.round(middle_edges_left[previous_corner].getX()), (int) Math.round(middle_edges_left[previous_corner].getY())); + corner_sticker.addPoint((int) Math.round(middle_corners[i].getX()), (int) Math.round(middle_corners[i].getY())); + corner_sticker.addPoint((int) Math.round(middle_edges_right[i].getX()), (int) Math.round(middle_edges_right[i].getY())); + fHotSpots.add(corner_sticker); + + // left corner sticker. + corner_sticker = new LLMinxCornerSticker(corner_cubie, LLMinx.NEGATIVE_ORIENTATION); + corner_sticker.addPoint((int) Math.round(middle_corners[i].getX()), (int) Math.round(middle_corners[i].getY())); + corner_sticker.addPoint((int) Math.round(middle_edges_right[i].getX()), (int) Math.round(middle_edges_right[i].getY())); + corner_sticker.addPoint((int) Math.round(left_outer_corner.getX()), (int) Math.round(left_outer_corner.getY())); + corner_sticker.addPoint((int) Math.round(outer_corners[i].getX()), (int) Math.round(outer_corners[i].getY())); + fHotSpots.add(corner_sticker); + + // right corner sticker. + corner_sticker = new LLMinxCornerSticker(corner_cubie, LLMinx.POSITIVE_ORIENTATION); + corner_sticker.addPoint((int) Math.round(middle_corners[i].getX()), (int) Math.round(middle_corners[i].getY())); + corner_sticker.addPoint((int) Math.round(middle_edges_left[previous_corner].getX()), (int) Math.round(middle_edges_left[previous_corner].getY())); + corner_sticker.addPoint((int) Math.round(right_outer_corner.getX()), (int) Math.round(right_outer_corner.getY())); + corner_sticker.addPoint((int) Math.round(outer_corners[i].getX()), (int) Math.round(outer_corners[i].getY())); + fHotSpots.add(corner_sticker); + } + fHotSpots.add(center_piece); + } + + private void invalidateShapes() { + fHotSpots.clear(); + } + + private LLMinxSticker getStickerUnderMouse(MouseEvent mouseEvent) { + Iterator hot_spots = fHotSpots.iterator(); + LLMinxSticker sticker_under_mouse = null; + while (hot_spots.hasNext() && sticker_under_mouse == null) { + LLMinxSticker hot_spot = hot_spots.next(); + if (hot_spot.contains(mouseEvent.getPoint())) { + sticker_under_mouse = hot_spot; + } + } + return sticker_under_mouse; + } + + private void performSelection(MouseEvent mouseEvent) { + fSelection = getStickerUnderMouse(mouseEvent); + repaint(); + } + + private Point2D.Double pointAt(Point2D.Double aPoint, double aDistance, double aAngle, Point2D.Double aResult) { + double x = aPoint.getX() + aDistance * Math.cos(aAngle); + double y = aPoint.getY() + aDistance * Math.sin(aAngle); + aResult.setLocation(x, y); + return aResult; + } + + private Point2D.Double lerp(Point2D.Double aPointOne, Point2D.Double aPointTwo, double aFraction, Point2D.Double aResult) { + double x = aPointOne.getX() * (1 - aFraction) + aPointTwo.getX() * aFraction; + double y = aPointOne.getY() * (1 - aFraction) + aPointTwo.getY() * aFraction; + aResult.setLocation(x, y); + return aResult; + } + + static boolean getLineLineIntersection(Line2D.Double l1, + Line2D.Double l2, + Point2D.Double intersection) { + double x1 = l1.getX1(), y1 = l1.getY1(), + x2 = l1.getX2(), y2 = l1.getY2(), + x3 = l2.getX1(), y3 = l2.getY1(), + x4 = l2.getX2(), y4 = l2.getY2(); + + intersection.x = det(det(x1, y1, x2, y2), x1 - x2, + det(x3, y3, x4, y4), x3 - x4) / + det(x1 - x2, y1 - y2, x3 - x4, y3 - y4); + intersection.y = det(det(x1, y1, x2, y2), y1 - y2, + det(x3, y3, x4, y4), y3 - y4) / + det(x1 - x2, y1 - y2, x3 - x4, y3 - y4); + + return true; + } + + static double det(double a, double b, double c, double d) { + return a * d - b * c; + } + + public LLMinx getMinx() { + return fMinx; + } + + public void setMinx(LLMinx minx) { + fMinx = minx; + updateShapes(); + repaint(); + } + + public void componentResized(ComponentEvent componentEvent) { + invalidateShapes(); + } + + public void componentMoved(ComponentEvent componentEvent) { + } + + public void componentShown(ComponentEvent componentEvent) { + } + + public void componentHidden(ComponentEvent componentEvent) { + } +} diff --git a/src/llminx/gui/LLMinxEdgeSticker.java b/src/llminx/gui/LLMinxEdgeSticker.java new file mode 100644 index 0000000..659259a --- /dev/null +++ b/src/llminx/gui/LLMinxEdgeSticker.java @@ -0,0 +1,98 @@ +package llminx.gui; + +import llminx.LLMinx; + +import java.awt.*; + +/** + * + */ +public class LLMinxEdgeSticker extends LLMinxSticker { + + private static final int[][] COLORS = new int[][]{ + {0, 1}, + {0, 2}, + {0, 3}, + {0, 4}, + {0, 5}, + }; + + public LLMinxEdgeSticker(LLMinxCubie cubie, byte orientation) { + super(cubie, orientation); + } + + public void paint(Graphics aGraphics, boolean aSelected, LLMinx aMinx) { + if (aSelected) { + applyHighlightStyle(aGraphics); + aGraphics.fillPolygon(getCubie()); + applyLineStyle(aGraphics, aSelected); + aGraphics.drawPolygon(getCubie()); + } + else { + byte edge_cubie = getCubie().getCubie(); + int orientation = Math.abs(getOrientation() - aMinx.getEdgeOrientation(edge_cubie)); + if (((orientation != 0 || aMinx.getIgnoreEdgeOrientations()[edge_cubie]) && aMinx.getIgnoreEdgePositions()[edge_cubie])) { + applyFillStyle(aGraphics, -1); + } + else { + if (aMinx.getIgnoreEdgeOrientations()[edge_cubie]) { + applyFillStyle(aGraphics, COLORS[aMinx.getEdgePositions()[edge_cubie]][0],COLORS[aMinx.getEdgePositions()[edge_cubie]][1]); + } + else { + applyFillStyle(aGraphics, COLORS[aMinx.getEdgePositions()[edge_cubie]][orientation]); + } + } + aGraphics.fillPolygon(this); + applyLineStyle(aGraphics, aSelected); + aGraphics.drawPolygon(this); + } + } + + public boolean interactsWith(LLMinxSticker aSticker, LLMinx aMinx) { + return aSticker instanceof LLMinxEdgeSticker && aSticker != this; + } + + public void paintInteraction(Graphics aGraphics, LLMinxSticker aSticker, LLMinx aMinx) { + if (aSticker.getCubie() == getCubie()) { + Point start = new Point(aSticker.getCenterX(), aSticker.getCenterY()); + Point end = new Point(getCenterX(), getCenterY()); + applyLineStyleIneraction(aGraphics); + aGraphics.drawLine(start.x, start.y, end.x, end.y); + applyLineStyle(aGraphics, true); + paintArrow(aGraphics, start, end, 10); + paintArrow(aGraphics, end, start, 10); + } + else { + applyHighlightStyle(aGraphics); + aGraphics.fillPolygon(getCubie()); + applyLineStyleIneraction(aGraphics); + aGraphics.drawPolygon(getCubie()); + Point start = new Point((int) aSticker.getCubie().getBounds().getCenterX(), + (int) aSticker.getCubie().getBounds().getCenterY()); + Point end = new Point((int) getCubie().getBounds().getCenterX(), + (int) getCubie().getBounds().getCenterY()); + aGraphics.drawLine(start.x, start.y, end.x, end.y); + applyLineStyle(aGraphics, true); + paintArrow(aGraphics, start, end, 10); + paintArrow(aGraphics, end, start, 10); + } + } + + public void performInteraction(LLMinxSticker aSticker, LLMinx aMinx) { + byte target_edge_cubie = getCubie().getCubie(); + if (aSticker.getCubie() == getCubie()) { + int orientation = (getOrientation() - aSticker.getOrientation() + aMinx.getEdgeOrientation(target_edge_cubie) + 2) % 2; + aMinx.setEdgeOrientation(target_edge_cubie, orientation); + } + else { + byte source_edge_cubie = aSticker.getCubie().getCubie(); + byte[] edges = aMinx.getEdgePositions(); + byte edge = edges[target_edge_cubie]; + edges[target_edge_cubie] = edges[source_edge_cubie]; + edges[source_edge_cubie] = edge; + int orientation = aMinx.getEdgeOrientation(target_edge_cubie); + aMinx.setEdgeOrientation(target_edge_cubie, aMinx.getEdgeOrientation(source_edge_cubie)); + aMinx.setEdgeOrientation(source_edge_cubie, orientation); + } + } +} diff --git a/src/llminx/gui/LLMinxSolverMain.java b/src/llminx/gui/LLMinxSolverMain.java new file mode 100644 index 0000000..4bc19ed --- /dev/null +++ b/src/llminx/gui/LLMinxSolverMain.java @@ -0,0 +1,30 @@ +package llminx.gui; + +import llminx.gui.LLMinxSolverMainWindow; +import llminx.solver.LLMinxSolver; + +import javax.swing.*; + +/** + * + */ +public class LLMinxSolverMain { + + public static void main(String[] args) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } + catch (Exception e) { + // We can always try. + } + LLMinxSolverMainWindow main_window = new LLMinxSolverMainWindow(new LLMinxSolver()); + main_window.setVisible(true); + main_window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } + } + ); + } + +} diff --git a/src/llminx/gui/LLMinxSolverMainWindow.java b/src/llminx/gui/LLMinxSolverMainWindow.java new file mode 100644 index 0000000..de791fa --- /dev/null +++ b/src/llminx/gui/LLMinxSolverMainWindow.java @@ -0,0 +1,526 @@ +package llminx.gui; + +import llminx.LLMinx; +import llminx.solver.LLMinxSolver; +import llminx.solver.StatusEvent; +import llminx.solver.StatusEventType; +import llminx.solver.StatusListener; +import llminx.solver.searchmode.LLMinxMetric; +import llminx.solver.searchmode.LLMinxSearchMode; + +import javax.swing.*; +import javax.swing.border.BevelBorder; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import java.awt.*; +import java.awt.event.*; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * + */ +public class LLMinxSolverMainWindow extends JFrame implements StatusListener { + + private LLMinxCustomizerPanel fCustomizerPanel; + private JComboBox fModes; + private JTextArea fTextArea; + private JCheckBox fLimitDepth; + private JSpinner fDepthSpinner; + private JButton fResetButton; + private JButton fSolveCancelButton; + private JLabel fStatusLabel; + private JEditorPane fInfoPane; + private JProgressBar fProgressBar; + private ArrayList fDisabledComponents; + private LLMinxSolver fMinxSolver; + private Timer fStatusTimer; + private boolean fFollowMessages = true; + private boolean fLineAdded = true; + + public LLMinxSolverMainWindow(LLMinxSolver aSolver) throws HeadlessException { + super("Last layer megaminx solver"); + getContentPane().setLayout(new GridBagLayout()); + fDisabledComponents = new ArrayList(); + + fMinxSolver = aSolver; + fMinxSolver.addStatusListener(this); + + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 0; + c.fill = GridBagConstraints.BOTH; + c.gridheight = 4; + fCustomizerPanel = new LLMinxCustomizerPanel(new LLMinx()); + JPanel customizer_panel = new JPanel(new GridLayout(1, 1)); + customizer_panel.add(fCustomizerPanel); + customizer_panel.setPreferredSize(new Dimension(300, 300)); + customizer_panel.setMinimumSize(new Dimension(300, 300)); + customizer_panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Starting position")); + getContentPane().add(customizer_panel, c); + + c.gridx = 1; + c.weightx = 1; + c.gridheight = 1; + c.fill = GridBagConstraints.HORIZONTAL; + c.anchor = GridBagConstraints.NORTH; + getContentPane().add(createModePanel(), c); + + c.gridy++; + getContentPane().add(createMetricPanel(), c); + + c.gridy++; + getContentPane().add(createDepthPanel(), c); + + c.gridy++; + c.fill = GridBagConstraints.BOTH; + getContentPane().add(createIgnorePiecesPanel(), c); + + c.gridwidth = 2; + c.gridx = 0; + c.gridy++; + c.anchor = GridBagConstraints.SOUTHEAST; + c.fill = GridBagConstraints.HORIZONTAL; + getContentPane().add(createButtonPanel(), c); + + c.fill = GridBagConstraints.BOTH; + c.gridy++; + c.weighty = 1; + c.weightx = 1; + getContentPane().add(createMessagePanel(), c); + + c.gridy++; + c.weighty = 0; + c.insets.left = 2; + c.insets.right = 16; + c.insets.top = 1; + c.insets.bottom = 1; + getContentPane().add(createStatusPanel(), c); + + pack(); + + fStatusTimer = new Timer(500, new UpdateStatusListener()); + fStatusTimer.setInitialDelay(2000); + } + + private JPanel createModePanel() { + LLMinxSearchMode[] modes = LLMinxSearchMode.values(); + JPanel panel = new JPanel(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.VERTICAL; + c.anchor = GridBagConstraints.WEST; + c.weightx = 1; + panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Allowed faces")); + fModes = new JComboBox(); + for (int i = 0; i < modes.length; i++) { + LLMinxSearchMode mode = modes[i]; + fModes.addItem(mode); + } + fModes.setSelectedItem(fMinxSolver.getSearchMode()); + fModes.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent aItemEvent) { + if (aItemEvent.getStateChange() == ItemEvent.SELECTED) { + fMinxSolver.setSearchMode((LLMinxSearchMode) aItemEvent.getItem()); + } + } + }); + panel.add(fModes, c); + fModes.setMinimumSize(fModes.getPreferredSize()); + return panel; + } + + private JPanel createDepthPanel() { + JPanel panel = new JPanel(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 0; + c.fill = GridBagConstraints.VERTICAL; + c.anchor = GridBagConstraints.WEST; + fLimitDepth = new JCheckBox("Limit"); + fLimitDepth.setSelected(fMinxSolver.isLimitDepth()); + fLimitDepth.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent aActionEvent) { + fMinxSolver.setLimitDepth(fLimitDepth.isSelected()); + fDepthSpinner.setEnabled(fLimitDepth.isSelected()); + } + }); + panel.add(fLimitDepth, c); + c.gridx++; + c.weightx = 1; + panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Search depth")); + fDepthSpinner = new JSpinner(new SpinnerNumberModel(fMinxSolver.getMaxDepth(), 1, 50, 1)); + fDepthSpinner.setEnabled(fLimitDepth.isSelected()); + fDepthSpinner.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent changeEvent) { + JSpinner spinner = (JSpinner) changeEvent.getSource(); + Integer value = (Integer) spinner.getValue(); + fMinxSolver.setMaxDepth(value.intValue()); + } + }); + panel.add(fDepthSpinner, c); + + return panel; + } + + private JPanel createMetricPanel() { + JPanel panel = new JPanel(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.VERTICAL; + c.anchor = GridBagConstraints.WEST; + c.weightx = 1; + panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Metric")); + JComboBox metric_box = new JComboBox(LLMinxMetric.values()); + metric_box.setSelectedItem(fMinxSolver.getMetric()); + metric_box.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent itemEvent) { + fMinxSolver.setMetric((LLMinxMetric) itemEvent.getItem()); + } + }); + panel.add(metric_box, c); + metric_box.setMinimumSize(metric_box.getPreferredSize()); + fDisabledComponents.add(metric_box); + return panel; + } + + private JPanel createIgnorePiecesPanel() { + JPanel panel = new JPanel(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.gridx = GridBagConstraints.REMAINDER; + c.weightx = 1; + c.fill = GridBagConstraints.HORIZONTAL; + panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Ignore")); + JCheckBox ignore_corner_positions = new JCheckBox(new ToggleIgnoreCornerPositionsAction()); + ignore_corner_positions.setSelected(fMinxSolver.isIgnoreCornerPositions()); + panel.add(ignore_corner_positions, c); + fDisabledComponents.add(ignore_corner_positions); + JCheckBox ignore_edge_positions = new JCheckBox(new ToggleIgnoreEdgePositionsAction()); + ignore_edge_positions.setSelected(fMinxSolver.isIgnoreEdgePositions()); + panel.add(ignore_edge_positions, c); + fDisabledComponents.add(ignore_edge_positions); + JCheckBox ignore_corner_orientations = new JCheckBox(new ToggleIgnoreCornerOrientationsAction()); + ignore_corner_orientations.setSelected(fMinxSolver.isIgnoreCornerOrientations()); + panel.add(ignore_corner_orientations, c); + fDisabledComponents.add(ignore_corner_orientations); + JCheckBox ignore_edge_orientations = new JCheckBox(new ToggleIgnoreEdgeOrientationsAction()); + ignore_edge_orientations.setSelected(fMinxSolver.isIgnoreEdgeOrientations()); + panel.add(ignore_edge_orientations, c); + fDisabledComponents.add(ignore_edge_orientations); + c.weighty = 1; + panel.add(new JLabel(), c); + return panel; + } + + private JPanel createButtonPanel() { + JPanel panel = new JPanel(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.insets.left = 4; + c.insets.right = 2; + c.weightx = 1; + c.fill = GridBagConstraints.HORIZONTAL; + fInfoPane = new JEditorPane("text/html", "Version 1.0. Developed by Lars Vandenbergh"); + fInfoPane.setEditable(false); + fInfoPane.setHighlighter(null); + fInfoPane.setBackground(panel.getBackground()); + fInfoPane.addMouseListener( new CharlieListener()); + fInfoPane.addHyperlinkListener(new HyperlinkListener() { + + public void hyperlinkUpdate(HyperlinkEvent aHyperlinkEvent) { + if (HyperlinkEvent.EventType.ACTIVATED.equals(aHyperlinkEvent.getEventType())) { + try { + Desktop.getDesktop().browse(new URI(aHyperlinkEvent.getURL().toString())); + } + catch (IOException e) { + e.printStackTrace(); + } + catch (URISyntaxException e) { + e.printStackTrace(); + } + } + } + }); + panel.add(fInfoPane, c); + fResetButton = new JButton(new ResetAction()); + c.weightx = 0; + c.insets.left = 0; + panel.add(fResetButton, c); + fSolveCancelButton = new JButton(new SolveAction()); + panel.add(fSolveCancelButton, c); + return panel; + } + + private JPanel createMessagePanel() { + JPanel panel = new JPanel(new GridBagLayout()); + panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Solving status")); + GridBagConstraints c = new GridBagConstraints(); + c.gridx = GridBagConstraints.REMAINDER; + c.weightx = 1; + c.weighty = 1; + c.fill = GridBagConstraints.BOTH; + fTextArea = new JTextArea(); + fTextArea.setEditable(false); + fTextArea.setFont(new Font("Monospaced", Font.PLAIN, 12)); + final JScrollPane text_area = new JScrollPane(fTextArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + fTextArea.addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent aComponentEvent) { + if (fFollowMessages && fLineAdded) { + fLineAdded = false; + text_area.getVerticalScrollBar().setValue(text_area.getVerticalScrollBar().getMaximum()); + } + } + }); + text_area.setPreferredSize(new Dimension(500, 200)); + text_area.setMinimumSize(new Dimension(500, 200)); + text_area.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); + text_area.setBackground(getBackground()); + panel.add(text_area, c); + JCheckBox follow_messages = new JCheckBox(new AbstractAction("Follow messages") { + public void actionPerformed(ActionEvent aActionEvent) { + fFollowMessages = ((JCheckBox) aActionEvent.getSource()).isSelected(); + } + }); + follow_messages.setSelected(fFollowMessages); + c.weighty = 0; + panel.add(follow_messages, c); + return panel; + } + + private JPanel createStatusPanel() { + JPanel panel = new JPanel(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.insets.right = 2; + c.weightx = 1; + c.fill = GridBagConstraints.BOTH; + fStatusLabel = new JLabel(" "); + panel.add(fStatusLabel, c); + c.weightx = 0; + fProgressBar = new JProgressBar(0, 1000); + fProgressBar.setVisible(false); + fProgressBar.setStringPainted(true); + fProgressBar.setMinimumSize(fProgressBar.getPreferredSize()); + panel.add(fProgressBar, c); + fStatusLabel.setPreferredSize(fProgressBar.getPreferredSize()); + return panel; + } + + public void statusEvent(StatusEvent aEvent) { + if (aEvent.getType() == StatusEventType.START_DEPTH || + aEvent.getType() == StatusEventType.END_DEPTH || + aEvent.getType() == StatusEventType.START_BUILDING_TABLE) { + fStatusLabel.setText(aEvent.getMessage()); + updateProgress(); + if (!fStatusTimer.isRunning()) + fStatusTimer.start(); + } + else if (aEvent.getType() == StatusEventType.END_BUILDING_TABLE) { + fProgressBar.setVisible(false); + fStatusTimer.stop(); + fStatusLabel.setText("Initializing search..."); + } + else if (aEvent.getType() == StatusEventType.MESSAGE) { + fTextArea.append(aEvent.getMessage()); + fTextArea.append("\n"); + if (fFollowMessages) fLineAdded = true; + } + } + + private void enableComponents() { + fCustomizerPanel.setEnabled(true); + fModes.setEnabled(true); + fSolveCancelButton.setAction(new SolveAction()); + fSolveCancelButton.setEnabled(true); + fLimitDepth.setEnabled(true); + fDepthSpinner.setEnabled(fLimitDepth.isSelected()); + fResetButton.setEnabled(true); + Iterator disabled_components = fDisabledComponents.iterator(); + while (disabled_components.hasNext()) { + disabled_components.next().setEnabled(true); + } + } + + private void disableComponents() { + fCustomizerPanel.setEnabled(false); + fModes.setEnabled(false); + fSolveCancelButton.setAction(new CancelAction()); + fSolveCancelButton.setEnabled(true); + fLimitDepth.setEnabled(false); + fDepthSpinner.setEnabled(false); + fResetButton.setEnabled(false); + Iterator disabled_components = fDisabledComponents.iterator(); + while (disabled_components.hasNext()) { + disabled_components.next().setEnabled(false); + } + } + + private class ResetAction extends AbstractAction { + + public ResetAction() { + super("Reset"); + } + + public void actionPerformed(ActionEvent actionEvent) { + LLMinx minx = new LLMinx(); + minx.setIgnoreCornerPositions(fCustomizerPanel.getMinx().getIgnoreCornerPositions()); + minx.setIgnoreEdgePositions(fCustomizerPanel.getMinx().getIgnoreEdgePositions()); + minx.setIgnoreCornerOrientations(fCustomizerPanel.getMinx().getIgnoreCornerOrientations()); + minx.setIgnoreEdgeOrientations(fCustomizerPanel.getMinx().getIgnoreEdgeOrientations()); + fCustomizerPanel.setMinx(minx); + } + + } + + private class SolveAction extends AbstractAction { + + public SolveAction() { + super("Solve"); + } + + public void actionPerformed(ActionEvent actionEvent) { + disableComponents(); + fTextArea.setText(""); + fStatusLabel.setText("Initializing search..."); + fMinxSolver.setStart(fCustomizerPanel.getMinx()); + Runnable solve_task = new Runnable() { + public void run() { + fMinxSolver.solve(); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + fProgressBar.setVisible(false); + fStatusLabel.setText("Done"); + fStatusTimer.stop(); + enableComponents(); + } + }); + } + }; + Thread solve_thread = new Thread(solve_task); + solve_thread.start(); + } + + } + + private class CancelAction extends AbstractAction { + + public CancelAction() { + super("Cancel"); + } + + public void actionPerformed(ActionEvent actionEvent) { + fMinxSolver.interupt(); + fSolveCancelButton.setEnabled(false); + } + + } + + private void updateProgress() { + fProgressBar.setValue((int) (fMinxSolver.getProgress() * 1000)); + fProgressBar.setString(((int) (fMinxSolver.getProgress() * 1000) / 10d) + "%"); + } + + private class UpdateStatusListener implements ActionListener { + public void actionPerformed(ActionEvent actionEvent) { + updateProgress(); + fProgressBar.setVisible(true); + } + } + + private class ToggleIgnoreCornerPositionsAction extends AbstractAction { + + + public ToggleIgnoreCornerPositionsAction() { + super("Corner positions"); + } + + public void actionPerformed(ActionEvent aEvent) { + boolean ignore = ((JCheckBox) aEvent.getSource()).isSelected(); + fMinxSolver.setIgnoreCornerPositions(ignore); + LLMinx minx = fCustomizerPanel.getMinx(); + minx.setIgnoreCornerPositions( + new boolean[]{ + ignore, ignore, ignore, ignore, ignore, false, false, false, false, false, false, false, false, false + } + ); + fCustomizerPanel.setMinx(minx); + } + + } + + private class ToggleIgnoreEdgePositionsAction extends AbstractAction { + + + public ToggleIgnoreEdgePositionsAction() { + super("Edge positions"); + } + + public void actionPerformed(ActionEvent aEvent) { + boolean ignore = ((JCheckBox) aEvent.getSource()).isSelected(); + fMinxSolver.setIgnoreEdgePositions(ignore); + LLMinx minx = fCustomizerPanel.getMinx(); + minx.setIgnoreEdgePositions( + new boolean[]{ + ignore, ignore, ignore, ignore, ignore, false, false, false, false, false, false, false, false, false, false, false, false, false + } + ); + fCustomizerPanel.setMinx(minx); + } + + } + + private class ToggleIgnoreCornerOrientationsAction extends AbstractAction { + + + public ToggleIgnoreCornerOrientationsAction() { + super("Corner orientations"); + } + + public void actionPerformed(ActionEvent aEvent) { + boolean ignore = ((JCheckBox) aEvent.getSource()).isSelected(); + fMinxSolver.setIgnoreCornerOrientations(ignore); + LLMinx minx = fCustomizerPanel.getMinx(); + minx.setIgnoreCornerOrientations( + new boolean[]{ + ignore, ignore, ignore, ignore, ignore, false, false, false, false, false, false, false, false, false + } + ); + fCustomizerPanel.setMinx(minx); + } + + } + + private class ToggleIgnoreEdgeOrientationsAction extends AbstractAction { + + + public ToggleIgnoreEdgeOrientationsAction() { + super("Edge orientations"); + } + + public void actionPerformed(ActionEvent aEvent) { + boolean ignore = ((JCheckBox) aEvent.getSource()).isSelected(); + fMinxSolver.setIgnoreEdgeOrientations(ignore); + LLMinx minx = fCustomizerPanel.getMinx(); + minx.setIgnoreEdgeOrientations( + new boolean[]{ + ignore, ignore, ignore, ignore, ignore, false, false, false, false, false, false, false, false, false, false, false, false, false + } + ); + fCustomizerPanel.setMinx(minx); + } + + } + + private class CharlieListener extends MouseAdapter{ + + private int fCount; + + public void mouseClicked( MouseEvent e ) { + fCount++; + if(fCount==42){ + fInfoPane.setText( "Charliebananananananananana :)" ); + } + } + } +} diff --git a/src/llminx/gui/LLMinxSticker.java b/src/llminx/gui/LLMinxSticker.java new file mode 100644 index 0000000..73f315d --- /dev/null +++ b/src/llminx/gui/LLMinxSticker.java @@ -0,0 +1,161 @@ +package llminx.gui; + +import llminx.LLMinx; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.awt.image.IndexColorModel; +import java.awt.image.WritableRaster; +import java.awt.geom.AffineTransform; + +/** + * + */ +public abstract class LLMinxSticker extends Polygon { + + private LLMinxCubie fCubie; + private byte fOrientation; + private final static Color[] COLORS = new Color[]{ + new Color(225, 225, 0), + new Color(200, 0, 0), + new Color(225, 100, 0), + new Color(0, 200, 0), + new Color(255, 150, 150), + new Color(0, 0, 150), + }; + protected final static Color IGNORE_COLOR = new Color(128, 128, 128); + protected final static Color fColor = Color.BLACK; + protected final static Color fSelectionColor = new Color(128, 0, 0); + protected final static Color fInteractionColor = new Color(128, 0, 0); + protected final static Color fHighlightColor = new Color(255, 0, 0, 32); + protected final static BasicStroke fStroke = new BasicStroke(1); + protected final static BasicStroke fSelectionStroke = new BasicStroke(3); + protected final static BasicStroke fInteractionStroke = new BasicStroke(3, + BasicStroke.CAP_SQUARE, + BasicStroke.JOIN_MITER, + 10, + new float[]{6, 8}, + 0 + ); + protected static final WritableRaster TWO_COLORS; + protected static final WritableRaster THREE_COLORS; + + static { + int width = 16; + BufferedImage image = new BufferedImage(width*2, width*2, BufferedImage.TYPE_BYTE_INDEXED); + for (int i = 0; i < image.getHeight(); i++) { + for (int j = 0; j < image.getWidth(); j++) { + int s = (i / width) % 2; + s += (j / width) % 2; + image.getRaster().setSample(j, i, 0, s % 2); + } + } + TWO_COLORS = image.getRaster(); + image = new BufferedImage(width*3, width*3, BufferedImage.TYPE_BYTE_INDEXED); + for (int i = 0; i < image.getHeight(); i++) { + for (int j = 0; j < image.getWidth(); j++) { + int s = (i / width) % 3; + s += (j / width) % 3; + image.getRaster().setSample(j, i, 0, s % 3); + } + } + THREE_COLORS = image.getRaster(); + } + + public LLMinxSticker(LLMinxCubie cubie, byte orientation) { + fCubie = cubie; + fOrientation = orientation; + } + + + public LLMinxCubie getCubie() { + return fCubie; + } + + public byte getOrientation() { + return fOrientation; + } + + public int getCenterX() { + int sum = 0; + for (int i = 0; i < xpoints.length; i++) { + sum += xpoints[i]; + } + return sum /= xpoints.length; + } + + public int getCenterY() { + int sum = 0; + for (int i = 0; i < ypoints.length; i++) { + sum += ypoints[i]; + } + return sum /= ypoints.length; + } + + public abstract void paint(Graphics aGraphics, boolean aSelected, LLMinx aMinx); + + public abstract boolean interactsWith(LLMinxSticker aCubie, LLMinx aMinx); + + public abstract void paintInteraction(Graphics aGraphics, LLMinxSticker aCubie, LLMinx aMinx); + + public abstract void performInteraction(LLMinxSticker aCubie, LLMinx aMinx); + + protected void applyLineStyle(Graphics aGraphics, boolean aSelected) { + Graphics2D graphics = (Graphics2D) aGraphics; + if (aSelected) { + graphics.setColor(fSelectionColor); + graphics.setStroke(fSelectionStroke); + } + else { + graphics.setColor(fColor); + graphics.setStroke(fStroke); + } + } + + public void paintArrow(Graphics aGraphics, Point aStart, Point aEnd, int aSize) { + Graphics2D g = (Graphics2D) aGraphics; + AffineTransform transform = g.getTransform(); + double rico = Math.atan2(aEnd.y - aStart.y, aEnd.x - aStart.x); + g.rotate(rico, aEnd.getX(), aEnd.getY()); + g.drawLine(aEnd.x, aEnd.y, aEnd.x - aSize, aEnd.y - aSize / 2); + g.drawLine(aEnd.x, aEnd.y, aEnd.x - aSize, aEnd.y + aSize / 2); + g.setTransform(transform); + } + + protected void applyLineStyleIneraction(Graphics aGraphics) { + Graphics2D graphics = (Graphics2D) aGraphics; + graphics.setColor(fInteractionColor); + graphics.setStroke(fInteractionStroke); + } + + protected void applyFillStyle(Graphics aGraphics, int aColor) { + if (aColor == -1) { + aGraphics.setColor(IGNORE_COLOR); + } + else { + aGraphics.setColor(COLORS[aColor]); + } + } + + protected void applyFillStyle(Graphics aGraphics, int aColorA, int aColorB) { + byte[] reds = new byte[]{(byte) COLORS[aColorA].getRed(), (byte) COLORS[aColorB].getRed()}; + byte[] greens = new byte[]{(byte) COLORS[aColorA].getGreen(), (byte) COLORS[aColorB].getGreen()}; + byte[] blues = new byte[]{(byte) COLORS[aColorA].getBlue(), (byte) COLORS[aColorB].getBlue()}; + IndexColorModel colorModel = new IndexColorModel(1, 2, reds, greens, blues); + ((Graphics2D) aGraphics).setPaint(new TexturePaint(new BufferedImage(colorModel, TWO_COLORS, false, null), TWO_COLORS.getBounds())); + } + + protected void applyFillStyle(Graphics aGraphics, int aColorA, int aColorB, int aColorC) { + byte[] reds = new byte[]{(byte) COLORS[aColorA].getRed(), (byte) COLORS[aColorB].getRed(), (byte) COLORS[aColorC].getRed(), 0}; + byte[] greens = new byte[]{(byte) COLORS[aColorA].getGreen(), (byte) COLORS[aColorB].getGreen(), (byte) COLORS[aColorC].getGreen(), 0}; + byte[] blues = new byte[]{(byte) COLORS[aColorA].getBlue(), (byte) COLORS[aColorB].getBlue(), (byte) COLORS[aColorC].getBlue(), 0}; + IndexColorModel colorModel = new IndexColorModel(2, 4, reds, greens, blues); + ((Graphics2D) aGraphics).setPaint(new TexturePaint(new BufferedImage(colorModel, THREE_COLORS, false, null), THREE_COLORS.getBounds())); + } + + protected void applyHighlightStyle(Graphics aGraphics) { + aGraphics.setColor(fHighlightColor); + } + +} diff --git a/src/llminx/solver/CoordinateUtilTest.java b/src/llminx/solver/CoordinateUtilTest.java new file mode 100644 index 0000000..1a101f2 --- /dev/null +++ b/src/llminx/solver/CoordinateUtilTest.java @@ -0,0 +1,82 @@ +package llminx.solver; + +import llminx.LLMinx; +import util.CoordinateUtil; + +/** + * + */ +public class CoordinateUtilTest { + + public static void main(String[] args) { + + LLMinx start = new LLMinx( + new byte[]{ + LLMinx.UC1_POSITION, + LLMinx.UC2_POSITION, + LLMinx.UC3_POSITION, + LLMinx.UC4_POSITION, + LLMinx.UC5_POSITION, + LLMinx.RC1_POSITION, + LLMinx.RC5_POSITION, + LLMinx.FC5_POSITION, + LLMinx.FC1_POSITION, + LLMinx.FC2_POSITION, + LLMinx.LC1_POSITION, + LLMinx.LC2_POSITION, + LLMinx.BC1_POSITION, + LLMinx.BC2_POSITION + }, + new byte[]{ + LLMinx.UE1_POSITION, + LLMinx.UE2_POSITION, + LLMinx.UE3_POSITION, + LLMinx.UE4_POSITION, + LLMinx.UE5_POSITION, + LLMinx.RE2_POSITION, + LLMinx.RE3_POSITION, + LLMinx.RE4_POSITION, + LLMinx.FE2_POSITION, + LLMinx.FE3_POSITION, + LLMinx.FE4_POSITION, + LLMinx.FE5_POSITION, + LLMinx.LE3_POSITION, + LLMinx.LE4_POSITION, + LLMinx.LE5_POSITION, + LLMinx.BE3_POSITION, + LLMinx.BE4_POSITION, + LLMinx.BE5_POSITION + }, +// (LLMinx.NEGATIVE_ORIENTATION << (LLMinx.UC4_POSITION * 2)) + +// (LLMinx.POSITIVE_ORIENTATION << (LLMinx.FC2_POSITION * 2)), +// (LLMinx.POSITIVE_ORIENTATION << (LLMinx.UC2_POSITION * 2)), +// (LLMinx.IGNORE_ORIENTATION << (LLMinx.UC4_POSITION * 2)) + +// (LLMinx.IGNORE_ORIENTATION << (LLMinx.UC5_POSITION * 2)), + 0, + 0, + new boolean[14], + new boolean[18], + new boolean[14], + new boolean[18], + new byte[25], + (byte) 0 + ); + byte[] cubies = new byte[]{ + LLMinx.UE5_POSITION, + LLMinx.UE4_POSITION, + LLMinx.UE3_POSITION, + }; + int permutation = CoordinateUtil.getPermutationCoordinate(start.getEdgePositions(), cubies); + System.out.println( permutation ); + byte[] edge_positions = start.getEdgePositions(); + for (int i = 0; i < edge_positions.length; i++) { + System.out.print(edge_positions[i] + " "); + } + CoordinateUtil.getPermutation(permutation, edge_positions, cubies); + System.out.println(); + for (int i = 0; i < edge_positions.length; i++) { + System.out.print(edge_positions[i] + " "); + } + System.out.println(); + } +} diff --git a/src/llminx/solver/LLMinxSearchProfiler.java b/src/llminx/solver/LLMinxSearchProfiler.java new file mode 100644 index 0000000..54982ff --- /dev/null +++ b/src/llminx/solver/LLMinxSearchProfiler.java @@ -0,0 +1,183 @@ +package llminx.solver; + +import llminx.LLMinx; + +import java.util.Vector; +import java.util.List; + +/** + * + */ +public class LLMinxSearchProfiler { + + private static final int DEPTH = 5; + + public static void main(String[] args) { + Vector queue = new Vector(); + queue.add(new LLMinx()); + int nodes = 0; + long time = System.currentTimeMillis(); + while (!queue.isEmpty()) { + LLMinx minx = queue.remove(0); + nodes++; + + // add children. + if (minx.getDepth() < DEPTH) addChildren(minx, queue, true); + } + System.out.println("Visit " + nodes + " nodes performed in " + (System.currentTimeMillis() - time) / 1000 + "s"); + + LLMinx start = new LLMinx(); + nodes = 0; + time = System.currentTimeMillis(); + boolean stop = false; + while (!stop) { + nodes++; + stop = nextNode(start); + } + System.out.println("Visit " + nodes + " nodes performed in " + (System.currentTimeMillis() - time) / 1000 + "s"); + } + + private static void addChildren(LLMinx aMinx, List aList, boolean aDepthFirst) { + LLMinx child = aMinx.clone(); + child.moveR(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveRi(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveR2(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveR2i(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveL(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveLi(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveL2(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveL2i(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveU(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveUi(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveU2(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveU2i(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveF(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveFi(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveF2(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveF2i(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveB(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveBi(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveB2(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + child = aMinx.clone(); + child.moveB2i(); + if (aDepthFirst) + aList.add(0, child); + else + aList.add(child); + } + + + private static boolean nextNode(LLMinx aMinx) { + if (aMinx.getDepth() < DEPTH) { + aMinx.move((byte) 0); + return false; + } + else { + while (aMinx.getLastMove() == LLMinx.B2i_MOVE) { + aMinx.undoMove(); + } + if (aMinx.getLastMove() == -1) { + return true; + } + else { + aMinx.move((byte) (aMinx.undoMove() + 1)); + return false; + } + } + } + +} diff --git a/src/llminx/solver/LLMinxSolver.java b/src/llminx/solver/LLMinxSolver.java new file mode 100644 index 0000000..fa8d45d --- /dev/null +++ b/src/llminx/solver/LLMinxSolver.java @@ -0,0 +1,615 @@ +package llminx.solver; + +import llminx.LLMinx; +import llminx.solver.searchmode.LLMinxMetric; +import llminx.solver.searchmode.LLMinxPruner; +import llminx.solver.searchmode.LLMinxSearchMode; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Vector; + +/** + * + */ +public class LLMinxSolver { + + public static boolean sDebug; + private LLMinxSearchMode fSearchMode = LLMinxSearchMode.RU_MODE; + private LLMinxSearchMode fLastSearchMode; + private LLMinxMetric fMetric = LLMinxMetric.FIFTH; + private LLMinxMetric fLastMetric; + private int fMaxDepth = 12; + private boolean fLimitDepth = false; + private int fDepth = 12; + private long fNodes = 0; + private LLMinxPruner fPruner; + private long[] pruned; + private LLMinxPruner[] pruners; + private LLMinxPruner[] fUsedPruners; + private byte[] moves; + private byte[][] next_siblings; + private byte[] first_moves; + private byte[][] tables; + private byte[][] fUsedTables; + private LLMinx fStart; + private Vector fEventListeners; + private boolean fInterupted = false; + private boolean fSearchStarted = false; + private boolean fIgnoreCornerPositions = false; + private boolean fIgnoreEdgePositions = false; + private boolean fIgnoreCornerOrientations = false; + private boolean fIgnoreEdgeOrientations = false; + + static { + sDebug = "on".equals( System.getProperty( "llminx.solver.LLMinxSolver.debug" ) ); + } + + public static void main( String[] args ) { + LLMinx start = new LLMinx( + new byte[]{ + LLMinx.UC1_POSITION, + LLMinx.UC2_POSITION, + LLMinx.UC3_POSITION, + LLMinx.UC4_POSITION, + LLMinx.UC5_POSITION, + LLMinx.RC1_POSITION, + LLMinx.RC5_POSITION, + LLMinx.FC5_POSITION, + LLMinx.FC1_POSITION, + LLMinx.FC2_POSITION, + LLMinx.LC1_POSITION, + LLMinx.LC2_POSITION, + LLMinx.BC1_POSITION, + LLMinx.BC2_POSITION + }, + new byte[]{ + LLMinx.UE1_POSITION, + LLMinx.UE2_POSITION, + LLMinx.UE3_POSITION, + LLMinx.UE4_POSITION, + LLMinx.UE5_POSITION, + LLMinx.RE2_POSITION, + LLMinx.RE3_POSITION, + LLMinx.RE4_POSITION, + LLMinx.FE2_POSITION, + LLMinx.FE3_POSITION, + LLMinx.FE4_POSITION, + LLMinx.FE5_POSITION, + LLMinx.LE3_POSITION, + LLMinx.LE4_POSITION, + LLMinx.LE5_POSITION, + LLMinx.BE3_POSITION, + LLMinx.BE4_POSITION, + LLMinx.BE5_POSITION + }, +// (LLMinx.NEGATIVE_ORIENTATION << (LLMinx.UC4_POSITION * 2)) + +// (LLMinx.POSITIVE_ORIENTATION << (LLMinx.FC2_POSITION * 2)), +// (LLMinx.POSITIVE_ORIENTATION << (LLMinx.UC2_POSITION * 2)), +// (LLMinx.IGNORE_ORIENTATION << (LLMinx.UC4_POSITION * 2)) + +// (LLMinx.IGNORE_ORIENTATION << (LLMinx.UC5_POSITION * 2)), + 0, + 0, + new boolean[14], + new boolean[18], + new boolean[14], + new boolean[18], + new byte[25], + ( byte ) 0 + ); + LLMinxSolver solver = new LLMinxSolver(); + solver.setStart( start ); + solver.setMaxDepth( 16 ); + solver.addStatusListener( new SystemOutStatusListener() ); + solver.solve(); + } + + public LLMinxSolver() { + this( LLMinxSearchMode.RU_MODE, 12 ); + } + + public LLMinxSolver( LLMinxSearchMode aSearchMode, int maxDepth ) { + setSearchMode( aSearchMode ); + fMaxDepth = maxDepth; + fEventListeners = new Vector(); + } + + public LLMinxSearchMode getSearchMode() { + return fSearchMode; + } + + public void setSearchMode( LLMinxSearchMode searchMode ) { + fSearchMode = searchMode; + } + + public LLMinxMetric getMetric() { + return fMetric; + } + + public void setMetric( LLMinxMetric metric ) { + fMetric = metric; + } + + public int getMaxDepth() { + return fMaxDepth; + } + + public void setMaxDepth( int maxDepth ) { + fMaxDepth = maxDepth; + } + + + public boolean isLimitDepth() { + return fLimitDepth; + } + + public void setLimitDepth( boolean aLimitDepth ) { + fLimitDepth = aLimitDepth; + } + + public LLMinx getStart() { + return fStart; + } + + public void setStart( LLMinx start ) { + fStart = start; + } + + public boolean isIgnoreCornerPositions() { + return fIgnoreCornerPositions; + } + + public void setIgnoreCornerPositions( boolean ignoreCornerPositions ) { + fIgnoreCornerPositions = ignoreCornerPositions; + } + + public boolean isIgnoreEdgePositions() { + return fIgnoreEdgePositions; + } + + public void setIgnoreEdgePositions( boolean ignoreEdgePositions ) { + fIgnoreEdgePositions = ignoreEdgePositions; + } + + public boolean isIgnoreCornerOrientations() { + return fIgnoreCornerOrientations; + } + + public void setIgnoreCornerOrientations( boolean aIgnoreCornerOrientations ) { + fIgnoreCornerOrientations = aIgnoreCornerOrientations; + } + + public boolean isIgnoreEdgeOrientations() { + return fIgnoreEdgeOrientations; + } + + public void setIgnoreEdgeOrientations( boolean aIgnoreEdgeOrientations ) { + fIgnoreEdgeOrientations = aIgnoreEdgeOrientations; + } + + public boolean solve() { + final long time = System.currentTimeMillis(); + fSearchStarted = false; + if ( fSearchMode != fLastSearchMode || fMetric != fLastMetric ) { + // init moves table + buildMovesTable(); + + // init pruning table + buildPruningTables(); + + if ( !fInterupted ) { + fLastSearchMode = fSearchMode; + fLastMetric = fMetric; + } + else { + fLastSearchMode = null; + fLastMetric = null; + } + } + filterPruningTables(); + + // start search + if ( !fInterupted ) { + fSearchStarted = true; + fireEvent( new StatusEvent( StatusEventType.MESSAGE, "Searching...", 0 ) ); + LLMinx.setKeepMoves( true ); + int i; + int levels_left; + boolean stop; + LLMinx goal = new LLMinx(); + for ( fDepth = 1; ( !fLimitDepth || fDepth <= fMaxDepth ) && fDepth < pruned.length && !fInterupted; fDepth++ ) { + Arrays.fill( pruned, 0 ); + fNodes = 0; + fireEvent( new StatusEvent( StatusEventType.START_DEPTH, "Searching depth " + fDepth + "...", 0 ) ); + LLMinx minx = fStart.clone(); + stop = false; + while ( !stop && !fInterupted ) { + fNodes++; + levels_left = fDepth - minx.getDepth(); + if ( minx.equals( goal ) ) { + if ( levels_left == 0 && checkOptimal( minx ) ) { + String msg = minx.getGeneratingMoves() + " (" + minx.getHTMLength() + "," + minx.getQTMLength() + ")"; + fireEvent( new StatusEvent( StatusEventType.MESSAGE, msg, 0 ) + ); + } + if ( levels_left > 0 ) { + pruned[levels_left - 1]++; + } + stop = backTrack( minx ); + } + else { + if ( levels_left > 0 ) { + for ( i = 0; i < fUsedPruners.length; i++ ) { + LLMinxPruner pruner = fUsedPruners[i]; + if ( fUsedTables[i][pruner.getCoordinate( minx )] > levels_left ) { + pruned[levels_left - 1]++; + break; + } + } + // add children. + if ( i == fUsedPruners.length ) { + stop = nextNode( minx ); + } + else { + stop = backTrack( minx ); + } + } + else { + stop = nextNode( minx ); + } + } + } + fireEvent( new StatusEvent( StatusEventType.END_DEPTH, "Searching depth " + fDepth + "...", 1 ) ); + } + } + + boolean interupted = fInterupted; + fInterupted = false; + String msg = ( interupted ? "Search interrupted after " : "Search completed in " ) + + ( int ) ( ( System.currentTimeMillis() - time ) / 1000 ) + " seconds."; + fireEvent( new StatusEvent( StatusEventType.MESSAGE, msg, 1 ) ); + return interupted; + } + + public void interupt() { + fInterupted = true; + } + + private void buildMovesTable() { + // filter out moves depending on selected metric. + byte[] possible_moves = fSearchMode.getPossibleMoves(); + if ( fMetric == LLMinxMetric.FACE ) { + moves = possible_moves; + } + else if ( fMetric == LLMinxMetric.FIFTH ) { + moves = new byte[possible_moves.length / 2]; + int moves_left = 0; + for ( int i = 0; i < possible_moves.length; i++ ) { + if ( ( possible_moves[i] % 4 ) < 2 ) { + moves[moves_left++] = possible_moves[i]; + } + } + } + + // intialize tables for first moves and sibling order. + first_moves = new byte[LLMinx.B2i_MOVE + 2]; + next_siblings = new byte[LLMinx.B2i_MOVE + 2][LLMinx.B2i_MOVE + 1]; + int last_sibling; + int last_move; + Arrays.fill( first_moves, moves[0] ); + Arrays.fill( next_siblings[0], ( byte ) -1 ); + + // fill out values for root node. + first_moves[0] = moves[0]; + for ( last_sibling = 0; last_sibling < moves.length - 1; last_sibling++ ) { + next_siblings[0][moves[last_sibling]] = moves[last_sibling + 1]; + } + + // fill out values for internal nodes. + for ( last_move = 0; last_move < moves.length; last_move++ ) { + int last_move_index = moves[last_move] + 1; + + // fill out first moves table. + if ( !isAllowed( moves[last_move], moves[0] ) ) { + int after_first_move = 0; + while ( !isAllowed( moves[last_move], moves[after_first_move] ) ) { + after_first_move++; + } + first_moves[last_move_index] = moves[after_first_move]; + } + + // fill out sibling order table + Arrays.fill( next_siblings[last_move_index], ( byte ) -1 ); + for ( last_sibling = 0; last_sibling < moves.length - 1; last_sibling++ ) { + if ( !isAllowed( moves[last_sibling], moves[last_move] ) ) { + // we don't want two successive turns of the same face. + continue; + } + else if ( !isAllowed( moves[last_sibling + 1], moves[last_move] ) ) { + // we don't want two successive turns of the same face. + int new_last_sibling = last_sibling + 1; + while ( new_last_sibling < moves.length && !isAllowed( moves[new_last_sibling], moves[last_move] ) ) { + new_last_sibling++; + } + if ( new_last_sibling < moves.length ) { + next_siblings[last_move_index][moves[last_sibling]] = moves[new_last_sibling]; + last_sibling = new_last_sibling - 1; + } + else { + break; + } + } + else { + next_siblings[last_move_index][moves[last_sibling]] = moves[last_sibling + 1]; + } + } + } + + if ( sDebug ) { + printMoveTables(); + } + } + + private void printMoveTables() { + System.out.println( "First move" ); + System.out.println(); + System.out.println( "Root: " + LLMinx.MOVE_STRINGS[first_moves[0]] ); + for ( int i = 0; i < moves.length; i++ ) { + byte move = moves[i]; + System.out.println( LLMinx.MOVE_STRINGS[move] + ": " + LLMinx.MOVE_STRINGS[first_moves[move + 1]] ); + } + System.out.println(); + System.out.println( "Siblings" ); + System.out.println(); + System.out.print( " " ); + for ( int i = 0; i < moves.length; i++ ) { + byte move = moves[i]; + System.out.print( LLMinx.MOVE_STRINGS[move] ); + } + System.out.println( "" ); + System.out.print( "Root: " ); + for ( int i = 0; i < moves.length; i++ ) { + byte move = moves[i]; + byte next_sibling = next_siblings[0][move]; + if ( next_sibling == -1 ) { + System.out.print( "-- " ); + } + else { + System.out.print( LLMinx.MOVE_STRINGS[next_sibling] ); + } + } + System.out.println( "" ); + for ( int j = 0; j < moves.length; j++ ) { + byte last_move = moves[j]; + System.out.print( LLMinx.MOVE_STRINGS[last_move] + ": " ); + for ( int i = 0; i < moves.length; i++ ) { + byte move = moves[i]; + byte next_sibling = next_siblings[last_move + 1][move]; + if ( next_sibling == -1 ) { + System.out.print( "-- " ); + } + else { + System.out.print( LLMinx.MOVE_STRINGS[next_sibling] ); + } + } + System.out.println( "" ); + } + } + + private boolean isAllowed( byte aPreviousMove, byte aMove ) { + return fMetric == LLMinxMetric.FIFTH ? + LLMinx.INVERSE_MOVES[aPreviousMove] != aMove : + aPreviousMove / 4 != aMove / 4; + } + + private void buildPruningTables() { + fUsedTables = null; + pruners = fSearchMode.getPruners(); + pruned = new long[Byte.MAX_VALUE + 1]; + tables = new byte[pruners.length][]; + for ( int i = 0; i < pruners.length && !fInterupted; i++ ) { + fireEvent( new StatusEvent( StatusEventType.MESSAGE, "Initialzing pruning table " + pruners[i].getName() + "...", 0 ) ); + Arrays.fill( pruned, 0 ); + fPruner = pruners[i]; + if ( !fPruner.isPrecomputed( fMetric ) ) { + // build pruning table. + fireEvent( new StatusEvent( StatusEventType.MESSAGE, "Building pruning table...", 0 ) ); + LLMinx.setKeepMoves( false ); + tables[i] = buildPruningTable( fPruner ); + if ( fInterupted ) break; + + // writing table to disk. + fireEvent( new StatusEvent( StatusEventType.MESSAGE, "Writing table to disk...", 0 ) ); + fPruner.saveTable( tables[i], fMetric ); + } + else { + // read table from disk. + fireEvent( new StatusEvent( StatusEventType.MESSAGE, "Reading pruning table from disk...", 0 ) ); + tables[i] = fPruner.loadTable( fMetric ); + for ( int j = 0; j < tables[i].length; j++ ) { + pruned[tables[i][j]]++; + } +// for (int j = 0; j < pruned.length && pruned[j] != 0; j++) { +// long l = pruned[j]; +// System.out.println("Depth " + j + ": " + l); +// } + } + } + } + + private byte[] buildPruningTable( LLMinxPruner aPruner ) { + byte[] table = new byte[aPruner.getTableSize()]; + Arrays.fill( table, Byte.MAX_VALUE ); + LLMinx minx = new LLMinx(); + int coordinate = aPruner.getCoordinate( minx ); + table[coordinate] = 0; + fNodes = 1; + int previous_depth_length = 1; + byte depth = 0; + byte next_depth; + boolean forward_search; + fireEvent( new StatusEvent( StatusEventType.START_BUILDING_TABLE, "Building pruning table " + fPruner.getName() + "...", 0 ) ); + while ( previous_depth_length > 0 && !fInterupted ) { + fireEvent( new StatusEvent( StatusEventType.MESSAGE, "Depth " + depth + ": " + previous_depth_length, 0 ) ); + forward_search = previous_depth_length < table.length - fNodes; + previous_depth_length = 0; + next_depth = ( byte ) ( depth + 1 ); + if ( forward_search ) { + // iterate over all table entries from previous depth + for ( int i = 0; i < table.length && !fInterupted; i++ ) { + byte tcoordinate = table[i]; + if ( tcoordinate == depth ) { + // recuperate situation. + aPruner.getMinx( i, minx ); + + // apply all moves on it. + for ( int m = 0; m < moves.length; m++ ) { + minx.move( moves[m] ); + coordinate = aPruner.getCoordinate( minx ); + // if it is a new situation, store depth in table. + if ( table[coordinate] == Byte.MAX_VALUE ) { + table[coordinate] = next_depth; + fNodes++; + previous_depth_length++; + } + minx.undoMove(); + } + } + } + } + else { + // iterate over all empty table entries + for ( int i = 0; i < table.length && !fInterupted; i++ ) { + byte tcoordinate = table[i]; + if ( tcoordinate == Byte.MAX_VALUE ) { + // recuperate situation. + aPruner.getMinx( i, minx ); + + // apply all moves on it. + for ( int m = 0; m < moves.length; m++ ) { + minx.move( moves[m] ); + coordinate = aPruner.getCoordinate( minx ); + // if it is a situation, store depth in table. + if ( table[i] == Byte.MAX_VALUE && table[coordinate] == depth ) { + table[i] = next_depth; + previous_depth_length++; + fNodes++; + } + minx.undoMove(); + } + } + } + } + depth++; + } + fireEvent( new StatusEvent( StatusEventType.END_BUILDING_TABLE, "Building pruning table " + fPruner.getName() + "...", 1 ) ); + return table; + } + + private void filterPruningTables() { + ArrayList used_pruners = new ArrayList(); + ArrayList used_tables = new ArrayList(); + for ( int i = 0; i < pruners.length; i++ ) { + if ( !( pruners[i].usesCornerPermutation() && fIgnoreCornerPositions ) && + !( pruners[i].usesEdgePermutation() && fIgnoreEdgePositions ) && + !( pruners[i].usesCornerOrientation() && fIgnoreCornerOrientations ) && + !( pruners[i].usesEdgeOrientation() && fIgnoreEdgeOrientations ) ) { + used_pruners.add( pruners[i] ); + used_tables.add( tables[i] ); + } + } + fUsedPruners = used_pruners.toArray( new LLMinxPruner[used_pruners.size()] ); + fUsedTables = used_tables.toArray( new byte[used_tables.size()][] ); + } + + private static boolean checkOptimal( LLMinx aMinx ) { + byte[] moves = aMinx.getMoves(); + for ( int i = 1; i < aMinx.getDepth(); i++ ) { + if ( i < aMinx.getDepth() - 1 && moves[i - 1] == moves[i] && moves[i] == moves[i + 1] ) { + return false; + } + if ( i < aMinx.getDepth() - 1 && moves[i + 1] == moves[i - 1] ) { + if ( ( moves[i] / 4 == LLMinx.L_MOVE / 4 && moves[i - 1] / 4 == LLMinx.R_MOVE / 4 ) + || ( moves[i] / 4 == LLMinx.R_MOVE / 4 && moves[i - 1] / 4 == LLMinx.L_MOVE / 4 ) + || ( moves[i] / 4 == LLMinx.B_MOVE / 4 && moves[i - 1] / 4 == LLMinx.F_MOVE / 4 ) + || ( moves[i] / 4 == LLMinx.F_MOVE / 4 && moves[i - 1] / 4 == LLMinx.B_MOVE / 4 ) ) { + return false; + } + } + } + return true; + } + + public double getProgress() { + if ( fSearchStarted ) { + long checked = fNodes; + int branching_factor = 1; + for ( int i = 0; i < next_siblings[1].length; i++ ) { + if ( next_siblings[1][i] != -1 ) branching_factor++; + } + for ( int i = 0; i < pruned.length; i++ ) { + long l = pruned[i]; + checked += ( ( long ) ( Math.pow( branching_factor, i + 1 ) - 1 ) / ( branching_factor - 1 ) ) * l * ( i + 1 == fDepth ? moves.length : branching_factor ); + } + long total = 1 + ( moves.length ) * ( ( long ) ( Math.pow( branching_factor, fDepth ) - 1 ) / ( branching_factor - 1 ) ); + return ( ( double ) checked / total ); +// return "Depth " + fDepth + ", nodes checked: " + checked + " / " + total + " (" + (checked * 1000 / total) / 10d + "%)" +// + " (" + ((total - nodes) * 100 / (double) total) + "% pruning table efficiency)" +// ; + } + else if ( fPruner != null ) { + return ( double ) fNodes / fPruner.getTableSize(); + } + else { + return 0; + } + } + + private boolean nextNode( LLMinx aMinx ) { + if ( aMinx.getDepth() < fDepth ) { + aMinx.move( first_moves[aMinx.getLastMove() + 1] ); + return false; + } + else { + return backTrack( aMinx ); + } + } + + private boolean backTrack( LLMinx aMinx ) { + if ( aMinx.getDepth() == 0 ) return true; + byte sibling = aMinx.undoMove(); + byte last_move = aMinx.getLastMove(); + byte next_sibling = next_siblings[last_move + 1][sibling]; + while ( last_move != -1 && next_sibling == -1 ) { + sibling = aMinx.undoMove(); + last_move = aMinx.getLastMove(); + next_sibling = next_siblings[last_move + 1][sibling]; + } + if ( last_move == -1 && next_sibling == -1 ) { + return true; + } + else { + aMinx.move( next_sibling ); + return false; + } + + } + + public void addStatusListener( StatusListener aListener ) { + fEventListeners.add( aListener ); + } + + public void removeEventListener( StatusListener aListener ) { + fEventListeners.remove( aListener ); + } + + private void fireEvent( StatusEvent aStatusEvent ) { + Iterator listeners = fEventListeners.iterator(); + while ( listeners.hasNext() ) { + StatusListener listener = listeners.next(); + listener.statusEvent( aStatusEvent ); + } + } + +} diff --git a/src/llminx/solver/StatusEvent.java b/src/llminx/solver/StatusEvent.java new file mode 100644 index 0000000..627bc42 --- /dev/null +++ b/src/llminx/solver/StatusEvent.java @@ -0,0 +1,29 @@ +package llminx.solver; + +/** + * + */ +public class StatusEvent { + + private StatusEventType fType; + private String fMessage; + private double fProgress; + + public StatusEvent(StatusEventType type, String aMessage, double progress) { + fType = type; + fMessage = aMessage; + fProgress = progress; + } + + public StatusEventType getType() { + return fType; + } + + public String getMessage() { + return fMessage; + } + + public double getProgress() { + return fProgress; + } +} diff --git a/src/llminx/solver/StatusEventType.java b/src/llminx/solver/StatusEventType.java new file mode 100644 index 0000000..df51235 --- /dev/null +++ b/src/llminx/solver/StatusEventType.java @@ -0,0 +1,8 @@ +package llminx.solver; + +/** + * + */ +public enum StatusEventType { + START_SEARCH, START_DEPTH, END_DEPTH, START_BUILDING_TABLE, END_BUILDING_TABLE, MESSAGE, FINISH_SEARCH +} diff --git a/src/llminx/solver/StatusListener.java b/src/llminx/solver/StatusListener.java new file mode 100644 index 0000000..b30550c --- /dev/null +++ b/src/llminx/solver/StatusListener.java @@ -0,0 +1,12 @@ +package llminx.solver; + +import java.util.EventListener; + +/** + * + */ +public interface StatusListener extends EventListener { + + public void statusEvent(StatusEvent aEvent); + +} diff --git a/src/llminx/solver/SystemOutStatusListener.java b/src/llminx/solver/SystemOutStatusListener.java new file mode 100644 index 0000000..2b73c36 --- /dev/null +++ b/src/llminx/solver/SystemOutStatusListener.java @@ -0,0 +1,13 @@ +package llminx.solver; + +/** + * + */ +public class SystemOutStatusListener implements StatusListener { + + + public void statusEvent(StatusEvent aEvent) { + System.out.println(aEvent.getMessage()); + } + +} diff --git a/src/llminx/solver/searchmode/AbstractLLMinxPruner.java b/src/llminx/solver/searchmode/AbstractLLMinxPruner.java new file mode 100644 index 0000000..31360cd --- /dev/null +++ b/src/llminx/solver/searchmode/AbstractLLMinxPruner.java @@ -0,0 +1,60 @@ +package llminx.solver.searchmode; + +import java.io.*; + +/** + * + */ +public abstract class AbstractLLMinxPruner implements LLMinxPruner { + + private static final String FILE_EXTENSION = ".prn"; + private String fName; + private String fTablePath; + + public AbstractLLMinxPruner( String name, String tablePath ) { + fName = name; + fTablePath = tablePath; + } + + public String getName() { + return fName; + } + + public boolean isPrecomputed( LLMinxMetric aMetric ) { + return getTableFile( aMetric ).exists(); + } + + public byte[] loadTable( LLMinxMetric aMetric ) { + try { + BufferedInputStream prun_in = new BufferedInputStream( new FileInputStream( getTableFile( aMetric ) ), 1 << 20 ); + byte[] table = new byte[getTableSize()]; + for ( int i = 0; i < table.length; i++ ) { + table[i] = (byte) prun_in.read(); + } + prun_in.read( table ); + prun_in.close(); + return table; + } + catch ( IOException e ) { + return null; + } + } + + public void saveTable( byte[] aTable, LLMinxMetric aMetric ) { + try { + BufferedOutputStream prun_out = new BufferedOutputStream( new FileOutputStream( getTableFile( aMetric ) ), 1 << 22 ); + for ( int i = 0; i < aTable.length; i++ ) { + prun_out.write( aTable[i] ); + } + prun_out.close(); + } + catch ( IOException e ) { + e.printStackTrace(); + } + } + + private File getTableFile( LLMinxMetric aMetric ) { + return new File( fTablePath + aMetric.name() + FILE_EXTENSION ); + } + +} diff --git a/src/llminx/solver/searchmode/CompositePruner.java b/src/llminx/solver/searchmode/CompositePruner.java new file mode 100644 index 0000000..fcdefdd --- /dev/null +++ b/src/llminx/solver/searchmode/CompositePruner.java @@ -0,0 +1,49 @@ +package llminx.solver.searchmode; + +import llminx.LLMinx; + +/** + * + */ +public class CompositePruner extends AbstractLLMinxPruner { + + private LLMinxPruner fPrunerA; + private LLMinxPruner fPrunerB; + + public CompositePruner(String name, String tablePath, LLMinxPruner prunerA, LLMinxPruner prunerB) { + super(name, tablePath); + fPrunerA = prunerA; + fPrunerB = prunerB; + } + + + public int getTableSize() { + return fPrunerA.getTableSize() * fPrunerB.getTableSize(); + } + + public int getCoordinate(LLMinx aMinx) { + return fPrunerA.getCoordinate(aMinx) * fPrunerB.getTableSize() + fPrunerB.getCoordinate(aMinx); + } + + public void getMinx(int aCoordinate, LLMinx aMinx) { + fPrunerB.getMinx(aCoordinate % fPrunerB.getTableSize(), aMinx); + fPrunerA.getMinx(aCoordinate / fPrunerB.getTableSize(), aMinx); + } + + public boolean usesCornerPermutation() { + return fPrunerA.usesCornerPermutation() || fPrunerB.usesCornerPermutation(); + } + + public boolean usesEdgePermutation() { + return fPrunerA.usesCornerPermutation() || fPrunerB.usesCornerPermutation(); + } + + public boolean usesCornerOrientation() { + return fPrunerA.usesCornerOrientation() || fPrunerB.usesCornerOrientation(); + } + + public boolean usesEdgeOrientation() { + return fPrunerA.usesEdgeOrientation() || fPrunerB.usesEdgeOrientation(); + } + +} diff --git a/src/llminx/solver/searchmode/CornerOrientationPruner.java b/src/llminx/solver/searchmode/CornerOrientationPruner.java new file mode 100644 index 0000000..54b29a6 --- /dev/null +++ b/src/llminx/solver/searchmode/CornerOrientationPruner.java @@ -0,0 +1,46 @@ +package llminx.solver.searchmode; + +import util.CoordinateUtil; +import llminx.LLMinx; + +/** + * + */ +public class CornerOrientationPruner extends AbstractLLMinxPruner { + + private byte[] fCorners; + + public CornerOrientationPruner(String aName, String aPath, byte[] aCorners) { + super(aName, aPath); + fCorners = aCorners; + } + + public int getTableSize() { + return CoordinateUtil.POWERS_OF_THREE[fCorners.length-1]; + } + + public int getCoordinate(LLMinx aMinx) { + return CoordinateUtil.getCornerOrientationCoordinate(aMinx.getCornerOrientations(), fCorners); + } + + public void getMinx(int aCoordinate, LLMinx aMinx) { + aMinx.setCornerOrientations(CoordinateUtil.getCornerOrientation(aCoordinate, fCorners)); + } + + public boolean usesCornerPermutation() { + return false; + } + + public boolean usesEdgePermutation() { + return false; + } + + public boolean usesCornerOrientation() { + return true; + } + + public boolean usesEdgeOrientation() { + return false; + } + +} diff --git a/src/llminx/solver/searchmode/CornerPermutationPruner.java b/src/llminx/solver/searchmode/CornerPermutationPruner.java new file mode 100644 index 0000000..587674e --- /dev/null +++ b/src/llminx/solver/searchmode/CornerPermutationPruner.java @@ -0,0 +1,46 @@ +package llminx.solver.searchmode; + +import llminx.LLMinx; +import util.CoordinateUtil; + +/** + * + */ +public class CornerPermutationPruner extends AbstractLLMinxPruner { + + private byte[] fCorners; + + public CornerPermutationPruner(String aName, String aPath, byte[] aCorners) { + super(aName, aPath); + fCorners = aCorners; + } + + public int getTableSize() { + return CoordinateUtil.FAC[fCorners.length]/2; + } + + public int getCoordinate(LLMinx aMinx) { + return CoordinateUtil.getPermutationCoordinate(aMinx.getCornerPositions(), fCorners); + } + + public void getMinx(int aCoordinate, LLMinx aMinx) { + CoordinateUtil.getPermutation(aCoordinate, aMinx.getCornerPositions(), fCorners); + } + + public boolean usesCornerPermutation() { + return true; + } + + public boolean usesEdgePermutation() { + return false; + } + + public boolean usesCornerOrientation() { + return false; + } + + public boolean usesEdgeOrientation() { + return false; + } + +} diff --git a/src/llminx/solver/searchmode/EdgeOrientationPruner.java b/src/llminx/solver/searchmode/EdgeOrientationPruner.java new file mode 100644 index 0000000..bbe2056 --- /dev/null +++ b/src/llminx/solver/searchmode/EdgeOrientationPruner.java @@ -0,0 +1,46 @@ +package llminx.solver.searchmode; + +import util.CoordinateUtil; +import llminx.LLMinx; + +/** + * + */ +public class EdgeOrientationPruner extends AbstractLLMinxPruner { + + private byte[] fEdges; + + public EdgeOrientationPruner(String aName, String aPath, byte[] aEdges) { + super(aName, aPath); + fEdges = aEdges; + } + + public int getTableSize() { + return CoordinateUtil.POWERS_OF_TWO[fEdges.length-1]; + } + + public int getCoordinate(LLMinx aMinx) { + return CoordinateUtil.getEdgeOrientationCoordinate(aMinx.getEdgeOrientations(), fEdges.length); + } + + public void getMinx(int aCoordinate, LLMinx aMinx) { + aMinx.setEdgeOrientations(CoordinateUtil.getEdgeOrientation(aCoordinate, fEdges.length)); + } + + public boolean usesCornerPermutation() { + return false; + } + + public boolean usesEdgePermutation() { + return false; + } + + public boolean usesCornerOrientation() { + return false; + } + + public boolean usesEdgeOrientation() { + return true; + } + +} diff --git a/src/llminx/solver/searchmode/EdgePermutationPruner.java b/src/llminx/solver/searchmode/EdgePermutationPruner.java new file mode 100644 index 0000000..45fed71 --- /dev/null +++ b/src/llminx/solver/searchmode/EdgePermutationPruner.java @@ -0,0 +1,46 @@ +package llminx.solver.searchmode; + +import util.CoordinateUtil; +import llminx.LLMinx; + +/** + * + */ +public class EdgePermutationPruner extends AbstractLLMinxPruner { + + private byte[] fEdges; + + public EdgePermutationPruner(String aName, String aPath, byte[] aEdges) { + super(aName, aPath); + fEdges = aEdges; + } + + public int getTableSize() { + return CoordinateUtil.FAC[fEdges.length]/2; + } + + public int getCoordinate(LLMinx aMinx) { + return CoordinateUtil.getPermutationCoordinate(aMinx.getEdgePositions(), fEdges); + } + + public void getMinx(int aCoordinate, LLMinx aMinx) { + CoordinateUtil.getPermutation(aCoordinate, aMinx.getEdgePositions(), fEdges); + } + + public boolean usesCornerPermutation() { + return false; + } + + public boolean usesEdgePermutation() { + return true; + } + + public boolean usesCornerOrientation() { + return false; + } + + public boolean usesEdgeOrientation() { + return false; + } + +} diff --git a/src/llminx/solver/searchmode/LLMinxMetric.java b/src/llminx/solver/searchmode/LLMinxMetric.java new file mode 100644 index 0000000..d39b709 --- /dev/null +++ b/src/llminx/solver/searchmode/LLMinxMetric.java @@ -0,0 +1,24 @@ +package llminx.solver.searchmode; + +/** + * + */ +public enum LLMinxMetric { + + FIFTH("Fifth turn metric"), FACE("Face turn metric"); + + private String fDescription; + + LLMinxMetric(String description) { + fDescription = description; + } + + public String getDescription() { + return fDescription; + } + + public String toString() { + return fDescription; + } + +} diff --git a/src/llminx/solver/searchmode/LLMinxPruner.java b/src/llminx/solver/searchmode/LLMinxPruner.java new file mode 100644 index 0000000..14d247b --- /dev/null +++ b/src/llminx/solver/searchmode/LLMinxPruner.java @@ -0,0 +1,36 @@ +package llminx.solver.searchmode; + +import llminx.LLMinx; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * + */ +public interface LLMinxPruner { + + public String getName(); + + public boolean isPrecomputed(LLMinxMetric aMetric); + + public byte[] loadTable(LLMinxMetric aMetric); + + public void saveTable(byte[] aTable, LLMinxMetric aMetric); + + public int getTableSize(); + + public int getCoordinate(LLMinx aMinx); + + public void getMinx(int aCoordinate, LLMinx aMinx); + + public boolean usesCornerPermutation(); + + public boolean usesEdgePermutation(); + + public boolean usesCornerOrientation(); + + public boolean usesEdgeOrientation(); + +} diff --git a/src/llminx/solver/searchmode/LLMinxSearchMode.java b/src/llminx/solver/searchmode/LLMinxSearchMode.java new file mode 100644 index 0000000..98e5fda --- /dev/null +++ b/src/llminx/solver/searchmode/LLMinxSearchMode.java @@ -0,0 +1,423 @@ +package llminx.solver.searchmode; + +import llminx.LLMinx; + +/** + * + */ +public enum LLMinxSearchMode { + + RU_MODE( + "RU", + new byte[]{ + LLMinx.R_MOVE, LLMinx.Ri_MOVE, LLMinx.R2_MOVE, LLMinx.R2i_MOVE, + LLMinx.U_MOVE, LLMinx.Ui_MOVE, LLMinx.U2_MOVE, LLMinx.U2i_MOVE + }, + new LLMinxPruner[]{ + new EdgePermutationPruner("Edge permutations RU", "ruedgepermutations", + new byte[]{ + LLMinx.UE1_POSITION, + LLMinx.UE2_POSITION, + LLMinx.UE3_POSITION, + LLMinx.UE4_POSITION, + LLMinx.UE5_POSITION, + LLMinx.RE2_POSITION, + LLMinx.RE3_POSITION, + LLMinx.RE4_POSITION, + LLMinx.FE2_POSITION + } + ), + new CompositePruner("Corners RU", "rucorners", + new CornerPermutationPruner("Corner permutations RU", "rucornerpermutations", + new byte[]{ + LLMinx.UC1_POSITION, + LLMinx.UC2_POSITION, + LLMinx.UC3_POSITION, + LLMinx.UC4_POSITION, + LLMinx.UC5_POSITION, + LLMinx.RC1_POSITION, + LLMinx.RC5_POSITION, + LLMinx.FC5_POSITION + } + ), + new CornerOrientationPruner("Corner orientations RU", "rucornerorientations", + new byte[]{ + LLMinx.UC1_POSITION, + LLMinx.UC2_POSITION, + LLMinx.UC3_POSITION, + LLMinx.UC4_POSITION, + LLMinx.UC5_POSITION, + LLMinx.RC1_POSITION, + LLMinx.RC5_POSITION, + LLMinx.FC5_POSITION + } + ) + ) + } + ), + RUF_MODE( + "RUF", + new byte[]{ + LLMinx.R_MOVE, LLMinx.Ri_MOVE, LLMinx.R2_MOVE, LLMinx.R2i_MOVE, + LLMinx.U_MOVE, LLMinx.Ui_MOVE, LLMinx.U2_MOVE, LLMinx.U2i_MOVE, + LLMinx.F_MOVE, LLMinx.Fi_MOVE, LLMinx.F2_MOVE, LLMinx.F2i_MOVE + }, + new LLMinxPruner[]{ + new CornerPermutationPruner("Corner permutations RUF", "rufcornerpermutations", + new byte[]{ + LLMinx.UC1_POSITION, + LLMinx.UC2_POSITION, + LLMinx.UC3_POSITION, + LLMinx.UC4_POSITION, + LLMinx.UC5_POSITION, + LLMinx.RC1_POSITION, + LLMinx.RC5_POSITION, + LLMinx.FC5_POSITION, + LLMinx.FC1_POSITION, + LLMinx.FC2_POSITION + } + ), + new EdgePermutationPruner("Edge permutations RUF", "rufedgepermutations", + new byte[]{ + LLMinx.UE1_POSITION, + LLMinx.UE2_POSITION, + LLMinx.UE3_POSITION, + LLMinx.UE4_POSITION, + LLMinx.UE5_POSITION, + LLMinx.RE2_POSITION, + LLMinx.RE3_POSITION, + LLMinx.RE4_POSITION, + LLMinx.FE2_POSITION, + LLMinx.FE3_POSITION, + LLMinx.FE4_POSITION, + LLMinx.FE5_POSITION + } + ), + new CompositePruner("Orientations RUF", "ruforientations", + new CornerOrientationPruner("Corner orientations RUF", "rufcornerorientations", + new byte[]{ + LLMinx.UC1_POSITION, + LLMinx.UC2_POSITION, + LLMinx.UC3_POSITION, + LLMinx.UC4_POSITION, + LLMinx.UC5_POSITION, + LLMinx.RC1_POSITION, + LLMinx.RC5_POSITION, + LLMinx.FC5_POSITION, + LLMinx.FC1_POSITION, + LLMinx.FC2_POSITION + } + ), + new EdgeOrientationPruner("Edge orientations RUF", "rufedgeorientations", + new byte[]{ + LLMinx.UE1_POSITION, + LLMinx.UE2_POSITION, + LLMinx.UE3_POSITION, + LLMinx.UE4_POSITION, + LLMinx.UE5_POSITION, + LLMinx.RE2_POSITION, + LLMinx.RE3_POSITION, + LLMinx.RE4_POSITION, + LLMinx.FE2_POSITION, + LLMinx.FE3_POSITION, + LLMinx.FE4_POSITION, + LLMinx.FE5_POSITION + } + ) + ) + }), + RUL_MODE( + "RUL", + new byte[]{ + LLMinx.R_MOVE, LLMinx.Ri_MOVE, LLMinx.R2_MOVE, LLMinx.R2i_MOVE, + LLMinx.U_MOVE, LLMinx.Ui_MOVE, LLMinx.U2_MOVE, LLMinx.U2i_MOVE, + LLMinx.L_MOVE, LLMinx.Li_MOVE, LLMinx.L2_MOVE, LLMinx.L2i_MOVE + }, + new LLMinxPruner[]{ + new CornerOrientationPruner("Corner orientations RUL", "rulcornerorientations", + new byte[]{ + LLMinx.UC1_POSITION, + LLMinx.UC2_POSITION, + LLMinx.UC3_POSITION, + LLMinx.UC4_POSITION, + LLMinx.UC5_POSITION, + LLMinx.RC1_POSITION, + LLMinx.RC5_POSITION, + LLMinx.FC5_POSITION, + LLMinx.FC2_POSITION, + LLMinx.LC1_POSITION, + LLMinx.LC2_POSITION + } + ), + new CornerPermutationPruner("Corner permutations RUL", "rulcornerpermutations", + new byte[]{ + LLMinx.UC1_POSITION, + LLMinx.UC2_POSITION, + LLMinx.UC3_POSITION, + LLMinx.UC4_POSITION, + LLMinx.UC5_POSITION, + LLMinx.RC1_POSITION, + LLMinx.RC5_POSITION, + LLMinx.FC5_POSITION, + LLMinx.FC2_POSITION, + LLMinx.LC1_POSITION, + LLMinx.LC2_POSITION + } + ) + }), + RUFL_MODE( + "RUFL", + new byte[]{ + LLMinx.R_MOVE, LLMinx.Ri_MOVE, LLMinx.R2_MOVE, LLMinx.R2i_MOVE, + LLMinx.U_MOVE, LLMinx.Ui_MOVE, LLMinx.U2_MOVE, LLMinx.U2i_MOVE, + LLMinx.F_MOVE, LLMinx.Fi_MOVE, LLMinx.F2_MOVE, LLMinx.F2i_MOVE, + LLMinx.L_MOVE, LLMinx.Li_MOVE, LLMinx.L2_MOVE, LLMinx.L2i_MOVE + }, + new LLMinxPruner[]{ + new EdgeOrientationPruner("Edge orientations RUFL", "rufledgeorientations", + new byte[]{ + LLMinx.UE1_POSITION, + LLMinx.UE2_POSITION, + LLMinx.UE3_POSITION, + LLMinx.UE4_POSITION, + LLMinx.UE5_POSITION, + LLMinx.RE2_POSITION, + LLMinx.RE3_POSITION, + LLMinx.RE4_POSITION, + LLMinx.FE2_POSITION, + LLMinx.FE3_POSITION, + LLMinx.FE4_POSITION, + LLMinx.FE5_POSITION, + LLMinx.LE3_POSITION, + LLMinx.LE4_POSITION, + LLMinx.LE5_POSITION + } + ), + new CornerOrientationPruner("Corner orientations RUFL", "ruflcornerorientations", + new byte[]{ + LLMinx.UC1_POSITION, + LLMinx.UC2_POSITION, + LLMinx.UC3_POSITION, + LLMinx.UC4_POSITION, + LLMinx.UC5_POSITION, + LLMinx.RC1_POSITION, + LLMinx.RC5_POSITION, + LLMinx.FC5_POSITION, + LLMinx.FC1_POSITION, + LLMinx.FC2_POSITION, + LLMinx.LC1_POSITION, + LLMinx.LC2_POSITION + } + ), + new CornerPermutationPruner("Corner permutations RUFL", "ruflcornerpermutations", + new byte[]{ + LLMinx.UC1_POSITION, + LLMinx.UC2_POSITION, + LLMinx.UC3_POSITION, + LLMinx.UC4_POSITION, + LLMinx.UC5_POSITION, + LLMinx.RC1_POSITION, + LLMinx.RC5_POSITION, + LLMinx.FC5_POSITION, + LLMinx.FC1_POSITION, + LLMinx.FC2_POSITION, + LLMinx.LC1_POSITION, + LLMinx.LC2_POSITION + } + ) + }), + RUFLB_MODE( + "RUFLB", + new byte[]{ + LLMinx.R_MOVE, LLMinx.Ri_MOVE, LLMinx.R2_MOVE, LLMinx.R2i_MOVE, + LLMinx.U_MOVE, LLMinx.Ui_MOVE, LLMinx.U2_MOVE, LLMinx.U2i_MOVE, + LLMinx.F_MOVE, LLMinx.Fi_MOVE, LLMinx.F2_MOVE, LLMinx.F2i_MOVE, + LLMinx.L_MOVE, LLMinx.Li_MOVE, LLMinx.L2_MOVE, LLMinx.L2i_MOVE, + LLMinx.B_MOVE, LLMinx.Bi_MOVE, LLMinx.B2_MOVE, LLMinx.B2i_MOVE + }, + new LLMinxPruner[]{ + new EdgeOrientationPruner("Edge orientations RUFLB", "ruflbedgeorientations", + new byte[]{ + LLMinx.UE1_POSITION, + LLMinx.UE2_POSITION, + LLMinx.UE3_POSITION, + LLMinx.UE4_POSITION, + LLMinx.UE5_POSITION, + LLMinx.RE2_POSITION, + LLMinx.RE3_POSITION, + LLMinx.RE4_POSITION, + LLMinx.FE2_POSITION, + LLMinx.FE3_POSITION, + LLMinx.FE4_POSITION, + LLMinx.FE5_POSITION, + LLMinx.LE3_POSITION, + LLMinx.LE4_POSITION, + LLMinx.LE5_POSITION, + LLMinx.BE3_POSITION, + LLMinx.BE4_POSITION, + LLMinx.BE5_POSITION + } + ), + new CornerOrientationPruner("Corner orientations RUFLB", "ruflbcornerorientations", + new byte[]{ + LLMinx.UC1_POSITION, + LLMinx.UC2_POSITION, + LLMinx.UC3_POSITION, + LLMinx.UC4_POSITION, + LLMinx.UC5_POSITION, + LLMinx.RC1_POSITION, + LLMinx.RC5_POSITION, + LLMinx.FC5_POSITION, + LLMinx.FC1_POSITION, + LLMinx.FC2_POSITION, + LLMinx.LC1_POSITION, + LLMinx.LC2_POSITION, + LLMinx.BC1_POSITION, + LLMinx.BC2_POSITION + } + ), + new CompositePruner("Edge orientations / Corner separations RUFLB", "ruflbedgeorientationscornerseparations", + new EdgeOrientationPruner("Edge orientations RUFLB", "ruflbedgeorientations", + new byte[]{ + LLMinx.UE1_POSITION, + LLMinx.UE2_POSITION, + LLMinx.UE3_POSITION, + LLMinx.UE4_POSITION, + LLMinx.UE5_POSITION, + LLMinx.RE2_POSITION, + LLMinx.RE3_POSITION, + LLMinx.RE4_POSITION, + LLMinx.FE2_POSITION, + LLMinx.FE3_POSITION, + LLMinx.FE4_POSITION, + LLMinx.FE5_POSITION, + LLMinx.LE3_POSITION, + LLMinx.LE4_POSITION, + LLMinx.LE5_POSITION, + LLMinx.BE3_POSITION, + LLMinx.BE4_POSITION, + LLMinx.BE5_POSITION + } + ), + new SeparationPruner("Corner separations U RUFLB", "ruflbcornerseparationsu", + new byte[]{ + LLMinx.UC1_POSITION, + LLMinx.UC2_POSITION, + LLMinx.UC3_POSITION, + LLMinx.UC4_POSITION, + LLMinx.UC5_POSITION, + }, + new byte[]{ + } + ) + ), + new SeparationPruner("Separations R RUFLB", "ruflbseparationsr", + new byte[]{ + LLMinx.RC1_POSITION, + LLMinx.FC5_POSITION, + LLMinx.UC3_POSITION, + LLMinx.UC2_POSITION, + LLMinx.RC5_POSITION + }, + new byte[]{ + LLMinx.FE2_POSITION, + LLMinx.RE2_POSITION, + LLMinx.RE3_POSITION, + LLMinx.RE4_POSITION, + LLMinx.UE5_POSITION + } + ), + new SeparationPruner("Separations L RUFLB", "ruflbseparationsl", + new byte[]{ + LLMinx.LC1_POSITION, + LLMinx.LC2_POSITION, + LLMinx.FC2_POSITION, + LLMinx.UC4_POSITION, + LLMinx.UC5_POSITION + }, + new byte[]{ + LLMinx.FE5_POSITION, + LLMinx.UE2_POSITION, + LLMinx.LE3_POSITION, + LLMinx.LE4_POSITION, + LLMinx.LE5_POSITION + } + ), + new SeparationPruner("Separations F RUFLB", "ruflbseparationsf", + new byte[]{ + LLMinx.FC5_POSITION, + LLMinx.FC2_POSITION, + LLMinx.FC1_POSITION, + LLMinx.UC4_POSITION, + LLMinx.UC3_POSITION + }, + new byte[]{ + LLMinx.UE1_POSITION, + LLMinx.FE2_POSITION, + LLMinx.FE3_POSITION, + LLMinx.FE4_POSITION, + LLMinx.FE5_POSITION + } + ), + new SeparationPruner("Separations B RUFLB", "ruflbseparationsb", + new byte[]{ + LLMinx.LC2_POSITION, + LLMinx.BC1_POSITION, + LLMinx.BC2_POSITION, + LLMinx.UC1_POSITION, + LLMinx.UC5_POSITION + }, + new byte[]{ + LLMinx.LE5_POSITION, + LLMinx.BE3_POSITION, + LLMinx.BE4_POSITION, + LLMinx.BE5_POSITION, + LLMinx.UE3_POSITION + } + ), +// new SeparationPruner("Separations U RUFLB", "ruflbseparationsu", +// new byte[]{ +// LLMinx.UC1_POSITION, +// LLMinx.UC2_POSITION, +// LLMinx.UC3_POSITION, +// LLMinx.UC4_POSITION, +// LLMinx.UC5_POSITION, +// }, +// new byte[]{ +// LLMinx.UE1_POSITION, +// LLMinx.UE2_POSITION, +// LLMinx.UE3_POSITION, +// LLMinx.UE4_POSITION, +// LLMinx.UE5_POSITION +// } +// ) + } + ); + + private String fDescription; + private LLMinxPruner[] fPruners; + private byte[] fPossibleMoves; + boolean fAllowSuccessiveFaceTurns; + + LLMinxSearchMode(String description, byte[] possibleMoves, LLMinxPruner[] pruners) { + fDescription = description; + fPruners = pruners; + fPossibleMoves = possibleMoves; + } + + public String getDescription() { + return fDescription; + } + + public LLMinxPruner[] getPruners() { + return fPruners; + } + + public byte[] getPossibleMoves() { + return fPossibleMoves; + } + + + public String toString() { + return fDescription; + } } diff --git a/src/llminx/solver/searchmode/SeparationPruner.java b/src/llminx/solver/searchmode/SeparationPruner.java new file mode 100644 index 0000000..26dad5e --- /dev/null +++ b/src/llminx/solver/searchmode/SeparationPruner.java @@ -0,0 +1,56 @@ +package llminx.solver.searchmode; + +import util.CoordinateUtil; +import llminx.LLMinx; + +/** + * + */ +public class SeparationPruner extends AbstractLLMinxPruner { + + private byte[] fCorners; + private byte[] fEdges; + + public SeparationPruner(String aName, String aPath, byte[] aCorners, byte[] aEdges) { + super(aName, aPath); + fCorners = aCorners; + fEdges = aEdges; + } + + public int getTableSize() { + return CoordinateUtil.CKN[14][fCorners.length] * CoordinateUtil.CKN[18][fEdges.length]; + } + + + public int getCoordinate(LLMinx aMinx) { + return CoordinateUtil.getSeparationCoordinate(aMinx.getCornerPositions(), fCorners) * + CoordinateUtil.CKN[18][fEdges.length] + + CoordinateUtil.getSeparationCoordinate(aMinx.getEdgePositions(), fEdges); + } + + public void getMinx(int aCoordinate, LLMinx aMinx) { + CoordinateUtil.getSeparation(aCoordinate % CoordinateUtil.CKN[18][fEdges.length], + aMinx.getEdgePositions(), + fEdges); + CoordinateUtil.getSeparation(aCoordinate / CoordinateUtil.CKN[18][fEdges.length], + aMinx.getCornerPositions(), + fCorners); + } + + public boolean usesCornerPermutation() { + return fCorners.length > 1; + } + + public boolean usesEdgePermutation() { + return fEdges.length > 1; + } + + public boolean usesCornerOrientation() { + return false; + } + + public boolean usesEdgeOrientation() { + return false; + } + +} diff --git a/src/util/CoordinateUtil.java b/src/util/CoordinateUtil.java new file mode 100644 index 0000000..c570484 --- /dev/null +++ b/src/util/CoordinateUtil.java @@ -0,0 +1,188 @@ +package util; + +import java.util.Arrays; + +/** + * + */ +public class CoordinateUtil { + + public static final int[] POWERS_OF_TWO_MINUS_ONE = new int[30]; + public static final int[] POWERS_OF_TWO = new int[30]; + public static final int[] POWERS_OF_THREE = new int[20]; + public static final int[] FAC = new int[13]; + public static final int[][] CKN; + + static { + for ( int i = 0; i < POWERS_OF_TWO_MINUS_ONE.length; i++ ) { + POWERS_OF_TWO[i] = 1 << i; + POWERS_OF_TWO_MINUS_ONE[i] = ( 1 << i ) - 1; + } + POWERS_OF_THREE[0] = 1; + for ( int i = 1; i < POWERS_OF_THREE.length; i++ ) { + POWERS_OF_THREE[i] = POWERS_OF_THREE[i - 1] * 3; + } + FAC[0] = 1; + for ( int i = 1; i < FAC.length; i++ ) { + FAC[i] = FAC[i - 1] * i; + } + CKN = new int[20][9]; + for ( int i = 0; i < CKN.length; i++ ) { + int[] ckns = CKN[i]; + for ( int j = 0; j < ckns.length; j++ ) { + ckns[j] = ( int ) c( i, j ); + } + } + } + + public static int getPermutationCoordinate( byte aPermutation[], byte aCubies[] ) { + int coordinate = 0; + byte[] locations = new byte[aCubies.length]; + System.arraycopy( aCubies, 0, locations, 0, aCubies.length ); + for ( int i = 0; i < aCubies.length - 2; i++ ) { + int piece = aCubies[i]; + for ( int j = 0; j + i < locations.length; j++ ) { + if ( aPermutation[locations[j]] == piece ) { + coordinate *= ( locations.length - i ); + coordinate += j; + for ( int k = j; k + i < locations.length - 1; k++ ) { + locations[k] = locations[k + 1]; + } + break; + } + } + } + return coordinate; + } + + public static void getPermutation( int aCoordinate, byte aPermutation[], byte aCubies[] ) { + int[] indices = new int[aCubies.length]; + byte[] locations = new byte[aCubies.length]; + System.arraycopy( aCubies, 0, locations, 0, aCubies.length ); + int factor = 3; + int sum = 0; + for ( int i = indices.length - 3; i >= 0; i-- ) { + indices[i] = ( aCoordinate % factor ); + sum += indices[i]; + aCoordinate /= factor; + factor++; + } + indices[indices.length - 2] = sum % 2; + indices[indices.length - 1] = 0; + for ( int i = 0; i < aCubies.length; i++ ) { + int index = indices[i]; + aPermutation[locations[index]] = aCubies[i]; + for ( int k = index; k + i < locations.length - 1; k++ ) { + locations[k] = locations[k + 1]; + } + } + } + +// public static int getPermutationCoordinate(byte aPermutation[], byte aCubies[]) { +// int coordinate = 0; +// for (int i = 1; i < aCubies.length; i++) { +// int count = 0; +// for (int j = 0; j < i; j++) +// if (aPermutation[aCubies[j]] > aPermutation[aCubies[i]]) +// count++; +// +// coordinate += FAC[i] * count; +// } +// +// return coordinate; +// } +// +// public static void getPermutation(int aCoordinate, byte aPermutation[], byte aCubies[]) { +// byte work_cubies[] = new byte[aCubies.length]; +// System.arraycopy(aCubies, 0, work_cubies, 0, aCubies.length); +// for (int i = work_cubies.length - 1; i > 0; i--) { +// int cubie = aCoordinate / FAC[i]; +// aPermutation[aCubies[i]] = work_cubies[i - cubie]; +// if (cubie > 0) +// System.arraycopy(work_cubies, (i + 1) - cubie, work_cubies, i - cubie, cubie); +// aCoordinate -= FAC[i] * cubie; +// } +// } + + public static int getSeparationCoordinate( byte aPermutation[], byte aCubies[] ) { + int coordinate = 0; + int count = 1; + for ( int i = 0; i < aPermutation.length; i++ ) { + byte aPosition = aPermutation[i]; + int j; + for ( j = 0; j < aCubies.length && aCubies[j] != aPosition; j++ ) ; + if ( j != aCubies.length ) { + coordinate += CKN[i][count]; + count++; + } + } + + return coordinate; + } + + public static void getSeparation( int aCoordinate, byte aPermutation[], byte aCubies[] ) { + Arrays.fill( aPermutation, ( byte ) -1 ); + for ( int count = aCubies.length; count > 0; count-- ) { + int position_left; + for ( position_left = aPermutation.length - 1; aCoordinate < CKN[position_left][count]; position_left-- ) ; + aPermutation[position_left] = aCubies[count - 1]; + aCoordinate -= CKN[position_left][count]; + } + + } + + public static int getEdgeOrientationCoordinate( int aEdgeOrientation, int aEdgeCount ) { + return POWERS_OF_TWO_MINUS_ONE[aEdgeCount - 1] & aEdgeOrientation; + } + + public static int getEdgeOrientation( int aEdgeCoordinate, int aEdgeCount ) { + aEdgeCoordinate = POWERS_OF_TWO_MINUS_ONE[aEdgeCount - 1] & aEdgeCoordinate; + return aEdgeCoordinate | getParity( aEdgeCoordinate ) << aEdgeCount - 1; + } + + public static int getCornerOrientationCoordinate( int aCornerOrientation, byte aCubies[] ) { + int coordinate = 0; + for ( int i = 0; i < aCubies.length - 1; i++ ) + coordinate += POWERS_OF_THREE[i] * ( aCornerOrientation >> aCubies[i] * 2 & 3 ); + + return coordinate; + } + + public static int getCornerOrientation( int aCornerOrientation, byte aCubies[] ) { + int orientation = 0; + int sum_orientation = 0; + int i; + for ( i = 0; i < aCubies.length - 1; i++ ) { + int cubie_orientation = aCornerOrientation % 3; + sum_orientation += cubie_orientation; + orientation += POWERS_OF_TWO[aCubies[i] * 2] * cubie_orientation; + aCornerOrientation /= 3; + } + + orientation += POWERS_OF_TWO[aCubies[i] * 2] * ( ( 3 - sum_orientation % 3 ) % 3 ); + return orientation; + } + + public static int getParity( int aInt ) { + int x = aInt; + x = x ^ ( x >> 16 ); + x = x ^ ( x >> 8 ); + x = x ^ ( x >> 4 ); + x = x ^ ( x >> 2 ); + x = x ^ ( x >> 1 ); + return x & 1; + } + + public static long c( int aN, int aK ) { + if ( aN < aK ) return 0; + long result = 1; + for ( int i = aN; i > aK; i-- ) { + result *= i; + } + for ( int i = 1; i <= aN - aK; i++ ) { + result /= i; + } + return result; + } + +}