From a879934fc88b0dd767aa65c9665dacc53fe54388 Mon Sep 17 00:00:00 2001 From: Michael Henke Date: Sun, 16 Sep 2018 22:50:10 +0200 Subject: [PATCH] speedup of "solution01 solver mode" We store as KnownStates the positions of the other (non-goal) robots, in order to avoid searching their positions multiple times. The goal robot can still drift on its sub-optimal path which it has to take, because we don't store its positions in KnownStates. fixes issue #12 --- .../model/KeyDepthMapTrieSpecial.java | 5 +- src/driftingdroids/model/KeyMakerInt.java | 42 ++++++++++--- src/driftingdroids/model/KeyMakerLong.java | 61 +++++++++++++++++-- src/driftingdroids/model/SolverIDDFS.java | 17 +++--- src/driftingdroids/ui/Starter.java | 2 +- 5 files changed, 104 insertions(+), 23 deletions(-) diff --git a/src/driftingdroids/model/KeyDepthMapTrieSpecial.java b/src/driftingdroids/model/KeyDepthMapTrieSpecial.java index 1ec5a43..0f676de 100644 --- a/src/driftingdroids/model/KeyDepthMapTrieSpecial.java +++ b/src/driftingdroids/model/KeyDepthMapTrieSpecial.java @@ -104,8 +104,9 @@ private KeyDepthMapTrieSpecial(final Board board) { this.elementLookup[i] = Integer.MIN_VALUE; } } - this.nodeNumber = board.getNumRobots() - 1; - this.nodeNumberUnCompr = (board.getNumRobots()*board.sizeNumBits + 8 - 31 + (board.sizeNumBits - 1)) / board.sizeNumBits; + final int numRobots = board.getNumRobots() - (board.isSolution01() ? 1 : 0); + this.nodeNumber = numRobots - 1; + this.nodeNumberUnCompr = (numRobots*board.sizeNumBits + 8 - 31 + (board.sizeNumBits - 1)) / board.sizeNumBits; this.nodeShift = board.sizeNumBits; this.nodeMask = (1 << board.sizeNumBits) - 1; diff --git a/src/driftingdroids/model/KeyMakerInt.java b/src/driftingdroids/model/KeyMakerInt.java index b23f91c..80a3ea7 100644 --- a/src/driftingdroids/model/KeyMakerInt.java +++ b/src/driftingdroids/model/KeyMakerInt.java @@ -46,14 +46,38 @@ public static KeyMakerInt createInstance(int boardNumRobots, int boardSizeNumBit switch (boardNumRobots) { case 1: keyMaker = new KeyMakerInt11(); break; case 2: keyMaker = (isBoardGoalWildcard ? new KeyMakerInt22(boardSizeNumBits) : new KeyMakerInt21(boardSizeNumBits)); break; - case 3: keyMaker = (isBoardGoalWildcard ? new KeyMakerInt33(boardSizeNumBits) : new KeyMakerInt32(boardSizeNumBits)); break; - case 4: keyMaker = (isBoardGoalWildcard ? new KeyMakerInt44(boardSizeNumBits) : new KeyMakerInt43(boardSizeNumBits)); break; + case 3: keyMaker = (isBoardGoalWildcard ? new KeyMakerInt33(boardSizeNumBits, 3) : new KeyMakerInt32(boardSizeNumBits)); break; + case 4: keyMaker = (isBoardGoalWildcard ? new KeyMakerInt44(boardSizeNumBits, 4) : new KeyMakerInt43(boardSizeNumBits)); break; default: keyMaker = new KeyMakerIntAll(boardNumRobots, boardSizeNumBits, isBoardGoalWildcard); } return keyMaker; } + /** + * Creates an instance of KeyMakerInt that is tailored to the given parameters. + * + * @param boardNumRobots number of robots on the board (length of parameter state of method run) + * @param boardSizeNumBits number of bits required to store the size of the board (the 16x16 board need 8 bits) + * @param isBoardGoalWildcard true if the current goal is a wildcard goal (can be reached by any robot) + * @param isSolution01 true if the active robot can reach the goal in one move + * @return the instance of KeyMakerInt created + */ + public static KeyMakerInt createInstance(int boardNumRobots, int boardSizeNumBits, boolean isBoardGoalWildcard, boolean isSolution01) { + final KeyMakerInt keyMaker; + if (isSolution01) { + switch (boardNumRobots) { + case 4: keyMaker = new KeyMakerInt33(boardSizeNumBits, 4); break; + case 5: keyMaker = new KeyMakerInt44(boardSizeNumBits, 5); break; + default: keyMaker = null; // other numbers of robots are not supported! + } + } else { + keyMaker = createInstance(boardNumRobots, boardSizeNumBits, isBoardGoalWildcard); + } + return keyMaker; + } + + private static final class KeyMakerIntAll extends KeyMakerInt { private final int[] tmpState; private final int idxSort, idxLen1, idxLen2; @@ -143,14 +167,15 @@ public final int run(final int[] state) { private static final class KeyMakerInt33 extends KeyMakerInt { //sort 3 of 3 elements - private final int s1, s2; - private KeyMakerInt33(int boardSizeNumBits) { + private final int s1, s2, len; + private KeyMakerInt33(int boardSizeNumBits, int len) { + this.len = len; this.s1 = boardSizeNumBits; this.s2 = boardSizeNumBits * 2; } @Override public final int run(final int[] state) { - assert 3 == state.length : state.length; + assert this.len == state.length : state.length; final int a = state[0], b = state[1], c = state[2]; final int result; if (a < b) { @@ -198,15 +223,16 @@ public final int run(final int[] state) { private static final class KeyMakerInt44 extends KeyMakerInt { //sort 4 of 4 elements - private final int s1, s2, s3; - private KeyMakerInt44(int boardSizeNumBits) { + private final int s1, s2, s3, len; + private KeyMakerInt44(int boardSizeNumBits, int len) { + this.len = len; this.s1 = boardSizeNumBits; this.s2 = boardSizeNumBits * 2; this.s3 = boardSizeNumBits * 3; } @Override public final int run(final int[] state) { - assert 4 == state.length : state.length; + assert this.len == state.length : state.length; final int a = state[0], b = state[1], c = state[2], d = state[3]; final int result; if (a <= b) { diff --git a/src/driftingdroids/model/KeyMakerLong.java b/src/driftingdroids/model/KeyMakerLong.java index d88882e..b59f6c9 100644 --- a/src/driftingdroids/model/KeyMakerLong.java +++ b/src/driftingdroids/model/KeyMakerLong.java @@ -44,7 +44,7 @@ public abstract class KeyMakerLong { public static KeyMakerLong createInstance(int boardNumRobots, int boardSizeNumBits, boolean isBoardGoalWildcard) { final KeyMakerLong keyMaker; switch (boardNumRobots) { - case 4: keyMaker = (isBoardGoalWildcard ? new KeyMakerLong44(boardSizeNumBits) : new KeyMakerLong43(boardSizeNumBits)); break; + case 4: keyMaker = (isBoardGoalWildcard ? new KeyMakerLong44(boardSizeNumBits, 4) : new KeyMakerLong43(boardSizeNumBits)); break; case 5: keyMaker = (isBoardGoalWildcard ? new KeyMakerLongAll(boardNumRobots, boardSizeNumBits, isBoardGoalWildcard) : new KeyMakerLong54(boardSizeNumBits)); break; default: keyMaker = new KeyMakerLongAll(boardNumRobots, boardSizeNumBits, isBoardGoalWildcard); } @@ -52,6 +52,30 @@ public static KeyMakerLong createInstance(int boardNumRobots, int boardSizeNumBi } + /** + * Creates an instance of KeyMakerLong that is tailored to the given parameters. + * + * @param boardNumRobots number of robots on the board (length of parameter state of method run) + * @param boardSizeNumBits number of bits required to store the size of the board (the 16x16 board need 8 bits) + * @param isBoardGoalWildcard true if the current goal is a wildcard goal (can be reached by any robot) + * @param isSolution01 true if the active robot can reach the goal in one move + * @return the instance of KeyMakerLong created + */ + public static KeyMakerLong createInstance(int boardNumRobots, int boardSizeNumBits, boolean isBoardGoalWildcard, boolean isSolution01) { + final KeyMakerLong keyMaker; + if (isSolution01) { + switch (boardNumRobots) { + case 4: keyMaker = new KeyMakerLong33(boardSizeNumBits, 4); break; + case 5: keyMaker = new KeyMakerLong44(boardSizeNumBits, 5); break; + default: keyMaker = null; // other numbers of robots are not supported! + } + } else { + keyMaker = createInstance(boardNumRobots, boardSizeNumBits, isBoardGoalWildcard); + } + return keyMaker; + } + + private static final class KeyMakerLongAll extends KeyMakerLong { private final int[] tmpState; private final int idxSort, idxLen1, idxLen2; @@ -79,6 +103,34 @@ public final long run(final int[] state) { } + private static final class KeyMakerLong33 extends KeyMakerLong { //sort 3 of 3 elements + private final int s1, s2, len; + private KeyMakerLong33(int boardSizeNumBits, int len) { + this.len = len; + this.s1 = boardSizeNumBits; + this.s2 = boardSizeNumBits * 2; + } + @Override + public final long run(final int[] state) { + assert this.len == state.length : state.length; + final int a = state[0], b = state[1], c = state[2]; + final long result; + if (a < b) { + if (a < c) { + if (b < c) { result = (a | (b << this.s1)) | ((long)c << this.s2); + } else { result = (a | (c << this.s1)) | ((long)b << this.s2); } + } else { result = (c | (a << this.s1)) | ((long)b << this.s2); } + } else { + if (b < c) { + if (a < c) { result = (b | (a << this.s1)) | ((long)c << this.s2); + } else { result = (b | (c << this.s1)) | ((long)a << this.s2); } + } else { result = (c | (b << this.s1)) | ((long)a << this.s2); } + } + return result; + } + } + + private static final class KeyMakerLong43 extends KeyMakerLong { //sort 3 of 4 elements private final int s1, s2, s3; private KeyMakerLong43(int boardSizeNumBits) { @@ -108,15 +160,16 @@ public final long run(final int[] state) { private static final class KeyMakerLong44 extends KeyMakerLong { //sort 4 of 4 elements - private final int s1, s2, s3; - private KeyMakerLong44(int boardSizeNumBits) { + private final int s1, s2, s3, len; + private KeyMakerLong44(int boardSizeNumBits, int len) { + this.len = len; this.s1 = boardSizeNumBits; this.s2 = boardSizeNumBits * 2; this.s3 = boardSizeNumBits * 3; } @Override public final long run(final int[] state) { - assert 4 == state.length : state.length; + assert this.len == state.length : state.length; final int a = state[0], b = state[1], c = state[2], d = state[3]; final long result; if (a <= b) { diff --git a/src/driftingdroids/model/SolverIDDFS.java b/src/driftingdroids/model/SolverIDDFS.java index 61788c0..cb29bf9 100644 --- a/src/driftingdroids/model/SolverIDDFS.java +++ b/src/driftingdroids/model/SolverIDDFS.java @@ -36,7 +36,7 @@ public class SolverIDDFS extends Solver { private final int goalPosition; private final int minRobotLast; private final int goalRobot; - private final boolean isSolution01; + private final boolean isSolution01, isSolution01NoSpeedup; private final int[] minimumMovesToGoal; private final int[] directionIncrement; @@ -52,6 +52,7 @@ protected SolverIDDFS(final Board board) { this.minRobotLast = (this.isBoardGoalWildcard ? 0 : this.states[0].length - 1); //swapGoalLast this.goalRobot = (this.isBoardGoalWildcard ? (null == this.board.getGoal() ? 0 : this.board.getGoal().robotNumber) : this.minRobotLast); //swapGoalLast this.isSolution01 = this.board.isSolution01(); + this.isSolution01NoSpeedup = (true == this.isSolution01) && ((true == this.isBoardGoalWildcard) || (4 > this.board.getNumRobots())); this.minimumMovesToGoal = new int[board.size]; this.directionIncrement = this.board.directionIncrement; } @@ -92,7 +93,7 @@ public List execute() throws InterruptedException { this.knownStates = new KnownStates(); System.out.println("startState=" + this.stateString(this.states[0])); - System.out.println("solution01=" + this.isSolution01); + System.out.println("solution01=" + this.isSolution01 + " isSolution01NoSpeedup=" + this.isSolution01NoSpeedup); System.out.println("goalWildcard=" + this.isBoardGoalWildcard); System.out.println(this.knownStates.getInfo()); @@ -220,7 +221,7 @@ private void dfsRecursion(final int depth, final int prevRobo, final int prevDir newState[robo] = newRoboPos; //special case (isSolution01): we must be able to visit states more than once, so we don't add them to knownStates //the new state is not already known (i.e. stored in knownStates) - if ((true == this.isSolution01) || (true == this.knownStates.add(newState, height))) { + if (this.isSolution01NoSpeedup || (this.isSolution01 && isGoalRobot) || (this.knownStates.add(newState, height))) { final int[] newDirs = this.directions[depth]; System.arraycopy(oldDirs, 0, newDirs, 0, oldDirs.length); newDirs[robo] = dir; @@ -414,7 +415,7 @@ private class KnownStates { private final AllKeys allKeys; public KnownStates() { - this.allKeys = ((true == isBoardStateInt32) ? new AllKeysInt() : new AllKeysLong()); + this.allKeys = (board.sizeNumBits * (board.getNumRobots() - (isSolution01 ? 1 : 0)) <= 32) ? new AllKeysInt() : new AllKeysLong(); } //store the unique keys of all known states @@ -436,7 +437,7 @@ public long getBytesAllocated() { //store the unique keys of all known states in 32-bit ints //supports up to 4 robots with a board size of 256 (16*16) private final class AllKeysInt extends AllKeys { - private final KeyMakerInt keyMaker = KeyMakerInt.createInstance(board.getNumRobots(), board.sizeNumBits, isBoardGoalWildcard); + private final KeyMakerInt keyMaker = KeyMakerInt.createInstance(board.getNumRobots(), board.sizeNumBits, isBoardGoalWildcard, isSolution01); public AllKeysInt() { super(); } @@ -447,13 +448,13 @@ public final boolean add(final int[] state, final int depth) { } @Override public String getInfo() { - return this.getClass().getSimpleName() + "," + this.theMap.getClass().getSimpleName() + "," + this.keyMaker.getClass().getSimpleName(); + return this.getClass().getSimpleName() + "," + this.theMap.getClass().getSimpleName() + "," + (null == this.keyMaker ? "n/a" : this.keyMaker.getClass().getSimpleName()); } } //store the unique keys of all known states in 64-bit longs //supports more than 4 robots and/or board sizes larger than 256 private final class AllKeysLong extends AllKeys { - private final KeyMakerLong keyMaker = KeyMakerLong.createInstance(board.getNumRobots(), board.sizeNumBits, isBoardGoalWildcard); + private final KeyMakerLong keyMaker = KeyMakerLong.createInstance(board.getNumRobots(), board.sizeNumBits, isBoardGoalWildcard, isSolution01); public AllKeysLong() { super(); } @@ -464,7 +465,7 @@ public final boolean add(final int[] state, final int depth) { } @Override public String getInfo() { - return this.getClass().getSimpleName() + "," + this.theMap.getClass().getSimpleName() + "," + this.keyMaker.getClass().getSimpleName(); + return this.getClass().getSimpleName() + "," + this.theMap.getClass().getSimpleName() + "," + (null == this.keyMaker ? "n/a" : this.keyMaker.getClass().getSimpleName()); } } diff --git a/src/driftingdroids/ui/Starter.java b/src/driftingdroids/ui/Starter.java index 1d7bcd9..c11ab41 100644 --- a/src/driftingdroids/ui/Starter.java +++ b/src/driftingdroids/ui/Starter.java @@ -40,7 +40,7 @@ public class Starter { public static void main(String[] args) throws InterruptedException, InvocationTargetException { - new SwingGUI("DriftingDroids 1.3.6 (2018-04-29)"); + new SwingGUI("DriftingDroids 1.3.7 __DEV__ (2018-09-16)"); // runTestRandom1000(); // runTestKeyDepthMap(); // runTestKeyInt();