From 08f0b8a0e97415251471cd262fba5dfde1c325cd Mon Sep 17 00:00:00 2001 From: LongCT_ Date: Thu, 9 Mar 2023 13:10:45 +0700 Subject: [PATCH 1/8] Switching to the decimal seed and deprecating the hexadecimal system. --- src/math/BaseConversionUtil.java | 11 +++++++ src/puzzle/Board.java | 53 ++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/math/BaseConversionUtil.java b/src/math/BaseConversionUtil.java index fab5425..cd21456 100644 --- a/src/math/BaseConversionUtil.java +++ b/src/math/BaseConversionUtil.java @@ -1,5 +1,7 @@ package math; +import java.util.ArrayList; + public class BaseConversionUtil { public static String hexToBin(String hex){ hex = hex.toLowerCase(); @@ -41,4 +43,13 @@ public static String getTinyBit(int value) { } throw new IllegalArgumentException("Cannot convert."); } + + public static int[] toBinaryArray(int x){ + int[] array = new int[32]; + for (int i = 31; i >= 0; i--) { + int mask = 1 << i; + array[31-i] = (x & mask) != 0 ? 1 : 0; + } + return array; + } } diff --git a/src/puzzle/Board.java b/src/puzzle/Board.java index d6fef06..b25cde8 100644 --- a/src/puzzle/Board.java +++ b/src/puzzle/Board.java @@ -45,11 +45,7 @@ public Board(String seed) { } public Board(int seed) { - if (seed < MIN_SEED || seed > MAX_SEED) { - throw new IllegalArgumentException("Seed must be in range of 0 and 8388607"); - } - String s = BaseConversionUtil.binToHex(Integer.toBinaryString(seed)); - parseSeed(s); + parseSeed(seed); } public int[][] getBoard() { @@ -376,7 +372,41 @@ private int[][] copyOfBoard(){ return newBoard; } - public void parseSeed(String seed) { + private void parseSeed(int seed){ + int[] bitArray = BaseConversionUtil.toBinaryArray(seed); + //first 9 bits are ignored + // 9-10 Rotation value + int rotationValue = bitArray[9] * 2 + bitArray[10] * 2; + // 11-12 Player Position + int px = bitArray[11] * 2 + 2; + int py = bitArray[12]; + board[px][py] = 2; + // 13 - 27 Board + int k = 13; + for (int x = 0; x < 4; x++) { + boolean rowOfPlayer = (px == x); + for (int y = 0; y < 4; y++) { + if (rowOfPlayer && py == y) { + continue; + } + board[x][y] = bitArray[k]; + k++; + } + } + //28-31 Goal + int gx = bitArray[28] * 2 + bitArray[29]; + int gy = bitArray[30] * 2 + bitArray[31]; + goal = new Vector2(gx, gy); + // Rotate + switch (rotationValue) { + case 1 -> rightRotate(); + case 2 -> rotate180(); + case 3 -> leftRotate(); + } + } + + @Deprecated + private void parseSeed(String seed) { // Check seed length if (seed.length() > 6) { throw new IllegalArgumentException("Seed length exceed 6."); @@ -386,6 +416,7 @@ public void parseSeed(String seed) { seedBuilder.insert(0, "0"); } while (seedBuilder.length() < 6); seed = seedBuilder.toString(); + } // Convert to binary String binarySeed = BaseConversionUtil.hexToBin(seed); @@ -490,4 +521,14 @@ public int getCoalCount(){ } return coal; } + + public static class BoardBuilder{ + int[][] board = new int[4][4]; + Vector2 goal = new Vector2(0,0); + Move lastMove; + + public BoardBuilder(){ + + } + } } From ecaeb91fb4ef8b1402306e1d99459abb9984c03d Mon Sep 17 00:00:00 2001 From: LongCT_ Date: Thu, 9 Mar 2023 13:26:33 +0700 Subject: [PATCH 2/8] Added BoardBuilder --- src/puzzle/Board.java | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/puzzle/Board.java b/src/puzzle/Board.java index b25cde8..36e9fe8 100644 --- a/src/puzzle/Board.java +++ b/src/puzzle/Board.java @@ -523,12 +523,35 @@ public int getCoalCount(){ } public static class BoardBuilder{ - int[][] board = new int[4][4]; - Vector2 goal = new Vector2(0,0); - Move lastMove; + private int[][] board = new int[4][4]; + private Vector2 goal; - public BoardBuilder(){ + public BoardBuilder(){} + public void setGoal(int x, int y){ + goal = new Vector2(x,y); + } + + public void setRow(int row, int[] args){ + int r = 4 - row; + for (int i = 0; i < 4; i++){ + board[r][i] = args[i]; + } + } + + public void setBoard(int[][] board){ + if (board.length != 4){ + throw new IllegalArgumentException("The argument for the board must be exactly 4x4"); + } + for (int[] row : board){ + if (row.length != 4){ + throw new IllegalArgumentException("The argument for the board must be exactly 4x4"); + } + } + this.board = board.clone(); + } + public Board build(){ + return new Board(board,goal); } } } From 6c0ce1d43942fc0f4a69ba3edbd35bffb01f19c4 Mon Sep 17 00:00:00 2001 From: LongCT_ Date: Thu, 9 Mar 2023 13:51:51 +0700 Subject: [PATCH 3/8] made getDecimalSeed into getSeed --- README.md | 28 +++++++++----- src/puzzle/Board.java | 90 +++++++++---------------------------------- 2 files changed, 36 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index f0ed2f1..85d4ffa 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ - # Sands Of Time's Piston Puzzle Solver A java program that can solve MCC's Sands Of Time's Piston Puzzle. What is the puzzle? Check out this [detailed document](https://docs.google.com/document/d/1ZbfKo57hn-H5eb_VkiYvJ5Ib-VdNfRtCjPyNy1HkyK8) made by edihau. @@ -7,16 +6,17 @@ What is the puzzle? Check out this [detailed document](https://docs.google.com/d Clone this project into your IDE of choice (I use IntelliJ for this project), then do what you want with it. Alternatively, you can download the .jar file in the releases page. Once downloaded: - - Open your console on your OS, then change your directory to the folder containing the downloaded jar. - - Run `java -jar PistonPuzzleSolver.jar (args)` - - For the arguments, supply an integer seed (range from 0 to 2^23 - 1, or 8388607), or multiple of them. The program will go through all of the provided seeds and either solve them or provide an Exception and skip it. - - Read below to see how seeds work. +- Open your console on your OS, then change your directory to the folder containing the downloaded jar. +- Run `java -jar PistonPuzzleSolver.jar (args)` +- For the arguments, supply an integer seed (range from 0 to 2^23 - 1, or 8388607), or multiple of them. The program will go through all of the provided seeds and either solve them or provide an Exception and skip it. +- Read below to see how seeds work. +- Alternatively, have your first argument be '-loop', and the program will instead wait for you to supply the seeds whilst running. It will end once you give it an empty input. -**Common Exceptions:** +**Common Exceptions:** - - java.lang.NumberFormatException: if you provided a seed that is not an integer. The code provides a way to use a 6-character hexadecimal seed for this, but it is not supported in the .jar file. - - java.lang.IllegalArgumentException: if you provided a seed that is out of the range given above. - - java.lang.RuntimeException: Should usually come with a message that said that you did not provide an argument. Happens when you run the jar file without any arguments. +- java.lang.NumberFormatException: if you provided a seed that is not an integer. The code provides a way to use a 6-character hexadecimal seed for this, but it is not supported in the .jar file. +- java.lang.IllegalArgumentException: if you provided a seed that is out of the range given above. +- java.lang.RuntimeException: Should usually come with a message that said that you did not provide an argument. Happens when you run the jar file without any arguments. ## What does the program print out after solving the puzzle? - If a valid solution is found, the program will print out an in depth step-by-step instruction on how to solve it, with what move to do, and what the board looks like after that move. - If none is found, the program will print out the provided board, with the message "No solutions" @@ -66,6 +66,14 @@ As an example, this is the board with the seed 100000. Try to convert the seed i | **.** | **.** | **.** | **.** | | **@** | **o** | **o** | **.** | | **o** | **.** | **o** | **.** | +## The Algorithm (WIP) ## Additional Infos (WIP) +## To-do List (WIP) +- Make a mod that does all of these. It can go with a map for practicing purposes. Ideally just a map, but I don't think mcfunctions alone can achieve randomly generated levels (that are solvable, at least). One way to achieve this is likely to load all levels template into that map, then use /clone and some random scoreboard functions to pick one, but there are A LOT of puzzles, and I'm not sure how viable that is. +- Alternatively, a web-based game/app for practicing could be achieved. +- A simple board-to-seed converter GUI for easier use. +- Improving the algorithms. And there are a lot to be improved. +## Note +This program is designed as a way to study and do research. Through the use of this, you can learn how to tackle a puzzle like this as a human, find potential strategy and common patterns. This is **NOT** a program made to be used as a mean of cheating inside an enviroment like the Minecraft Championship, or to be expanded into programs that enable such acts. Please do not use this for those purposes. ## License -Read [LICENSE](https://github.com/LongCTygo/sot-piston-puzzle/blob/master/LICENSE). +Read [LICENSE](https://github.com/LongCTygo/sot-piston-puzzle/blob/master/LICENSE). \ No newline at end of file diff --git a/src/puzzle/Board.java b/src/puzzle/Board.java index 36e9fe8..cc71651 100644 --- a/src/puzzle/Board.java +++ b/src/puzzle/Board.java @@ -56,15 +56,13 @@ public Vector2 getGoal() { return goal; } - - @Deprecated - public String getSeed() { + public int getSeed(){ Vector2 go = new Vector2(goal); - // 1 ignored - StringBuilder sb = new StringBuilder("0"); + int seed = 0; + // 0 ignored // 1-2 rotation value int rotationalValue = getRotationalValue(); - sb.append(getTinyBit(rotationalValue)); + seed += (int) Math.pow(2,21) * rotationalValue; // Rotate player back to III int[][] bo = null; switch (rotationalValue) { @@ -86,84 +84,32 @@ public String getSeed() { int px = -1; int py = -1; // 5 - 19 board - StringBuilder b = new StringBuilder(); + int k = 18; for (int x = 0; x < 4; x++) { for (int y = 0; y < 4; y++) { if (bo[x][y] == 2) { px = x; py = y; } else { - b.append(bo[x][y]); + seed += (int) Math.pow(2,k) * bo[x][y]; + k--; } } } - // throws ArrayIndexOutOfBoundException if board does not contain player - sb.append(px - 2); - sb.append(py); - sb.append(b); - // 20 - 23 goal - sb.append(getTinyBit(go.x)); - sb.append(getTinyBit(go.y)); - //Seed - String binarySeed = sb.toString(); - StringBuilder hexSeed = new StringBuilder(BaseConversionUtil.binToHex(binarySeed)); - while (hexSeed.length() < 6) { - hexSeed.insert(0, "0"); + // throws IllegalStateException if board does not contain player, or multiple players + if (px == -1 && py == -1){ + throw new IllegalStateException("The board does not have a player.\n" + this); } - return hexSeed.toString(); - } - - public int getDecimalSeed(){ - Vector2 go = new Vector2(goal); - // 1 ignored - StringBuilder sb = new StringBuilder("0"); - // 1-2 rotation value - int rotationalValue = getRotationalValue(); - sb.append(getTinyBit(rotationalValue)); - // Rotate player back to III - int[][] bo = null; - switch (rotationalValue) { - case 0: - bo = copyOfBoard(); - break; - case 1: - bo = leftR(); - go.leftRotateBoard(); - break; - case 2: - bo = fullR(); - go.fullRotateBoard(); - break; - case 3: - bo = rightR(); - go.rightRotateBoard(); - break; - } - // 3-4 player location - int px = -1; - int py = -1; - // 5 - 19 board - StringBuilder b = new StringBuilder(); - for (int x = 0; x < 4; x++) { - for (int y = 0; y < 4; y++) { - if (bo[x][y] == 2) { - px = x; - py = y; - } else { - b.append(bo[x][y]); - } - } + if (k != 3){ + throw new IllegalStateException("The board has more than one player.\n" + this); } - // throws ArrayIndexOutOfBoundException if board does not contain player - sb.append(px - 2); - sb.append(py); - sb.append(b); + seed += (int) Math.pow(2,20) * (px -2) + + (int) Math.pow(2,19) * (py); // 20 - 23 goal - sb.append(getTinyBit(go.x)); - sb.append(getTinyBit(go.y)); + seed += 4 * go.x; + seed += 1 * go.y; //Seed - String binarySeed = sb.toString(); - return Integer.parseInt(binarySeed,2); + return seed; } @@ -378,7 +324,7 @@ private void parseSeed(int seed){ // 9-10 Rotation value int rotationValue = bitArray[9] * 2 + bitArray[10] * 2; // 11-12 Player Position - int px = bitArray[11] * 2 + 2; + int px = bitArray[11] + 2; int py = bitArray[12]; board[px][py] = 2; // 13 - 27 Board From a010ee85d079c4905892cdd48a3d2dba67801d38 Mon Sep 17 00:00:00 2001 From: LongCT_ Date: Thu, 9 Mar 2023 13:59:24 +0700 Subject: [PATCH 4/8] Update README.md --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 85d4ffa..571ba84 100644 --- a/README.md +++ b/README.md @@ -8,22 +8,22 @@ Alternatively, you can download the .jar file in the releases page. Once downloa - Open your console on your OS, then change your directory to the folder containing the downloaded jar. - Run `java -jar PistonPuzzleSolver.jar (args)` -- For the arguments, supply an integer seed (range from 0 to 2^23 - 1, or 8388607), or multiple of them. The program will go through all of the provided seeds and either solve them or provide an Exception and skip it. +- For the arguments, supply an integer seed (range from 0 to 2^23 - 1, or 8388607), or multiple of them. The program will go through all the provided seeds and either solve them or provide an Exception and skip it. - Read below to see how seeds work. - Alternatively, have your first argument be '-loop', and the program will instead wait for you to supply the seeds whilst running. It will end once you give it an empty input. **Common Exceptions:** -- java.lang.NumberFormatException: if you provided a seed that is not an integer. The code provides a way to use a 6-character hexadecimal seed for this, but it is not supported in the .jar file. -- java.lang.IllegalArgumentException: if you provided a seed that is out of the range given above. +- java.lang.NumberFormatException: if you provided a seed that is not an integer. +- java.lang.IllegalArgumentException: if you provided a seed that is out of the range given above. This should not happen in the latest release, as the program now accept full 32-bit integer as a valid seed. The program will still only use the 23 bits it needs. - java.lang.RuntimeException: Should usually come with a message that said that you did not provide an argument. Happens when you run the jar file without any arguments. ## What does the program print out after solving the puzzle? - If a valid solution is found, the program will print out an in depth step-by-step instruction on how to solve it, with what move to do, and what the board looks like after that move. - If none is found, the program will print out the provided board, with the message "No solutions" - The '@' represents the player, 'o' represents a box, 'x' represents an empty goal, or it will be '#' instead if something is occupying that spot. (Either a box, or the player, the latter means it is solved). -- Additionally, its runtime in miliseconds is also provided. +- Additionally, its runtime in milliseconds is also provided. ## How does the seed system work? -**WARNING: JANKY EXPLAINATIONS AND BROKEN ENGLISH DOWN BELOW** +#### **WARNING: INCOMPLETE EXPLANATIONS AND BROKEN ENGLISH DOWN BELOW** The seed is a 24-bit unsigned integer (0 - 2^23 - 1). Out of the 24 bits, only 23 are used, the first (left-most) bit is ignored. The seed can be either stored as just an integer, or a 6-characters hexadecimal seed. **For example:** @@ -32,15 +32,15 @@ The seed is a 24-bit unsigned integer (0 - 2^23 - 1). Out of the 24 bits, only 2 244532 in decimal -In binary they should look like this +In binary, they should look like this 0000 0011 1011 1011 0011 0100 -Let's go through all of the bits one by one. Remember, the first bit is ignored. +Let's go through all the bits one by one. Remember, the first bit is ignored. 0**00**0 0011 1011 1011 0011 0100 **Bit 2 & 3** -The 2nd and 3rd left-most bit are used to store the *rotation value* of the board. If the value of these 2 bits are both 0, then the position of the player is always inside the III quarter of the board (the bottom-left quarter). These 2 bits represent how many time the board should be rotated *counter-clockwise* to reach the position where the player is in that quarter. +The 2nd and 3rd left-most bit are used to store the *rotation value* of the board. If the value of these 2 bits are both 0, then the position of the player is always inside the III quarter of the board (the bottom-left quarter). These 2 bits represent how many times the board should be rotated *counter-clockwise* to reach the position where the player is in that quarter. When generating the board, the board will generate a board with the player in the bottom-left quarter, then rotate it clockwise that many times to get the board of that seed. 000**0 0**011 1011 1011 0011 0100 **Bit 4 & 5** @@ -53,12 +53,12 @@ These two bits represent the position of the player. As the player is always lo 0000 0**011 1011 1011 0011** 0100 **Bit 6 - 20** These 15 bits represent the remaining 15 slots on the board. A 0 represents that that slot is empty, while a 1 represents that a *box* is inside that slot. -Similar to the player, it starts at the top left corner of the **board**, going to the right of the row, then to the next row. It skips the slot the player is in. +Similar to the player, it starts in the top left corner of the **board**, going to the right of the row, then to the next row. It skips the slot the player is in. 0000 0011 1011 1011 0011 **0100** **Bit 21 - 24** -The last 4 bits are the location of the goal. It starts at the top left corner of the **board**, going to the right of the row, then to the next row, like the two above. +The last 4 bits are the location of the goal. It starts in the top left corner of the **board**, going to the right of the row, then to the next row, like the two above. As an example, this is the board with the seed 100000. Try to convert the seed into a board. | x | . | o | o | @@ -67,13 +67,13 @@ As an example, this is the board with the seed 100000. Try to convert the seed i | **@** | **o** | **o** | **.** | | **o** | **.** | **o** | **.** | ## The Algorithm (WIP) -## Additional Infos (WIP) +## Additional Info (WIP) ## To-do List (WIP) - Make a mod that does all of these. It can go with a map for practicing purposes. Ideally just a map, but I don't think mcfunctions alone can achieve randomly generated levels (that are solvable, at least). One way to achieve this is likely to load all levels template into that map, then use /clone and some random scoreboard functions to pick one, but there are A LOT of puzzles, and I'm not sure how viable that is. - Alternatively, a web-based game/app for practicing could be achieved. - A simple board-to-seed converter GUI for easier use. - Improving the algorithms. And there are a lot to be improved. ## Note -This program is designed as a way to study and do research. Through the use of this, you can learn how to tackle a puzzle like this as a human, find potential strategy and common patterns. This is **NOT** a program made to be used as a mean of cheating inside an enviroment like the Minecraft Championship, or to be expanded into programs that enable such acts. Please do not use this for those purposes. +This program is designed as a way to study and do research. Through the use of this, you can learn how to tackle a puzzle like this as a human, find potential strategy and common patterns. This is **NOT** a program made to be used as a mean of cheating inside an environment like the Minecraft Championship, or to be expanded into programs that enable such acts. Please do not use this for those purposes. ## License Read [LICENSE](https://github.com/LongCTygo/sot-piston-puzzle/blob/master/LICENSE). \ No newline at end of file From b4abd3f694a3f754ea3abb511ae9e17a06df3084 Mon Sep 17 00:00:00 2001 From: LongCT_ Date: Fri, 10 Mar 2023 19:19:06 +0700 Subject: [PATCH 5/8] Put all packages into net.longct.pistonsolver --- .gitignore | 2 +- .idea/uiDesigner.xml | 124 ++++++++++++++++++ README.md | 6 +- src/META-INF/MANIFEST.MF | 2 +- src/{ => net/longct/pistonsolver}/Main.java | 7 +- .../pistonsolver}/iterate/AllPuzzlesMain.java | 8 +- .../math/BaseConversionUtil.java | 4 +- .../longct/pistonsolver}/math/Vector2.java | 2 +- .../longct/pistonsolver}/puzzle/Board.java | 8 +- .../longct/pistonsolver}/puzzle/Move.java | 2 +- .../longct/pistonsolver}/solver/Node.java | 14 +- .../pistonsolver}/solver/SolutionTree.java | 2 +- 12 files changed, 151 insertions(+), 30 deletions(-) create mode 100644 .idea/uiDesigner.xml rename src/{ => net/longct/pistonsolver}/Main.java (90%) rename src/{ => net/longct/pistonsolver}/iterate/AllPuzzlesMain.java (92%) rename src/{ => net/longct/pistonsolver}/math/BaseConversionUtil.java (97%) rename src/{ => net/longct/pistonsolver}/math/Vector2.java (98%) rename src/{ => net/longct/pistonsolver}/puzzle/Board.java (98%) rename src/{ => net/longct/pistonsolver}/puzzle/Move.java (95%) rename src/{ => net/longct/pistonsolver}/solver/Node.java (94%) rename src/{ => net/longct/pistonsolver}/solver/SolutionTree.java (98%) diff --git a/.gitignore b/.gitignore index 081439c..d9091ea 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,4 @@ bin/ ### Mac OS ### .DS_Store /solutions/ -/src/test/ +/src/net/longct/pistonsolver/test/ diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 571ba84..20d0c48 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Sands Of Time's Piston Puzzle Solver A java program that can solve MCC's Sands Of Time's Piston Puzzle. -What is the puzzle? Check out this [detailed document](https://docs.google.com/document/d/1ZbfKo57hn-H5eb_VkiYvJ5Ib-VdNfRtCjPyNy1HkyK8) made by edihau. +What is the net.longct.pistonsolver.puzzle? Check out this [detailed document](https://docs.google.com/document/d/1ZbfKo57hn-H5eb_VkiYvJ5Ib-VdNfRtCjPyNy1HkyK8) made by edihau. ## How do I use this? Clone this project into your IDE of choice (I use IntelliJ for this project), then do what you want with it. @@ -17,7 +17,7 @@ Alternatively, you can download the .jar file in the releases page. Once downloa - java.lang.NumberFormatException: if you provided a seed that is not an integer. - java.lang.IllegalArgumentException: if you provided a seed that is out of the range given above. This should not happen in the latest release, as the program now accept full 32-bit integer as a valid seed. The program will still only use the 23 bits it needs. - java.lang.RuntimeException: Should usually come with a message that said that you did not provide an argument. Happens when you run the jar file without any arguments. -## What does the program print out after solving the puzzle? +## What does the program print out after solving the net.longct.pistonsolver.puzzle? - If a valid solution is found, the program will print out an in depth step-by-step instruction on how to solve it, with what move to do, and what the board looks like after that move. - If none is found, the program will print out the provided board, with the message "No solutions" - The '@' represents the player, 'o' represents a box, 'x' represents an empty goal, or it will be '#' instead if something is occupying that spot. (Either a box, or the player, the latter means it is solved). @@ -74,6 +74,6 @@ As an example, this is the board with the seed 100000. Try to convert the seed i - A simple board-to-seed converter GUI for easier use. - Improving the algorithms. And there are a lot to be improved. ## Note -This program is designed as a way to study and do research. Through the use of this, you can learn how to tackle a puzzle like this as a human, find potential strategy and common patterns. This is **NOT** a program made to be used as a mean of cheating inside an environment like the Minecraft Championship, or to be expanded into programs that enable such acts. Please do not use this for those purposes. +This program is designed as a way to study and do research. Through the use of this, you can learn how to tackle a net.longct.pistonsolver.puzzle like this as a human, find potential strategy and common patterns. This is **NOT** a program made to be used as a mean of cheating inside an environment like the Minecraft Championship, or to be expanded into programs that enable such acts. Please do not use this for those purposes. ## License Read [LICENSE](https://github.com/LongCTygo/sot-piston-puzzle/blob/master/LICENSE). \ No newline at end of file diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF index 5ee19cb..e88cfcd 100644 --- a/src/META-INF/MANIFEST.MF +++ b/src/META-INF/MANIFEST.MF @@ -1,3 +1,3 @@ Manifest-Version: 1.0 -Main-Class: Main +Main-Class: net.longct.pistonsolver.Main diff --git a/src/Main.java b/src/net/longct/pistonsolver/Main.java similarity index 90% rename from src/Main.java rename to src/net/longct/pistonsolver/Main.java index f38e19f..2fccf53 100644 --- a/src/Main.java +++ b/src/net/longct/pistonsolver/Main.java @@ -1,7 +1,8 @@ +package net.longct.pistonsolver; -import puzzle.Board; -import solver.Node; -import solver.SolutionTree; +import net.longct.pistonsolver.puzzle.Board; +import net.longct.pistonsolver.solver.Node; +import net.longct.pistonsolver.solver.SolutionTree; import java.util.Scanner; diff --git a/src/iterate/AllPuzzlesMain.java b/src/net/longct/pistonsolver/iterate/AllPuzzlesMain.java similarity index 92% rename from src/iterate/AllPuzzlesMain.java rename to src/net/longct/pistonsolver/iterate/AllPuzzlesMain.java index c5fc6fc..b2857a2 100644 --- a/src/iterate/AllPuzzlesMain.java +++ b/src/net/longct/pistonsolver/iterate/AllPuzzlesMain.java @@ -1,8 +1,8 @@ -package iterate; +package net.longct.pistonsolver.iterate; -import puzzle.Board; -import solver.Node; -import solver.SolutionTree; +import net.longct.pistonsolver.puzzle.Board; +import net.longct.pistonsolver.solver.Node; +import net.longct.pistonsolver.solver.SolutionTree; import java.io.BufferedWriter; import java.io.File; diff --git a/src/math/BaseConversionUtil.java b/src/net/longct/pistonsolver/math/BaseConversionUtil.java similarity index 97% rename from src/math/BaseConversionUtil.java rename to src/net/longct/pistonsolver/math/BaseConversionUtil.java index cd21456..7b73e68 100644 --- a/src/math/BaseConversionUtil.java +++ b/src/net/longct/pistonsolver/math/BaseConversionUtil.java @@ -1,6 +1,4 @@ -package math; - -import java.util.ArrayList; +package net.longct.pistonsolver.math; public class BaseConversionUtil { public static String hexToBin(String hex){ diff --git a/src/math/Vector2.java b/src/net/longct/pistonsolver/math/Vector2.java similarity index 98% rename from src/math/Vector2.java rename to src/net/longct/pistonsolver/math/Vector2.java index 4b61496..4248ea5 100644 --- a/src/math/Vector2.java +++ b/src/net/longct/pistonsolver/math/Vector2.java @@ -1,4 +1,4 @@ -package math; +package net.longct.pistonsolver.math; public class Vector2 { public int x; diff --git a/src/puzzle/Board.java b/src/net/longct/pistonsolver/puzzle/Board.java similarity index 98% rename from src/puzzle/Board.java rename to src/net/longct/pistonsolver/puzzle/Board.java index cc71651..6694cfd 100644 --- a/src/puzzle/Board.java +++ b/src/net/longct/pistonsolver/puzzle/Board.java @@ -1,12 +1,10 @@ -package puzzle; +package net.longct.pistonsolver.puzzle; -import math.BaseConversionUtil; -import math.Vector2; +import net.longct.pistonsolver.math.BaseConversionUtil; +import net.longct.pistonsolver.math.Vector2; import java.util.Arrays; -import static math.BaseConversionUtil.getTinyBit; - public class Board { public static final int MIN_SEED = 0; public static final int MAX_SEED = 8388607; diff --git a/src/puzzle/Move.java b/src/net/longct/pistonsolver/puzzle/Move.java similarity index 95% rename from src/puzzle/Move.java rename to src/net/longct/pistonsolver/puzzle/Move.java index fa9a470..2a323fa 100644 --- a/src/puzzle/Move.java +++ b/src/net/longct/pistonsolver/puzzle/Move.java @@ -1,4 +1,4 @@ -package puzzle; +package net.longct.pistonsolver.puzzle; public class Move { diff --git a/src/solver/Node.java b/src/net/longct/pistonsolver/solver/Node.java similarity index 94% rename from src/solver/Node.java rename to src/net/longct/pistonsolver/solver/Node.java index d706aa7..5ce9ec0 100644 --- a/src/solver/Node.java +++ b/src/net/longct/pistonsolver/solver/Node.java @@ -1,14 +1,14 @@ -package solver; +package net.longct.pistonsolver.solver; -import puzzle.Board; -import puzzle.Move; +import net.longct.pistonsolver.puzzle.Board; +import net.longct.pistonsolver.puzzle.Move; +import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Stack; /** * - * @author ADMIN + * @author LongCT_ */ public class Node { final double SMART_LIMIT = 1.5; @@ -132,8 +132,8 @@ public void createChildren(boolean ignoreBadMoves){ } public void printSolutions(){ - Stack stack1 = new Stack<>(); - Stack stack2 = new Stack<>(); + ArrayDeque stack1 = new ArrayDeque<>(); + ArrayDeque stack2 = new ArrayDeque<>(); Node p = this; while (p!=null){ stack1.push(p.board); diff --git a/src/solver/SolutionTree.java b/src/net/longct/pistonsolver/solver/SolutionTree.java similarity index 98% rename from src/solver/SolutionTree.java rename to src/net/longct/pistonsolver/solver/SolutionTree.java index 5a8caad..95505ca 100644 --- a/src/solver/SolutionTree.java +++ b/src/net/longct/pistonsolver/solver/SolutionTree.java @@ -1,4 +1,4 @@ -package solver; +package net.longct.pistonsolver.solver; import java.util.ArrayDeque; import java.util.ArrayList; From d2451d6cead9fd010bc38e79377cfed040b4f286 Mon Sep 17 00:00:00 2001 From: LongCT_ Date: Mon, 13 Mar 2023 07:37:04 +0700 Subject: [PATCH 6/8] De-referencing children nodes to hopefully reduce memory usage --- PistonPuzzleSolver.iml | 1 + src/net/longct/pistonsolver/solver/Node.java | 5 +++-- src/net/longct/pistonsolver/solver/SolutionTree.java | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/PistonPuzzleSolver.iml b/PistonPuzzleSolver.iml index c90834f..b5d0958 100644 --- a/PistonPuzzleSolver.iml +++ b/PistonPuzzleSolver.iml @@ -7,5 +7,6 @@ + \ No newline at end of file diff --git a/src/net/longct/pistonsolver/solver/Node.java b/src/net/longct/pistonsolver/solver/Node.java index 5ce9ec0..00e73ba 100644 --- a/src/net/longct/pistonsolver/solver/Node.java +++ b/src/net/longct/pistonsolver/solver/Node.java @@ -5,6 +5,7 @@ import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Stack; /** * @@ -132,8 +133,8 @@ public void createChildren(boolean ignoreBadMoves){ } public void printSolutions(){ - ArrayDeque stack1 = new ArrayDeque<>(); - ArrayDeque stack2 = new ArrayDeque<>(); + Stack stack1 = new Stack<>(); + Stack stack2 = new Stack<>(); Node p = this; while (p!=null){ stack1.push(p.board); diff --git a/src/net/longct/pistonsolver/solver/SolutionTree.java b/src/net/longct/pistonsolver/solver/SolutionTree.java index 95505ca..355592e 100644 --- a/src/net/longct/pistonsolver/solver/SolutionTree.java +++ b/src/net/longct/pistonsolver/solver/SolutionTree.java @@ -57,6 +57,8 @@ public int solveBFS(int maxDepth, int maxSearch, boolean ignoreBadMoves, boolean queue.add(c); } } + //De-referencing children nodes + p.children.clear(); } else{ if (printSol) p.printSolutions(); return result; From 8aaed4c88d9fb4f2ce83ef9589fa4761348a7954 Mon Sep 17 00:00:00 2001 From: LongCT_ Date: Mon, 13 Mar 2023 07:47:18 +0700 Subject: [PATCH 7/8] Changed solving method return type to a Node chain. --- src/net/longct/pistonsolver/Main.java | 12 +++++- .../pistonsolver/solver/SolutionTree.java | 42 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/net/longct/pistonsolver/Main.java b/src/net/longct/pistonsolver/Main.java index 2fccf53..84ecb14 100644 --- a/src/net/longct/pistonsolver/Main.java +++ b/src/net/longct/pistonsolver/Main.java @@ -39,8 +39,16 @@ public static void solve(String seed){ System.out.printf("------------------- Seed '%s' -------------------\n", seed); try { int s = Integer.parseInt(seed); - SolutionTree st = new SolutionTree(new Node(new Board(s))); - st.solveBFS(20,10000000,true,true); + Board board = new Board(s); + SolutionTree st = new SolutionTree(new Node(board)); +// st.solveBFS(20,10000000,true,true); + Node node = st.solveBFS(20, 10000000, true); + if (node != null){ + node.printSolutions(); + } else { + System.out.println(board); + System.out.println("No solutions"); + } System.out.println(); } catch (Exception ex){ System.err.printf("Exception Caught for argument %s: %s\n",seed, ex.getClass().getName()); diff --git a/src/net/longct/pistonsolver/solver/SolutionTree.java b/src/net/longct/pistonsolver/solver/SolutionTree.java index 355592e..f625676 100644 --- a/src/net/longct/pistonsolver/solver/SolutionTree.java +++ b/src/net/longct/pistonsolver/solver/SolutionTree.java @@ -19,6 +19,7 @@ public boolean isEmpty(){ return root == null; } + @Deprecated public int solveBFS(int maxDepth, int maxSearch, boolean ignoreBadMoves, boolean printSol) { ArrayDeque queue = new ArrayDeque<>(); ArrayList bestDist = new ArrayList<>(); @@ -70,4 +71,45 @@ public int solveBFS(int maxDepth, int maxSearch, boolean ignoreBadMoves, boolean } return -1; } + + public Node solveBFS(int maxDepth, int maxSearch, boolean ignoreBadMoves){ + ArrayDeque queue = new ArrayDeque<>(); + ArrayList bestDist = new ArrayList<>(); + + bestDist.add(root.board.getDistance()); + int currentDepth = 0; + int currentProcess = 0; + queue.add(root); + while (!queue.isEmpty()){ + Node p = queue.pop(); + currentProcess++; + if (currentProcess > maxSearch) { + return null; + } + int depthP = p.depth(); + double distP = p.board.getDistance(); + if (depthP != currentDepth){ + currentDepth = depthP; + bestDist.add(distP); + } else if (distP < bestDist.get(depthP)){ + bestDist.set(depthP, distP); + } + int result = p.breadthSolve(maxDepth,depthP,distP); + if (result == -2){ + break; + } else if (result == -1){ + p.createChildren(ignoreBadMoves); + for (Node c : p.children){ + if (depthP <= 3 || c.board.getDistance() < bestDist.get(depthP - 4)){ + queue.add(c); + } + } + //De-referencing children nodes + p.children.clear(); + } else{ + return p; + } + } + return null; + } } From c43df40bb6885b4978e67d2f2ed3f06b09a392ba Mon Sep 17 00:00:00 2001 From: LongCT_ Date: Mon, 13 Mar 2023 08:11:51 +0700 Subject: [PATCH 8/8] Updated README.md --- PistonPuzzleSolver.iml | 1 + README.md | 15 +++++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/PistonPuzzleSolver.iml b/PistonPuzzleSolver.iml index b5d0958..9be9ef4 100644 --- a/PistonPuzzleSolver.iml +++ b/PistonPuzzleSolver.iml @@ -4,6 +4,7 @@ + diff --git a/README.md b/README.md index 20d0c48..e3c17c3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Sands Of Time's Piston Puzzle Solver A java program that can solve MCC's Sands Of Time's Piston Puzzle. -What is the net.longct.pistonsolver.puzzle? Check out this [detailed document](https://docs.google.com/document/d/1ZbfKo57hn-H5eb_VkiYvJ5Ib-VdNfRtCjPyNy1HkyK8) made by edihau. +What is the puzzle? Check out this [detailed document](https://docs.google.com/document/d/1ZbfKo57hn-H5eb_VkiYvJ5Ib-VdNfRtCjPyNy1HkyK8) made by edihau. ## How do I use this? Clone this project into your IDE of choice (I use IntelliJ for this project), then do what you want with it. @@ -17,7 +17,7 @@ Alternatively, you can download the .jar file in the releases page. Once downloa - java.lang.NumberFormatException: if you provided a seed that is not an integer. - java.lang.IllegalArgumentException: if you provided a seed that is out of the range given above. This should not happen in the latest release, as the program now accept full 32-bit integer as a valid seed. The program will still only use the 23 bits it needs. - java.lang.RuntimeException: Should usually come with a message that said that you did not provide an argument. Happens when you run the jar file without any arguments. -## What does the program print out after solving the net.longct.pistonsolver.puzzle? +## What does the program print out after solving the puzzle? - If a valid solution is found, the program will print out an in depth step-by-step instruction on how to solve it, with what move to do, and what the board looks like after that move. - If none is found, the program will print out the provided board, with the message "No solutions" - The '@' represents the player, 'o' represents a box, 'x' represents an empty goal, or it will be '#' instead if something is occupying that spot. (Either a box, or the player, the latter means it is solved). @@ -28,11 +28,7 @@ Alternatively, you can download the .jar file in the releases page. Once downloa The seed is a 24-bit unsigned integer (0 - 2^23 - 1). Out of the 24 bits, only 23 are used, the first (left-most) bit is ignored. The seed can be either stored as just an integer, or a 6-characters hexadecimal seed. **For example:** -03BB34 in hex - -244532 in decimal - -In binary, they should look like this +The seed 244532 in binary is: 0000 0011 1011 1011 0011 0100 @@ -67,6 +63,9 @@ As an example, this is the board with the seed 100000. Try to convert the seed i | **@** | **o** | **o** | **.** | | **o** | **.** | **o** | **.** | ## The Algorithm (WIP) +The algorithm aims to solve the puzzle with a breadth-first search approach. Starting from the root, it generates all possible board states, then decide whether the board is 'on the right track' and add it to the queue, or else it will ignore that board. + +From a board, it will try out all 16 different moves, ignoring moves that does not advance the board state. From there, it determines if a move is advantageous or not, and continue from there. ## Additional Info (WIP) ## To-do List (WIP) - Make a mod that does all of these. It can go with a map for practicing purposes. Ideally just a map, but I don't think mcfunctions alone can achieve randomly generated levels (that are solvable, at least). One way to achieve this is likely to load all levels template into that map, then use /clone and some random scoreboard functions to pick one, but there are A LOT of puzzles, and I'm not sure how viable that is. @@ -74,6 +73,6 @@ As an example, this is the board with the seed 100000. Try to convert the seed i - A simple board-to-seed converter GUI for easier use. - Improving the algorithms. And there are a lot to be improved. ## Note -This program is designed as a way to study and do research. Through the use of this, you can learn how to tackle a net.longct.pistonsolver.puzzle like this as a human, find potential strategy and common patterns. This is **NOT** a program made to be used as a mean of cheating inside an environment like the Minecraft Championship, or to be expanded into programs that enable such acts. Please do not use this for those purposes. +This program is designed as a way to study and do research. Through the use of this, you can learn how to tackle a puzzle like this as a human, find potential strategy and common patterns. This is **NOT** a program made to be used as a mean of cheating inside an environment like the Minecraft Championship, or to be expanded into programs that enable such acts. Please do not use this for those purposes. ## License Read [LICENSE](https://github.com/LongCTygo/sot-piston-puzzle/blob/master/LICENSE). \ No newline at end of file