Skip to content

Commit

Permalink
speedup of "solution01 solver mode"
Browse files Browse the repository at this point in the history
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
  • Loading branch information
smack42 committed Sep 16, 2018
1 parent 052a164 commit a879934
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 23 deletions.
5 changes: 3 additions & 2 deletions src/driftingdroids/model/KeyDepthMapTrieSpecial.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
42 changes: 34 additions & 8 deletions src/driftingdroids/model/KeyMakerInt.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 <tt>KeyMakerInt</tt> that is tailored to the given parameters.
*
* @param boardNumRobots number of robots on the board (length of parameter <tt>state</tt> of method <tt>run</tt>)
* @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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down
61 changes: 57 additions & 4 deletions src/driftingdroids/model/KeyMakerLong.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,38 @@ 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);
}
return keyMaker;
}


/**
* Creates an instance of <tt>KeyMakerLong</tt> that is tailored to the given parameters.
*
* @param boardNumRobots number of robots on the board (length of parameter <tt>state</tt> of method <tt>run</tt>)
* @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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down
17 changes: 9 additions & 8 deletions src/driftingdroids/model/SolverIDDFS.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
}
Expand Down Expand Up @@ -92,7 +93,7 @@ public List<Solution> 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());

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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();
}
Expand All @@ -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();
}
Expand All @@ -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());
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/driftingdroids/ui/Starter.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit a879934

Please sign in to comment.