first concurrent solution
This commit is contained in:
parent
502458d373
commit
7c146ce8ad
|
@ -26,10 +26,10 @@ final public class Solver extends JPanel {
|
|||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// The default size of the labyrinth (i.e. unless program is invoked with size arguments):
|
||||
private static final int DEFAULT_WIDTH_IN_CELLS = 100;
|
||||
private static final int DEFAULT_HEIGHT_IN_CELLS = 100;
|
||||
private static final int DEFAULT_WIDTH_IN_CELLS = 1000;
|
||||
private static final int DEFAULT_HEIGHT_IN_CELLS = 1000;
|
||||
|
||||
private static final int N_RUNS_HALF = 5; // #runs will be 2*N_RUNS_HALF + 1
|
||||
private static final int N_RUNS_HALF = 10; // #runs will be 2*N_RUNS_HALF + 1
|
||||
|
||||
// The grid defining the structure of the labyrinth
|
||||
private final Labyrinth labyrinth;
|
||||
|
@ -38,7 +38,8 @@ final public class Solver extends JPanel {
|
|||
private boolean[][] visited; // initialized in solve()
|
||||
private AtomicBoolean[][] visitedAtomic;
|
||||
|
||||
private static final int DISTANCE_PER_TASK = 20;
|
||||
// determined by trial and error
|
||||
private static final int DISTANCE_PER_TASK = DEFAULT_WIDTH_IN_CELLS * DEFAULT_HEIGHT_IN_CELLS / 128;
|
||||
|
||||
private Point[] solution = null; // set to solution path once that has been computed
|
||||
|
||||
|
@ -124,16 +125,19 @@ final public class Solver extends JPanel {
|
|||
|
||||
public Point[] solveConcurrently() {
|
||||
// dummy origin direction for start
|
||||
PointAndDirection start = new PointAndDirection(labyrinth.getStart(), Direction.N);
|
||||
PointAndDirection start = new PointAndDirection(labyrinth.getStart(), null);
|
||||
|
||||
visitedAtomic = new AtomicBoolean[labyrinth.getWidth()][labyrinth.getHeight()];
|
||||
|
||||
ArrayDeque<PointAndDirection> backtrackStack = new ArrayDeque<PointAndDirection>();
|
||||
for (int i = 0; i < visitedAtomic.length; i++) {
|
||||
for (int j = 0; j < visitedAtomic[i].length; j++) {
|
||||
visitedAtomic[i][j] = new AtomicBoolean(false);
|
||||
}
|
||||
}
|
||||
|
||||
ForkJoinPool pool = ForkJoinPool.commonPool();
|
||||
|
||||
|
||||
ConcurrentSolverTask t = new ConcurrentSolverTask(backtrackStack, start);
|
||||
ArrayDeque<Point> pathSoFar = new ArrayDeque<>();
|
||||
ConcurrentSolverTask t = new ConcurrentSolverTask(start, pathSoFar, pool);
|
||||
Point[] result = pool.invoke(t);
|
||||
|
||||
return result;
|
||||
|
@ -142,57 +146,138 @@ final public class Solver extends JPanel {
|
|||
private class ConcurrentSolverTask extends RecursiveTask<Point[]> {
|
||||
private ArrayDeque<PointAndDirection> backtrackStack;
|
||||
private PointAndDirection start;
|
||||
private int initialStackSize;
|
||||
private HashSet<Point> visitedThisTask;
|
||||
public ConcurrentSolverTask(ArrayDeque<PointAndDirection> backtrackStack, PointAndDirection start) {
|
||||
this.backtrackStack = backtrackStack;
|
||||
private ArrayDeque<Point> pathSoFar;
|
||||
private ForkJoinPool pool;
|
||||
private ArrayList<ConcurrentSolverTask> subtasks;
|
||||
public ConcurrentSolverTask(PointAndDirection start, ArrayDeque<Point> pathSoFar, ForkJoinPool pool) {
|
||||
this.start = start;
|
||||
this.initialStackSize = backtrackStack.size();
|
||||
this.visitedThisTask = new HashSet<>();
|
||||
this.pathSoFar = pathSoFar;
|
||||
this.pool = pool;
|
||||
|
||||
this.backtrackStack = new ArrayDeque<>();
|
||||
this.subtasks = new ArrayList<>();
|
||||
}
|
||||
|
||||
public Point[] compute() {
|
||||
PointAndDirection current = this.start;
|
||||
int currentLength = 0;
|
||||
Point current = this.start.getPoint();
|
||||
|
||||
if (labyrinth.isDestination(current.getPoint())) {
|
||||
return backtrackStackToPath(backtrackStack);
|
||||
}
|
||||
Point next;
|
||||
Direction[] dirs = Direction.values();
|
||||
|
||||
if (!current.getDirectionToBranchingPoint().equals(Direction.N)) {
|
||||
if () {
|
||||
while (!labyrinth.isDestination(current) && !Thread.interrupted()) {
|
||||
// System.out.println("Visiting " + current);
|
||||
|
||||
next = null;
|
||||
|
||||
for (Direction dir : dirs) {
|
||||
|
||||
// don't check direction of origin
|
||||
if (
|
||||
current.equals(this.start.getPoint())
|
||||
&& dir.equals(start.getDirectionToBranchingPoint())
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if labyrinth has passage in this direction
|
||||
if (!labyrinth.hasPassage(current, dir)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Point neighbour = current.getNeighbor(dir);
|
||||
|
||||
// avoid blind alleys
|
||||
if (
|
||||
labyrinth.isBlindAlley(neighbour, dir.opposite)
|
||||
&& !labyrinth.isDestination(neighbour)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if that cell has been visited
|
||||
// set visitedAtomic to true if it has not been visited
|
||||
if (!visitedAtomic[neighbour.x][neighbour.y].compareAndSet(false, true)) {
|
||||
continue;
|
||||
}
|
||||
// System.out.println("Found unvisited neighbour: " + neighbour);
|
||||
if (currentLength >= DISTANCE_PER_TASK) {
|
||||
// if we have reached the distance limit, create a subtask
|
||||
ArrayDeque<Point> subPath = this.pathSoFar.clone();
|
||||
subPath.addLast(current);
|
||||
ConcurrentSolverTask subtask = new ConcurrentSolverTask(
|
||||
new PointAndDirection(neighbour, dir.opposite),
|
||||
subPath,
|
||||
this.pool
|
||||
);
|
||||
subtask.fork();
|
||||
subtasks.add(subtask);
|
||||
} else {
|
||||
if (next == null) {
|
||||
// visit first unvisited neighbour next
|
||||
next = neighbour;
|
||||
// System.out.println("Visiting next.");
|
||||
} else {
|
||||
// push all other unvisited neighbours on backtrack stack to be checked later
|
||||
this.backtrackStack.push(new PointAndDirection(neighbour, dir.opposite));
|
||||
// System.out.println("Pushing to stack.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// at least one unvisited neighbour found
|
||||
if (next != null) {
|
||||
pathSoFar.addLast(current);
|
||||
current = next;
|
||||
currentLength++;
|
||||
}
|
||||
if (next == null) {
|
||||
// no unvisited neighbour found
|
||||
// backtrack if possible
|
||||
if (this.backtrackStack.isEmpty()) {
|
||||
// no solution found from this point
|
||||
|
||||
// check for solutions in subtasks
|
||||
// System.out.println("End of task reached - waiting for " + subtasks.size() + " subtasks");
|
||||
for (ConcurrentSolverTask subtask : subtasks) {
|
||||
Point[] subtaskResult = subtask.join();
|
||||
if (subtaskResult != null) {
|
||||
// found a solution in a subtask
|
||||
return subtaskResult;
|
||||
}
|
||||
}
|
||||
// no solution found
|
||||
return null;
|
||||
}
|
||||
|
||||
PointAndDirection pd = this.backtrackStack.pop();
|
||||
current = pd.getPoint();
|
||||
|
||||
// Remove the dead end from the top of pathSoFar, i.e. all cells after branchingPoint:
|
||||
Point branchingPoint = current.getNeighbor(pd.getDirectionToBranchingPoint());
|
||||
while (!pathSoFar.isEmpty() && !pathSoFar.peekLast().equals(branchingPoint)) {
|
||||
pathSoFar.removeLast();
|
||||
currentLength--;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean visit(PointAndDirection p) {
|
||||
if (visitedThisTask.contains(p.getPoint())) {
|
||||
return false;
|
||||
// parent thread has found solution or other interrupt
|
||||
if (Thread.interrupted()) {
|
||||
return null;
|
||||
}
|
||||
Point lastPoint = backtrackStack.getLast().getPoint();
|
||||
if (Math.abs(lastPoint.y - p.getPoint().y) + Math.abs(lastPoint.x - p.getPoint().x) > 1) {
|
||||
System.err.println("Cannot visit point " + p.getPoint() + " because it is not adjacened to " + lastPoint);
|
||||
return false;
|
||||
pathSoFar.addLast(current);
|
||||
|
||||
// solution found, interrupt subtasks
|
||||
for (ConcurrentSolverTask subtask : subtasks) {
|
||||
subtask.cancel(true);
|
||||
}
|
||||
if (!visitedAtomic[p.getPoint().x][p.getPoint().y].compareAndSet(false, true)) return false;
|
||||
|
||||
backtrackStack.add(p);
|
||||
visitedThisTask.add(p.getPoint());
|
||||
|
||||
return true;
|
||||
return pathSoFar.toArray(new Point[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private Point[] backtrackStackToPath(ArrayDeque<PointAndDirection> backtrackStack) {
|
||||
Point[] result = new Point[backtrackStack.size()];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[backtrackStack.size() - 1] = backtrackStack.remove().getPoint();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics graphics) {
|
||||
super.paintComponent(graphics);
|
||||
|
@ -321,7 +406,7 @@ private static void displayLabyrinth(Solver solver) {
|
|||
}
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
solver.solution = solver.solve();
|
||||
solver.solution = solver.solveConcurrently();
|
||||
long endTime = System.currentTimeMillis();
|
||||
|
||||
if (solver.solution == null)
|
||||
|
|
Loading…
Reference in New Issue