Creating a maze solving algorithm in Java
Asked Answered
P

5

7

I've been assigned with the task of creating a maze solver in Java. Here's the assignment:

Write an application that finds a path through a maze.  
The maze should be read from a file.  A sample maze is shown below.

O O O O O X O
X X O X O O X
O X O O X X X
X X X O O X O
X X X X O O X
O O O O O O O
X X O X X X O

The character 'X' represents a wall or a blocked position and the character 'O' represents an open position. You may assume that the entrance to the maze is always in the lower right hand corner, and the exit is always in the upper left hand corner. Your program should send its output to a file. If a path is found the output file should contain the path. If a path is not found a message should be sent to the file. Please note that a maze may have more than one solution path, but in this exercise you are only being asked to locate one solution, not all solutions.

Your program should use a stack to record the path that it is exploring and backtrack when it reaches a blocked position.

Be sure to write a complete algorithm before writing your code. Feel free to create any additional classes that will help you complete the assignment.

Here's my Algorithm:
1)Initialize array list to hold maze
2)Read text file holding maze in format
    o x x x x o
    o o x o x x
    o o o o o x
    x x x x o o
3)Create variables to hold numbers of columns and rows
3)While text file has next line
    A. Read next line
    B. Tokenize line
    C. Create a temporary ArrayList
    D. While there are tokens
        i. Get token
        ii. create a Point
        iii. add point to temp ArrayList
        iv. increment maximum number of columns
    E. Add temp to maze arraylist, increment max rows
    F. initialize a hold of points as max rows - 1
    G. Create a start point with x values as maximum number of rows - 1, and y values as maximum number of columns - 1
    H. Create stack of points and push starting location
    I. While finished searching is not done
        i. Look at top of stack and check for finish
        ii. check neighbors
        iii. is there an open neighbor?
            - if yes, update flags and push
            - if no, pop from stack
    J. Print solution
4. Done is true

Anyway, what I have set up is a Points class that has set/get methods for traveling in all cardinal directions which will return booleans as shown:

public class Points<E>
{
private int xCoord;
private int yCoord;
private char val;
private boolean N;
private boolean S;
private boolean E;
private boolean W;

public Points()
{
    xCoord =0;
    yCoord =0;
    val =' ';
    N = true;
    S = true;
    E = true;
    W = true;

}

public Points (int X, int Y)
{
        xCoord = X;
        yCoord = Y;

}

public void setX(int x)
{
    xCoord = x;
}
public void setY(int y)
{
    yCoordinate = y;
}

public void setNorth(boolean n)
{
    N = n;
}
public void setSouth(boolean s)
{
    S= s;
}
public void setEast(boolean e)
{
    E = e;
}

public void setWest(boolean w)
{
    W = w;
}

public int getX()
{
    return xCoord;

}

public int getY()
{
    return yCoord;
}
public char getVal()
{
    return val;
}

public boolean getNorth()
{
    return N;
}
public boolean getSouth()
{
    return S;
}

public boolean getEast()
{
    return E;
}
public boolean getWest()
{
    return W;
}

public String toString1()
{
    String result = "(" + xCoord + ", " +yCoord + ")";
    return result;
}

}

I'm just having problems getting the actual solving down in the main. Here's what I have:

import java.io.*;
import java.util.*;
import java.lang.*;
import java.text.*;


public class MazeSolve1
{
  public static void main(String[] args)
  {
//Create arrayList of Points
ArrayList<ArrayList<Points>> MAZE = new ArrayList<ArrayList<Points>>();
Scanner in = new Scanner(System.in);

//Read File in
System.out.print("Enter the file name: ");
String fileName = in.nextLine();
fileName = fileName.trim();
FileReader reader = new FileReader(fileName+".txt");
Scanner in2 = new Scanner(reader);

//Write file out
FileWriter writer = new FileWriter("Numbers.out");
PrintWriter out = new PrintWriter(writer);
boolean done = false;
int maxCol = 0;
int maxRow = 0;

while(!done) {

    //creating array lists
    while (in2.hasNextLine()) {
        //Read next line
        String nextLine = in2.nextLine();
        //Tokenize Line
        StringTokenizer st = new StringTokenizer(nextLine, " ");
        //Create temp ArrayList
        ArrayList<ArrayList<Points>> temp = new ArrayList<ArrayList<Points>>();

        //While there are more tokens
        while (st.hasNextToken()) {
            String token = st.nextToken();
            Points pt = new Points();
            temp.add(pt);
            maxCol++
        }
        MAZE.add(temp);
        maxRow++;
    }

    //create hold arraylist for max rows of maze -1 
    //create variables for start x and y coordinates
    ArrayList<ArrayList<Points>> hold = new ArrayList<ArrayList<Points>>();
    hold = MAZE.get(maxRow - 1);
    int startColumn = hold.get(maxCol - 1);
    int startRow = hold.get(maxRow - 1);
    Point start = new Point();
    start.setX(startColumn);
    start.setY(startRow);

    //initialize stack, and push the start position
    MyStack<Points> st = new ArrayStack<Points>();
    st.push(start.toString1());
    //south and east of start are edges of array
    start.setSouth(false);
    start.setEast(false);

    //while your position is not equal to point (0,0) [finish]
    while (st.peek() != "(0, 0)") {

        //getting the next coordinate to the North
        int nextY = start.getY() - 1;
        int nextX = start.getX();

        //if character to the North is an O it's open and the North flag is true
        if (hold.get(nextY) = 'O' && start.getNorth() == true) {
            //set flags and push coordinate
            start.setNorth(false);
            st.push(start.toString1());
        }
        //else pop from stack
        else { st.pop(); }

        //look at coordinate to the East
        nextX = start.getX() + 1;
        //if character to the East is a O and the East flag is true
        if (hold.get(nextX) = 'O' && start.getEast() == true) {
            //set flags and push coordinate
            start.setEast(false);
            st.push(start.toString1());
        }
        //else pop from stack
        else { st.pop(); }

        //look at coordinate to the South
        nextY = start.getY() + 1;
        //if character to the South is a O and the West flag is true
        if (hold.get(nextY) = 'O' && start.getSouth() == true) {
            //set flags and push coordinate
            start.setSouth(false);
            st.push(start.toString1());
        }
        //else pop from stack
        else { st.pop() }

        //keep looping until the top of the stack reads (0, 0)
    }

done = true;
}

//Print the results
System.out.println("---Path taken---");   
for (int i = 0; i< st.size(); i++) {
    System.out.println(st.pop);
    i++
}

Aside from any syntax errors, could you guys offer me some help? Thanks a lot.

Palaeolithic answered 16/2, 2012 at 20:26 Comment(4)
What specific error are you encountering?Humes
Could you describe the problems you are having? In what way(s) do you believe this to not be correct?Interlunar
Well I'm not sure if I'm doing the actual neighbor checking correctly, like iterating through the mazePalaeolithic
What specific symptoms are you getting? Can you give a specific, concrete example of what's going wrong?Humes
O
13

enter image description here

I submitted a similar answer here Maze Solving Algorithm in C++.

To have a chance in solving it, you ought to:

  • Create a Solve() routine and recursively call itself:
    • if 1st, 2nd, 3rd, ... are true Solve has succeeded in finding a solution
    • if 1st, 2nd, 3rd, ... contains a false, it has to backtrack and find another way
  • You need to build a buffer of places you've been to avoid infinite loops
    • as you make moves it needs to keep tabs on it
    • when we hit a dead end, we need to erase bad moves
    • we can implement the above by burning in a guess and removing it if it's wrong

Here's some pseudo code for the solution.

boolean solve(int X, int Y)
{
    if (mazeSolved(X, Y))
    {
        return true;
    }

    // Test for (X + 1, Y)
    if (canMove(X + 1, Y))
    {
        placeDude(X + 1, Y);
        if (solve(X + 1, Y)) return true;
        eraseDude(X + 1, Y);
    }

    // Repeat Test for (X - 1, Y), (X, Y - 1) and (X, Y + 1)
    // ...

    // Otherwise force a back track.
    return false;
 }
Outline answered 16/2, 2012 at 23:3 Comment(1)
This is a DFS algorithm. You can read more about it on Baeldung.Daybreak
S
7

You probably should module your program - as I can understand it, you are reading the maze from file and trying to solve it at the same time.

A better approach will be to split the program into 2 distinct parts:

  1. read the input file and create a matrix with all the data
  2. solve the maze from given matrix

Doing so will help you to build and test each part seperately, which will probably result in better, more reliable program.

Solving the maze could be done by a simple BFS, which is similar to what your algorithm originally suggested , which is a DFS

Supervisory answered 16/2, 2012 at 20:33 Comment(4)
I think the original algorithm is a DFS, since he's using a stack. That said, I like the BFS solution more.Humes
@templatetypedef: You are correct, I editted and fixed that mistake. thanks.Supervisory
I don't know what any of that means...we haven't learned that stuff yet.Palaeolithic
@Copernikush: The main idea of this answer is simple: Instead of reading the file and at the same time try to find a solution to the maze, first read the file - and create a matrix from it, including all the data. Once this step is done - focus on solving the maze using your matrix. This approach will help you to test small parts of your program and make sure each of them works, so it will be easier to find and repair any bugs.Supervisory
P
1

As amit has said, you should first read the entire maze and store it as a 2 dimensional array. This lets you see the whole maze without having to solve it line-by-line.

Since you'll first need to find the size of the array, you should read the text file into a List of Strings.

List<String> strs = new ArrayList<String>();

//Pseudocode, choose however you want to read the file
while(file_has_next_line) {
    strs.add(get_next_line);
}

The size of the List gives you the number of rows, and assuming it's always a grid, you can use split().length, (count spaces + 1) or count the symbols on any one of the Strings to get the number of columns.

Easiest way to store the map data is with a 2D array of booleans. Where true is a wall and false is empty space.

boolean[][] wallMap = new boolean[rows][cols];

for(int i = 0; i < wallMap.length; i++) {

    //Separate each symbol in corresponding line
    String[] rowSymbols = strs.get(i).split(" ");

    for(int j = 0; j < wallMap[i].length; j++) {

        //Ternary operator can be used here, I'm just keeping it simple
        if(rowSymbols[j].equals("X")) {
             wallMap[i][j] = true;
        } else {
             wallMap[i][j] = false;
        }

    }

}

Now that you have the map data stored in an array it's much easier to traverse the map and make your choices, you can either use a ready-made algorithm (see amit's answer) or make your own. As this is homework, you should try and think up your own.

Have fun.

Powerless answered 16/2, 2012 at 21:37 Comment(0)
S
0

You need to separate your program in two phases. The first one is the initialization, where you read the maze description and the initial position of the player. After this you have a data structure to represent the board. The second one is the actual game, where there should be 3 abstractions:

  • The player state. In your case it is pretty simple, its actual position on the board.
  • The maze itself, which is the environment. It should have functions telling you if you have visited a given position, to mark position you have visited, where is the goal,the next reachable cells, etc...
  • The logic, which is the search algorithm.

Any of these should be able to change without much change of the others. For example, you may be asked to improve your search algorithm, or a problem where you have more than one goal. The easiness of switching from the current problem to a slightly modified one is the real metric of a program design.

Southwestwardly answered 16/2, 2012 at 21:49 Comment(0)
C
0

I tried to implement this using DFS algorithm utilizing some Java OOP concepts.

See complete solution on my github repository

private boolean solveDfs() {
    Block block = stack.peekFirst();
    if (block == null) {
        // stack empty and not reached the finish yet; no solution
        return false;
    } else if (block.equals(maze.getEnd())) {
        // reached finish, exit the program
        return true;
    } else {
        Block next = maze.getNextAisle(block);
        // System.out.println("next:" + next);
        if (next == null) {
            // Dead end, chose alternate path
            Block discard = stack.pop();
            discard.setInPath(false);
            // System.out.println("Popped:" + discard);
        } else {
            // Traverse next block
            next.setVisited(true);
            next.setInPath(true);
            stack.push(next);
        }
    }
    return solveDfs();

}

Cordless answered 6/6, 2017 at 16:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.