diff --git a/src/main/java/hse/java/practice/task1/Permutations.java b/src/main/java/hse/java/practice/task1/Permutations.java new file mode 100644 index 00000000..8af34961 --- /dev/null +++ b/src/main/java/hse/java/practice/task1/Permutations.java @@ -0,0 +1,84 @@ +package hse.java.practice.task1; + +import java.util.Arrays; +import java.util.stream.*; +import java.util.stream.IntStream; + +public class Permutations { + static final int[] CURR_EDGES_ORDER = {2, 4, 3, 5, 0, 1}; + public static final int G_SIZE = 48; + + // Циклы перестановок для левой грани по часовой стрелке + static final int[][] L_CW = { + {1, 3, 8, 6}, + {2, 5, 7, 4}, + {33, 9, 41, 32}, + {36, 12, 44, 29}, + {38, 14, 46, 27} + }; + + // Циклы перестановок для передней грани по часовой стрелке + static final int[][] F_CW = { + {9, 11, 16, 14}, + {10, 13, 15, 12}, + {38, 17, 43, 8}, + {39, 20, 42, 5}, + {40, 22, 41, 3} + }; + + // Циклы перестановок для правой грани по часовой стрелке + static final int[][] R_CW = { + {17, 19, 24, 22}, + {18, 21, 23, 20}, + {48, 16, 40, 25}, + {45, 13, 37, 28}, + {43, 11, 35, 30} + }; + + // Циклы перестановок для задней грани по часовой стрелке + static final int[][] B_CW = { + {25, 27, 32, 30}, + {26, 29, 31, 28}, + {19, 33, 6, 48}, + {21, 34, 4, 47}, + {24, 35, 1, 46} + }; + + // Циклы перестановок для верхней грани по часовой стрелке + static final int[][] U_CW = { + {33, 35, 40, 38}, + {34, 37, 39, 36}, + {25, 17, 9, 1}, + {26, 18, 10, 2}, + {27, 19, 11, 3} + }; + + // Циклы перестановок для нижней грани по часовой стрелке + static final int[][] D_CW = { + {41, 43, 48, 46}, + {42, 45, 47, 44}, + {6, 14, 22, 30}, + {7, 15, 23, 31}, + {8, 16, 24, 32} + }; + + // То же самое, но против часовой + static final int[][] U_CCW = reverseCycles(U_CW); + static final int[][] D_CCW = reverseCycles(D_CW); + static final int[][] L_CCW = reverseCycles(L_CW); + static final int[][] R_CCW = reverseCycles(R_CW); + static final int[][] F_CCW = reverseCycles(F_CW); + static final int[][] B_CCW = reverseCycles(B_CW); + + private static int[][] reverseCycles(int[][] cs) { + return Arrays.stream(cs) + .map(c -> { + final int cLen = c.length; + int[] rev = new int[cLen]; + rev[0] = c[0]; + IntStream.range(1, cLen).forEach(i -> rev[i] = c[cLen - i]); + return rev; + }) + .toArray(int[][]::new); + } +} diff --git a/src/main/java/hse/java/practice/task1/RubiksCube.java b/src/main/java/hse/java/practice/task1/RubiksCube.java index 2091b657..6fa9dda5 100644 --- a/src/main/java/hse/java/practice/task1/RubiksCube.java +++ b/src/main/java/hse/java/practice/task1/RubiksCube.java @@ -6,11 +6,15 @@ * Необходимо реализовать интерфейс Cube * При повороте передней грани, меняются верх низ право и лево */ -public class RubiksCube { +public class RubiksCube implements Cube{ - private static final int EDGES_COUNT = 6; + private static final int EDGES_COUNT = 6; + private static final int EDGE_SIZE = 3; + private static final int CENTER_INDEX = 5; + private static final int FIXED_ELEM = 8; private final Edge[] edges = new Edge[EDGES_COUNT]; + private final int[] states; /** * Создать валидный собранный кубик @@ -20,22 +24,92 @@ public class RubiksCube { * ... */ public RubiksCube() { - CubeColor[] colors = CubeColor.values(); - for (int i = 0; i < 6; i++) { - edges[i] = new Edge(colors[i]); - } + CubeColor[] colors = CubeColor.values(); + for (int i = 0; i < EDGES_COUNT; i++) edges[i] = new Edge(colors[i]); + + states = new int[Permutations.G_SIZE + 1]; + for (int i = 1; i < Permutations.G_SIZE + 1; i++) states[i] = i; } - public void front(RotateDirection direction) { + public Edge[] getEdges() { + for (int i = 0; i < EDGES_COUNT; i++) edges[Permutations.CURR_EDGES_ORDER[i]].setParts(buildEdge(i)); + return edges; + } + @Override + public void front(RotateDirection direction) { + applyCyclesToStates(direction == RotateDirection.CLOCKWISE ? Permutations.F_CW : Permutations.F_CCW); } - public Edge[] getEdges() { - return edges; + @Override + public void back(RotateDirection direction) { + applyCyclesToStates(direction == RotateDirection.CLOCKWISE ? Permutations.B_CW : Permutations.B_CCW); } @Override public String toString() { return Arrays.toString(edges); } + + @Override + public void left(RotateDirection direction) { + applyCyclesToStates(direction == RotateDirection.CLOCKWISE ? Permutations.L_CW : Permutations.L_CCW); + } + + @Override + public void right(RotateDirection direction) { + applyCyclesToStates(direction == RotateDirection.CLOCKWISE ? Permutations.R_CW : Permutations.R_CCW); + } + + @Override + public void up(RotateDirection direction) { + applyCyclesToStates(direction == RotateDirection.CLOCKWISE ? Permutations.U_CW : Permutations.U_CCW); + } + + @Override + public void down(RotateDirection direction) { + applyCyclesToStates(direction == RotateDirection.CLOCKWISE ? Permutations.D_CW : Permutations.D_CCW); + } + + private void applyCycleToState(int[] c) { + final int cLen = c.length; + int prev = states[c[cLen - 1]]; + for (int i = 0; i < cLen; i++) { + int cur = states[c[i]]; + states[c[i]] = prev; + prev = cur; + } + } + + private void applyCyclesToStates(int[][] cs) { + for (int[] c : cs) applyCycleToState(c); + } + + private static int toElemPos(int x, int y) { + int pos = x + y * EDGE_SIZE + 1; + return pos > CENTER_INDEX ? pos - 1 : pos; + } + + private static boolean isCenter(int x, int y) { + return x == 1 && y == 1; + } + + private CubeColor centerColor(int edgeIndex) { + return CubeColor.values()[Permutations.CURR_EDGES_ORDER[edgeIndex]]; + } + + private CubeColor elemColor(int edgeIndex, int pos) { + int stateIndex = states[edgeIndex * FIXED_ELEM + pos]; + int front = (stateIndex - 1) / FIXED_ELEM; + return CubeColor.values()[Permutations.CURR_EDGES_ORDER[front]]; + } + + private CubeColor[][] buildEdge(int edgeIndex) { + CubeColor[][] newEdge = new CubeColor[EDGE_SIZE][EDGE_SIZE]; + for (int y = 0; y < EDGE_SIZE; y++) { + for (int x = 0; x < EDGE_SIZE; x++) + newEdge[y][x] = isCenter(x, y) ? centerColor(edgeIndex) : elemColor(edgeIndex, toElemPos(x, y)); + } + return newEdge; + } }