- Definition:
0. use a 2d array of integers to represent a maze;
1. 0 for passable, 1 if not;
2. the first element (maze[0][0]) is the entry;
3. the last element (maze[maze.length - 1][maze[maze.length - 1].length - 1]) is the exit;
eg.
0, 1, 1
1, 0, 1
1, 1, 0
4. horizontal, vertical and diagonal moves are allowed;
- Question:
Find out all the possible ways out and figure out the shortest one.
- Solution:
0. start from the entry point of the maze, define and initialize three pointers: previous, current and next;
1. search all passable points next to current point and store them in a linked list;
2. remove one from the passable list as next, point previous to current and point current to next, then push the rest passables into a stack for later backtracking;
3. if no more passable point is found at the current position, pop one point from the stack, go back to the right place and choose a different move;
4. wrap 1-3 in a loop until all paths of the maze are searched.
- Codes:
0. MazeSearcher
package co.speedar.dsal.maze;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
/**
* Search all the ways out in a given maze.
*
* @author ben
* @creation 2014年4月13日
*/
public class MazeSearcher {
/**
* 2d integer matrix, representing the maze.<br/>
* Definition: <br/>
* 1. 0 for passable, 1 if not;<br/>
* 2. the first element (maze[0][0]) is the entry;<br/>
* 3. the last element (maze[maze.length - 1][maze[maze.length - 1].length - 1]) is the exit;<br/>
* eg.<br/>
* 0, 1, 1<br/>
* 1, 0, 1<br/>
* 1, 1, 0<br/>
*/
private int[][] maze;
/**
* current search trunk<br/>
* {currentLoc,preLoc}
*/
private List<Point[]> currentPath = new LinkedList<Point[]>();
/**
* holds candidate movables, used for backtracking<br/>
* {currentLoc,preLoc,nextLoc}
*/
private Stack<Point[]> stack = new Stack<Point[]>();
/**
* holds all the solutions.
*/
private List<List<Point[]>> paths = new LinkedList<List<Point[]>>();
public MazeSearcher(int[][] maze) {
super();
this.maze = maze;
}
/**
* find out all the possible paths
*/
public void searchAllPaths() {
Point current = new Point(0, 0);
Point pre = null;
Point next = null;
// add the start point
currentPath.add(new Point[] { current, pre });
List<Point> movables = searchNext(current, pre);
do {
if (movables != null && !movables.isEmpty()) {
// still movable
next = movables.remove(0);
if (movables != null && !movables.isEmpty()) {
// has optional paths, push them into the stack for backtracking
for (int i = 0; i < movables.size(); i++) {
// {current position, pre position, next position}
Point temPoint = movables.remove(0);
System.out.println("stack.push: " + "{" + current + ","
+ pre + "," + temPoint + "}");
stack.push(new Point[] { current, pre, temPoint });
}
}
pre = current;
current = next;
currentPath.add(new Point[] { current, pre });
next = null;
} else {
// backtrack
if (!stack.isEmpty() && !currentPath.isEmpty()) {
Point[] backtrackPoints = stack.pop();
System.out.println("stack.pop: " + "{" + backtrackPoints[0]
+ "," + backtrackPoints[1] + ","
+ backtrackPoints[2] + "}");
Point[] currentPathPoints;
while (!currentPath.isEmpty()) {
currentPathPoints = currentPath.remove(currentPath
.size() - 1);
if (currentPathPoints[0].equals(backtrackPoints[0])
&& ((currentPathPoints[1] == null && backtrackPoints[1] == null) || currentPathPoints[1]
.equals(backtrackPoints[1]))) {
// track back to the right position
currentPath.add(currentPathPoints);
break;
}
}
// choose a different position at that point
next = backtrackPoints[2];
pre = backtrackPoints[1];
current = backtrackPoints[0];
pre = current;
current = next;
currentPath.add(new Point[] { current, pre });
next = null;
}
}
// check and save path if exit
if (isExit(current)) {
rememberCurrentPath();
}
// search next movables
movables = searchNext(current, pre);
// print out current path
printCurrentPath();
} while ((movables != null && !movables.isEmpty()) || !stack.isEmpty()); // continue loop if still movable or backtrackable
}
public void printCurrentPath() {
for (Point[] currentPoints : currentPath) {
System.out.print(currentPoints[0] + " -> ");
}
if (isExit(currentPath.get(currentPath.size() - 1)[0])) {
System.out.print("end");
System.out.println();
System.out.println("a way out found!");
} else {
System.out.println();
}
}
public void printShortestPath() {
int shortestLength = paths.get(0).size();
List<Point[]> shortestPath = paths.get(0);
for (List<Point[]> tempPath : paths) {
if (tempPath.size() < shortestLength) {
shortestLength = tempPath.size();
shortestPath = tempPath;
}
}
System.out.println("The shortest path is: ");
for (Point[] currentPoints : shortestPath) {
System.out.print(currentPoints[0] + " -> ");
}
System.out.println("end");
}
public void printAllPaths() {
System.out.println("Here are all the ways out: ");
for (List<Point[]> currentPoints : paths) {
for (Point[] points : currentPoints) {
System.out.print(points[0] + " -> ");
}
System.out.println("end");
}
}
private void rememberCurrentPath() {
List<Point[]> currentPoints = new LinkedList<Point[]>();
for (Point[] points : currentPath) {
currentPoints.add(points);
}
paths.add(currentPoints);
}
/**
* the current point is exit or not
*
* @param current
* @return
*/
private boolean isExit(Point current) {
if (maze.length - 1 == current.x
&& maze[maze.length - 1].length - 1 == current.y
&& isAvailable(current)) {
return true;
}
return false;
}
/**
* search all next movable positions<br/>
*
* @param current
* @param pre
* @return
*/
private List<Point> searchNext(Point current, Point pre) {
Point next = null;
List<Point> movables = new LinkedList<Point>();
if (isExit(current)) {
return movables;
}
next = searchNorth(current);
checkAndSaveNext(current, pre, next, movables);
next = searchNorthEast(current);
checkAndSaveNext(current, pre, next, movables);
next = searchEast(current);
checkAndSaveNext(current, pre, next, movables);
next = searchSouthEast(current);
checkAndSaveNext(current, pre, next, movables);
next = searchSouth(current);
checkAndSaveNext(current, pre, next, movables);
next = searchSouthWest(current);
checkAndSaveNext(current, pre, next, movables);
next = searchWest(current);
checkAndSaveNext(current, pre, next, movables);
next = searchNorthWest(current);
checkAndSaveNext(current, pre, next, movables);
return movables;
}
/**
* 1.check if null;<br/>
* 2.check if available;<br/>
* 3.check if duplicated to previous;<br/>
* 4.check if circled;
*
* @param current
* @param pre
* @param next
* @param movables
*/
private void checkAndSaveNext(Point current, Point pre, Point next,
List<Point> movables) {
if (next != null && isAvailable(next) && !next.equals(pre)
&& !containsCircle(next)) {
movables.add(next);
}
}
private boolean containsCircle(Point next) {
for (Point[] path : currentPath) {
if (path[0].equals(next)) {
return true;
}
}
return false;
}
private boolean isAvailable(Point next) {
if (maze[next.x][next.y] == 0) {
return true;
}
return false;
}
private Point searchNorthWest(Point current) {
Point next = null;
if (current.x - 1 >= 0 && current.y - 1 >= 0) {
next = new Point(current.x - 1, current.y - 1);
}
return next;
}
private Point searchWest(Point current) {
Point next = null;
if (current.y - 1 >= 0) {
next = new Point(current.x, current.y - 1);
}
return next;
}
private Point searchSouthWest(Point current) {
Point next = null;
if (current.x + 1 < maze.length && current.y - 1 >= 0) {
next = new Point(current.x + 1, current.y - 1);
}
return next;
}
private Point searchSouth(Point current) {
Point next = null;
if (current.x + 1 < maze.length) {
next = new Point(current.x + 1, current.y);
}
return next;
}
private Point searchSouthEast(Point current) {
Point next = null;
if (current.x + 1 < maze.length
&& current.y + 1 < maze[current.x + 1].length) {
next = new Point(current.x + 1, current.y + 1);
}
return next;
}
private Point searchEast(Point current) {
Point next = null;
if (current.y + 1 < maze[current.x].length) {
next = new Point(current.x, current.y + 1);
}
return next;
}
private Point searchNorthEast(Point current) {
Point next = null;
if (current.x - 1 >= 0 && current.y + 1 < maze[current.x - 1].length) {
next = new Point(current.x - 1, current.y + 1);
}
return next;
}
private Point searchNorth(Point current) {
Point next = null;
if (current.x - 1 >= 0) {
next = new Point(current.x - 1, current.y);
}
return next;
}
}
1. Point
package co.speedar.dsal.maze;
/**
* Simulates a point in the maze.
*
* @author ben
* @creation 2014年4月14日
*/
public class Point {
/**
* row index
*/
int x;
/**
* column index
*/
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Point) {
Point that = (Point) obj;
if (this.x == that.x && this.y == that.y) {
return true;
}
}
return false;
}
@Override
public String toString() {
return "(" + this.x + "," + this.y + ")";
}
}
2. MazeSearcherTest
/**
*
*/
package co.speedar.dsal.maze;
/**
* @author ben
* @creation 2014年4月14日
*/
public class MazeSearcherTest {
/**
* @param args
*/
public static void main(String[] args) {
int[][] maze = new int[4][4];
int[] tempInts;
tempInts = new int[] { 0, 0, 1, 1 };
maze[0] = tempInts;
tempInts = new int[] { 1, 1, 0, 1 };
maze[1] = tempInts;
tempInts = new int[] { 1, 0, 1, 0 };
maze[2] = tempInts;
tempInts = new int[] { 0, 1, 0, 0 };
maze[3] = tempInts;
MazeSearcher searcher = new MazeSearcher(maze);
searcher.searchAllPaths();
searcher.printAllPaths();
searcher.printShortestPath();
}
}
- Test Result:
(0,0) -> (0,1) ->
(0,0) -> (0,1) -> (1,2) ->
stack.push: {(1,2),(0,1),(2,1)}
(0,0) -> (0,1) -> (1,2) -> (2,3) ->
stack.push: {(2,3),(1,2),(3,2)}
(0,0) -> (0,1) -> (1,2) -> (2,3) -> (3,3) -> end
a way out found!
stack.pop: {(2,3),(1,2),(3,2)}
(0,0) -> (0,1) -> (1,2) -> (2,3) -> (3,2) ->
stack.push: {(3,2),(2,3),(2,1)}
(0,0) -> (0,1) -> (1,2) -> (2,3) -> (3,2) -> (3,3) -> end
a way out found!
stack.pop: {(3,2),(2,3),(2,1)}
(0,0) -> (0,1) -> (1,2) -> (2,3) -> (3,2) -> (2,1) ->
(0,0) -> (0,1) -> (1,2) -> (2,3) -> (3,2) -> (2,1) -> (3,0) ->
stack.pop: {(1,2),(0,1),(2,1)}
(0,0) -> (0,1) -> (1,2) -> (2,1) ->
stack.push: {(2,1),(1,2),(3,0)}
(0,0) -> (0,1) -> (1,2) -> (2,1) -> (3,2) ->
stack.push: {(3,2),(2,1),(3,3)}
(0,0) -> (0,1) -> (1,2) -> (2,1) -> (3,2) -> (2,3) ->
(0,0) -> (0,1) -> (1,2) -> (2,1) -> (3,2) -> (2,3) -> (3,3) -> end
a way out found!
stack.pop: {(3,2),(2,1),(3,3)}
(0,0) -> (0,1) -> (1,2) -> (2,1) -> (3,2) -> (3,3) -> end
a way out found!
stack.pop: {(2,1),(1,2),(3,0)}
(0,0) -> (0,1) -> (1,2) -> (2,1) -> (3,0) ->
Here are all the ways out:
(0,0) -> (0,1) -> (1,2) -> (2,3) -> (3,3) -> end
(0,0) -> (0,1) -> (1,2) -> (2,3) -> (3,2) -> (3,3) -> end
(0,0) -> (0,1) -> (1,2) -> (2,1) -> (3,2) -> (2,3) -> (3,3) -> end
(0,0) -> (0,1) -> (1,2) -> (2,1) -> (3,2) -> (3,3) -> end
The shortest path is:
(0,0) -> (0,1) -> (1,2) -> (2,3) -> (3,3) -> end