d2jsp
Log InRegister
d2jsp Forums > Off-Topic > Computers & IT > Programming & Development > Java Maze Solver > Just Looking For Input
12Next
Add Reply New Topic New Poll
Member
Posts: 4,598
Joined: Jul 22 2011
Gold: 3.00
Mar 15 2014 12:31am
I was assigned a lab where we had to create a maze GUI and solver class to allow users to create their own maze that would output a 2d string array which would implement the solver method to solve the maze. I successfully finished the lab albeit with some cases in which the solver didn't work (instructor gave us an outline for an algorithm that would work on mazes that didn't involve infinite loops) but I was just curious to see how you guys would have written it. Im SURE there was a much easier way to do it than I did and wanted to see how you guys would do it.
Code

import java.util.ArrayList;

public class Solver implements MazeSolver {

private static ArrayList<Coordinate> coordinates = new ArrayList<Coordinate>();
public Solver() {
}
@Override
public String[][] solve(String[][] map) {
//creates a 2d array of strings to store the solved map
String[][] str =
new String[map.length][map[0].length];
boolean done = false;
for (int row = 0; row < map.length; row++) {
for (int col = 0; col < map[0].length; col++) {
str[row][col] = map[row][col];
}
}
//Find the start and finish coordinate repectively
Coordinate c1 = findStart(str);
Coordinate c2 = findFinish(str);

int x = 0;

//While loop to keep track of whether of not the maze is solved or not
while (done == false) {

//when the coordinate that marks the position within the array reaches the Finish Coordinate, end
if ((c1.getRow() + 1 == c2.getRow()) && c1.getCol() == c2.getCol()) {
str[c2.getRow()][c2.getCol()] = "RIP";
break;

}

// Check left right up and down for the first available space and moves coordinate to that location while marking its previous location with X
if (isClear(str, c1.getRow() + 1, c1.getCol())) {

str[c1.getRow() + 1][c1.getCol()] = "X";
c1 = new Coordinate(c1.getRow() + 1, c1.getCol());
coordinates.add(c1);
} else if (isClear(str, c1.getRow(), c1.getCol() + 1)) {
str[c1.getRow()][c1.getCol() + 1] = "X";
c1 = new Coordinate(c1.getRow(), c1.getCol() + 1);
coordinates.add(c1);
} else if (isClear(str, c1.getRow(), c1.getCol() - 1)) {
str[c1.getRow()][c1.getCol() - 1] = "X";
c1 = new Coordinate(c1.getRow(), c1.getCol() - 1);
coordinates.add(c1);
} else if (isClear(str, c1.getRow() - 1, c1.getCol())) {
str[c1.getRow() - 1][c1.getCol()] = "X";
c1 = new Coordinate(c1.getRow() - 1, c1.getCol());
coordinates.add(c1);
}

//If the current coordinate is surrounded on all sides by X, W, or D then it has exhausted all available paths and moves back one space, simultaneously changing said space to a D so as not to revisit the spot again.
if ((str[c1.getRow() - 1][c1.getCol()].equals("X")
|| str[c1.getRow() - 1][c1.getCol()].equals("W") || str[c1
.getRow() - 1][c1.getCol()].equals("D"))
&& (str[c1.getRow() + 1][c1.getCol()].equals("X")
|| str[c1.getRow() + 1][c1.getCol()].equals("W") || str[c1
.getRow() + 1][c1.getCol()].equals("D"))
&& (str[c1.getRow()][c1.getCol() - 1].equals("X")
|| str[c1.getRow()][c1.getCol() - 1].equals("W") || str[c1
.getRow()][c1.getCol() - 1].equals("D"))
&& (str[c1.getRow()][c1.getCol() + 1].equals("X")
|| str[c1.getRow()][c1.getCol() + 1].equals("W") || str[c1
.getRow()][c1.getCol() + 1].equals("D"))) {

str[c1.getRow()][c1.getCol()] = "D";
coordinates.remove(coordinates.size() - 1);
c1 = coordinates.get(coordinates.size() - 1);
}

//Counter for x. In the event that the map uses over 500 iterations of the loop, break because its stuck in an infinite loop and wont solve.
x++;
if(x >= 500) {
c1 = findStart(map);
break;
}
}
//Returns solved Array
return str;
}

//finds start coordinate (simple)
public Coordinate findStart(String[][] map) {
Coordinate start = new Coordinate(0, 0);
for (int row = 0; row < map.length; row++) {
for (int col = 0; col < map[0].length; col++) {
if (map[row][col].equals("S")) {
start = new Coordinate(row, col);
}
}
}
return start;
}

//Finds the Finish coordinate (simple)
public Coordinate findFinish(String[][] map) {
Coordinate finish = new Coordinate(0, 0);
for (int row = 0; row < map.length; row++) {
for (int col = 0; col < map[0].length; col++) {
if (map[row][col].equals("F")) {
finish = new Coordinate(row, col);
}
}
}
return finish;
}
//Checks each individual space within the map array. If space contains W, S, F, D, or is out of the array boundaries its not a clear space.
public boolean isClear(String[][] map, int x, int y) {
if (x >= map.length) {
return false;
}
if (x >= map[0].length) {
return false;
}
if (map[x][y].equals("S")) {
return false;
}
if (map[x][y].equals("D")) {
return false;
}
if (map[x][y].equals("W")) {
return false;
}
return true;
}
}


This post was edited by pwnage007 on Mar 15 2014 12:40am
Member
Posts: 1,995
Joined: Jun 28 2006
Gold: 7.41
Mar 15 2014 12:39am
What are the methods on the interface?

and a Coordinate

This post was edited by Minkomonster on Mar 15 2014 12:40am
Member
Posts: 4,598
Joined: Jul 22 2011
Gold: 3.00
Mar 15 2014 12:42am
Quote (Minkomonster @ Mar 14 2014 11:39pm)
What are the methods on the interface?

and a Coordinate


Whooops sorry here:

Code

public class Coordinate {
private int row;
private int col;

public Coordinate(int row, int col) {
this.row = row;
this.col = col;
}

public int getRow() {
return row;
}

public int getCol() {
return col;
}
public String toString() {
return "Coordinate [row=" + row + ", col=" + col + "]";
}

}



Code

public interface MazeSolver {
public String[][] solve(String[][] map);
}


This post was edited by pwnage007 on Mar 15 2014 12:42am
Member
Posts: 1,995
Joined: Jun 28 2006
Gold: 7.41
Mar 15 2014 12:44am
And you aren't given the Start and End, you have to find them yourself?
Member
Posts: 4,598
Joined: Jul 22 2011
Gold: 3.00
Mar 15 2014 12:47am
Quote (Minkomonster @ Mar 14 2014 11:44pm)
And you aren't given the Start and End, you have to find them yourself?


The start and End were part of the GUI class, but for the purpose of the Solver i used the positions:
Start position [0][1]
Finish position [9][8] in array




Here's an example of an array that I used for my testing, and it prints the Solution in an array.
I hard-coded the array, but in actuality that would be similar to what the user would input as their own maze using the gui

Code
public static void main(String[] args) {
Solver s1 = new Solver();

String[][] map = {
{ "W", "S", "W", "W", "W", "W", "W", "W", "W", "" , "W"},
{ "W", "", "W", "", "", "", "", "", "", "" , "W"},
{ "W", "", "W", "", "W", "W", "W", "W", "W", "", "W" },
{ "W", "", "W", "", "W", "", "W", "W", "W", "" , "W"},
{ "W", "", "W", "", "W", "W", "", "W", "W", "", "W" },
{ "W", "", "W", "", "W", "", "", "W", "W", "" , "W"},
{ "W", "", "W", "", "", "", "W", "W", "W", "" , "W"},
{ "W", "", "W", "", "W", "W", "W", "W", "W", "" , "W"},
{ "W", "", "", "", "W", "", "", "", "W", "" , "W"},
{ "W", "W", "W", "W", "W", "W", "W", "W", "W", "F" , "W"} };
for (int i = 0; i < s1.solve(map).length; i++) {
for (int j = 0; j < s1.solve(map)[0].length; j++) {
System.out.printf("%3s", s1.solve(map)[i][j]);
}
System.out.println();
}
}


This post was edited by pwnage007 on Mar 15 2014 12:58am
Member
Posts: 1,995
Joined: Jun 28 2006
Gold: 7.41
Mar 15 2014 12:48am
Give me a couple minutes to come up with something
Member
Posts: 1,995
Joined: Jun 28 2006
Gold: 7.41
Mar 15 2014 12:57am
So the clear spots are empty strings? What does D mean?
Member
Posts: 4,598
Joined: Jul 22 2011
Gold: 3.00
Mar 15 2014 01:00am
Quote (Minkomonster @ Mar 14 2014 11:57pm)
So the clear spots are empty strings? What does D mean?


Theyre all strings representing these scenarios in the maze :

"" = Clear spot (unvisited & not a wall & neither the starting or ending point)
S = Start
F = Finish
W = walls
X = Clear spot that has been visited
D = spot has been revisited / exhausted all available adjacent clear spaces

while the solver works through the maze, it marks spots as an X, but if it reaches a spot where none of the adjacent spots are clear (dead end), it goes backwards along the traveled path, marking its path with D (no longer a clear space).
Once it reaches the last available clear space, it no longer registers the path filled with "D" as clear so it wont go down that route by default.

Here's an example of what the output would be given the maze on the right if that helps (ill draw up a quick example using the array I posted above


This post was edited by pwnage007 on Mar 15 2014 01:26am
Member
Posts: 1,995
Joined: Jun 28 2006
Gold: 7.41
Mar 15 2014 02:03am
I believe this is a solution

Code
public class RecursiveMazeSolver implements MazeSolver
{

private MazeWalker walker;
public String[][] solve(String[][] map)
{
walker = new MazeWalker(map);
solve();
return map;
}
private void solve()
{
for(Direction d : Direction.values())
{

solve(d);


}

}
private void solve(Direction d)
{
if(!walker.isDone() && walker.canMove(d) && !walker.isVisited(d))
{
walker.move(d);
solve();
if(!walker.isDone())
{
walker.backUp(d.getOpposite());

}


}
}

public static void main(String[] args)
{
MazeSolver s1 = new RecursiveMazeSolver();

String[][] map = {
{ "W", "S", "W", "W", "W", "W", "W", "W", "W", "" , "W"},
{ "W", "", "W", "", "", "", "", "", "", "" , "W"},
{ "W", "", "W", "", "W", "W", "W", "W", "W", "", "W" },
{ "W", "", "W", "", "W", "", "W", "W", "W", "" , "W"},
{ "W", "", "W", "", "W", "W", "", "W", "W", "", "W" },
{ "W", "", "W", "", "W", "", "", "W", "W", "" , "W"},
{ "W", "", "W", "", "", "", "W", "W", "W", "" , "W"},
{ "W", "", "W", "", "W", "W", "W", "W", "W", "" , "W"},
{ "W", "", "", "", "W", "", "", "", "W", "" , "W"},
{ "W", "W", "W", "W", "W", "W", "W", "W", "W", "F" , "W"} };

map = s1.solve(map);
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map[0].length; j++) {
System.out.printf("%3s", map[i][j]);
}
System.out.println();
}
}


}


Code
public class MazeWalker {
private String[][] maze;
private int currR, currC;
private static final String START = "S";
private static final String FINISH = "F";
private static final String WALL = "W";
private static final String VISITED = "D";
private static final String PATH = "X";
private static final String OPEN = "";

public MazeWalker(String[][] maze)
{
this.maze = maze;
findStart();

}

public boolean canMove(Direction d)
{
int dR, dC;

dR = currR + deltaRow(d);
dC = currC + deltaCol(d);


return dR >= 0 && dR < maze.length && dC >= 0 && dC < maze[dR].length && !maze[dR][dC].equals(WALL);

}

public boolean isVisited(Direction d)
{
int r = currR + deltaRow(d);
int c = currC + deltaCol(d);

return canMove(d) && (maze[r][c].equals(VISITED) || maze[r][c].equals(PATH));
}

public boolean isDone()
{
return maze[currR][currC].equals(FINISH);
}

public void backUp(Direction d)
{
if(canMove(d))
{
if(!(maze[currR][currC].equals(START) || maze[currR][currC].equals(FINISH)))
maze[currR][currC] = VISITED;
currR += deltaRow(d);
currC += deltaCol(d);
}

}
public void move(Direction d)
{
//mark path
if(maze[currR][currC].equals(OPEN))
maze[currR][currC] = PATH;

if(canMove(d))
{
currR += deltaRow(d);
currC += deltaCol(d);

}

}



private int deltaRow(Direction d)
{
int delta = 0;
switch(d)
{
case UP:
delta = -1;
break;
case DOWN:
delta = +1;
break;
default:
}

return delta;
}

private int deltaCol(Direction d)
{
int delta = 0;
switch(d)
{
case LEFT:
delta = -1;
break;
case RIGHT:
delta = +1;
break;
default:
}

return delta;
}


private void findStart()
{
for(int i = 0; i < maze.length; i++)
{
for(int j = 0; j < maze[i].length; j++)
{
if(maze[i][j] == START)
{
currR = i; currC = j;
}
}
}
}
}


Code
public enum Direction {
UP,
DOWN,
LEFT,
RIGHT;

private Direction opposite;

static
{
UP.opposite = DOWN;
DOWN.opposite = UP;
LEFT.opposite = RIGHT;
RIGHT.opposite = LEFT;
}

public Direction getOpposite()
{
return opposite;
}
}


This post was edited by Minkomonster on Mar 15 2014 02:23am
Member
Posts: 4,598
Joined: Jul 22 2011
Gold: 3.00
Mar 15 2014 03:55pm
Thanks for the input Minkomonster, your code was definitely better than mine xD.



I have a question regarding a GUI (its for the previously mentioned Solver class)
The GUI works exactly as expected, it creates a frame with a grid of labels that can be clicked by the user to create/destroy the walls of the maze which gets its solution path from the Solver. My question/problem was that although the 2 classes (GUI / SOLVER) work together perfectly, I wasn't ableto figure out a way to make the GUI update its existing grid of labels to highlight the path taken by the solver. Does anyone have any idea how I might have gone about doing this? What I was thinking was that once the user clicks the button "solve" it will update the existing array using the solvers array, and then changes the colors of the labels corresponding to the array's X values (visited spots) to any other color to highlight it but I've no idea where to input that piece of code

Code
package lab8;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.*;

public class MazeGUI {
String[][] map = new String[10][10];
JPanel labelPanel, startPanel;
JLabel labs;

private void createMaze() {
// creates the maze
final JFrame frame = new JFrame("Zombie Run");
frame.setLayout(new BorderLayout());
final JPanel labelPanel = new JPanel();
labelPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
labelPanel.setLayout(new GridLayout(10, 10));
JPanel panelss = new JPanel();
final JLabel unsolvable = new JLabel();

for (int row = 0; row < 10; row++) {
for (int cols = 0; cols < 10; cols++) {
final int i = row;
final int j = cols;
final JLabel labs = new JLabel();

// create and set the labels
labs.setSize(new Dimension(50, 50));
labs.setBorder(BorderFactory.createLineBorder(Color.blue));

labs.setBackground(Color.black);
map[row][cols] = "";

mapArray(row, cols, labs);
labs.addMouseListener(new MouseListener() {
boolean clicked = false;

@Override
public void mouseClicked(MouseEvent arg0) {
// the color changes when clicked

if (clicked == true) {
clicked = false;
labs.setBackground(Color.black);
map[i][j] = "";

} else {
clicked = true;
labs.setBackground(Color.cyan);
map[i][j] = "W";

}

}

@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub

}

@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub

}

@Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub

}

@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub

}
});
if (map[i][j].equals("X") || map[i][j].equals("D")) {
labs.setBackground(Color.RED);
}

if (row == 0 || row == 9) {
labs.setBackground(Color.cyan);
map[row][cols] = "W";
}

if (cols == 0 || cols == 9) {
labs.setBackground(Color.cyan);
map[row][cols] = "W";
}

if (row == 0 && cols == 1) {
labs.setBackground(Color.black);
map[row][cols] = "S";
}
if (row == 9 && cols == 8) {
labs.setBackground(Color.black);
map[row][cols] = "F";

}
labs.setOpaque(true);
labelPanel.add(labs);

}

}

startPanel = new JPanel();
JButton runMaze = new JButton("Feed the Zombie");
runMaze.addActionListener(new ActionListener() {
boolean clicked = false;

@Override
public void actionPerformed(ActionEvent arg0) {
// Makes the zombie find the brain
if (clicked == false) {
clicked = true;
Solver solver;
solver = new Solver();
solver.solve(map);
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map[0].length; j++) {
System.out.printf("%3s", solver.solve(map)[i][j]);
if (solver.solve(map)[9][8].equals("F")) {
unsolvable.setText("UNSOLVABLE MAZE");
frame.pack();
}
}
System.out.println();
labelPanel.validate();
labelPanel.repaint();
}
}

}
});
panelss.add(unsolvable);
runMaze.setBackground(Color.green);
startPanel.add(runMaze);
runMaze.add(labelPanel);
frame.add(labelPanel, BorderLayout.NORTH);
frame.add(startPanel, BorderLayout.SOUTH);
frame.add(panelss, BorderLayout.EAST);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map[0].length; j++) {
System.out.printf("%3s", map[i][j]);
}
System.out.println();
}
System.out.println();
}

private void mapArray(int row, int cols, JLabel labs) {
// checks the space and inserts string

if (row == 0 || row == 9) {
map[row][cols] = "W";
map[row][cols] = "W";
}
if (cols == 0 || cols == 9) {
map[row][cols] = "W";
map[row][cols] = "W";
}
if (row == 0 && cols == 1) {
map[0][1] = "S";
labs.setText("Start");
}
if (row == 9 && cols == 8) {
map[row][8] = "F";
labs.setText("End");
} else {
map[row][cols] = "";
}
}

public static void main(String[] args) {
// runs the program
MazeGUI c = new MazeGUI();
c.createMaze();
}
};


This post was edited by pwnage007 on Mar 15 2014 04:02pm
Go Back To Programming & Development Topic List
12Next
Add Reply New Topic New Poll