Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I am working on a Robot Maze where the robot finds the target without crashing into walls. I know I've missed something or done something incorrectly (most likely a couple of things haha) but I've been racking my brain over it for a couple of hours now and tried several alternatives. I’m pretty sure my error is either where and how I declared the ArrayList.
It cannot find the symbol for the passageDirections and nonwallDirections.
Any help is appreciated :)
PS: I am a beginner programmer, still learning so explain your answer as if you were to explain it to a three year old :)
Your problem is in the nonwallExits method - you cannot have any code after a return statement, as this statement terminates the method. Just move return nonwallExits; to be the last line of the method and you should be fine.
I for example see a problem here in this method:
/* Junction method states if there is more than one passage, it will randomly select one.
If there is no passage, it will randomly select a nonwall/BeenBefore direction. */
public int junction(IRobot robot) {
if (passageExits(robot) >= 1) {
int randomPassage = ((Math.random())*(passageDirections.length()));
passageDirections.get(randomPassage);
} else {
int randomNonwall = ((Math.random())*(nonwallDirections.length()));
nonwallDirections.get(randomNonwall);
}
}
Here you are using the lists 'passageDirections' and 'nonwallDirections' without declaring them first. I see that you have declared those lists in other functions as local variables, which means that they exist as long as the function is executing which they are declared in. So if you want to use them in other functions, you shouldn make them somehow global or pass them around as parameters...
Without fully understanding your code, I would suggest try to do something like this to make the code "compileable":
import uk.ac.warwick.dcs.maze.logic.IRobot;
import java.util.ArrayList;
import java.util.*;
public class Explorer2 {
private ArrayList<Integer> passageDirections = new ArrayList<Integer>();
private ArrayList<Integer> nonWallDirections = new ArrayList<Integer>();
private Random random = new Random();
public void controlRobot (IRobot robot) {
int exits = nonwallExits(robot);
int direction;
if (exits < 2) {
direction = deadEnd(robot);
} else if (exits == 2) {
direction = corridor(robot);
} else if (exits == 3) {
direction = junction(robot);
} else {
direction = crossroads(robot);
}
robot.face(direction);
}
/* Deadend method will make the robot turn around except from the beginning.
Because the robot can face any direction at the start, it will follow
the one and only passage it will detect. */
public int deadEnd (IRobot robot) {
if (passageExits(robot) == 0)
return IRobot.BEHIND;
else
return -1; //FIXME: return correct direction!!!
}
/* Corridor method will make the robot follow the one and only passage. */
public int corridor (IRobot robot) {
return -1; //FIXME: return correct direction!!!
}
/* Junction method states if there is more than one passage, it will randomly select one.
If there is no passage, it will randomly select a nonwall/BeenBefore direction. */
public int junction(IRobot robot) {
if (passageExits(robot) >= 1) {
int randomPassage = random.nextInt(passageDirections.size());
return passageDirections.get(randomPassage);
} else {
int randomNonwall = random.nextInt(nonWallDirections.size());
return nonWallDirections.get(randomNonwall);
}
}
/* Crossroads method states if there is more than one passage, it will randomly select one.
If there is no passage, it will randomly select a nonwall/BeenBefore direction. */
public int crossroads (IRobot robot) {
if (passageExits(robot) >= 1) {
int randomPassage = random.nextInt(passageDirections.size());
return passageDirections.get(randomPassage);
} else {
int randomNonwall = random.nextInt(nonWallDirections.size());
return nonWallDirections.get(randomNonwall);
}
}
//Calculates number of exits and stores the direction of the exits in an ArrayList
private int nonwallExits (IRobot robot) {
int nonwallExits = 4;
if(robot.look(IRobot.AHEAD) == IRobot.WALL)
nonwallExits--;
if(robot.look(IRobot.LEFT) == IRobot.WALL)
nonwallExits--;
if(robot.look(IRobot.RIGHT) == IRobot.WALL)
nonwallExits--;
if(robot.look(IRobot.BEHIND) == IRobot.WALL)
nonwallExits--;
for(int direction = IRobot.AHEAD; direction < IRobot.LEFT; direction++) {
if(robot.look(direction) != IRobot.PASSAGE)
nonWallDirections.add(direction);
}
return nonwallExits;
}
//Calculates number of passages and stores the direction of the passages in an ArrayList
private int passageExits (IRobot robot) {
int passageExits = 0;
if(robot.look(IRobot.AHEAD) == IRobot.PASSAGE)
passageExits++;
if(robot.look(IRobot.LEFT) == IRobot.PASSAGE)
passageExits++;
if(robot.look(IRobot.RIGHT) == IRobot.PASSAGE)
passageExits++;
for(int direction = IRobot.AHEAD; direction < IRobot.LEFT; direction++) {
if(robot.look(direction) == IRobot.PASSAGE)
passageDirections.add(direction);
}
return passageExits;
}
}
This way you declare the lists to be instance variables, meaning that they represent the state of an Explorer2 object. Now all the instance methods operating on the Explorer2 objects can access these variables as this.passageDirections and this.nonWallDirections, where this refers to the object you are currently "working on". (and if there are no ambiguities you can skip the this. part).
Related
I'm a bit new to Android Studio and I want to make small quiz app. On the start screen, there are two buttons. With the first button, you just click through your questions and you can start at a specific question number if you want (this is already working). With the second button, I wand to create a random mode BUT every question should only be asked once. So there should not be the possibility to get the same questions twice.
For that I created an Array:
public ArrayList<Integer> questionsDone = new ArrayList<>();
And got the lenght of the Question Array:
public int maxQuestions = QuestionLibrary.nQuestions.length;
then I have a function for updating the question:
private void updateQuestion(){
//RANDOM MODE
if (startNumber == -1) {
if (questionsDone.size() >= maxQuestions) {
finishQuiz();
} else {
nQuestionNumber = (int) (Math.random() * (maxQuestions));
do {
if (questionsDone.contains(nQuestionNumber)) {
nQuestionNumber = nQuestionNumber - 1;
if (nQuestionNumber == -1) {
nQuestionNumber = maxQuestions-1;
}
} else {
questionsDone.add(nQuestionNumber);
notDone = true;
}
} while (notDone = false);
}
}
nQuestionView.setText(nQuestionLibrary.getQuestion(nQuestionNumber));
nButtonChoice1.setText(nQuestionLibrary.getChoice1(nQuestionNumber));
nButtonChoice2.setText(nQuestionLibrary.getChoice2(nQuestionNumber));
nButtonChoice3.setText(nQuestionLibrary.getChoice3(nQuestionNumber));
So my idea was that when I start random mode, I pass the value "-1". If the size of the array (questions were done) equals the number of the available questions the quiz should stop. If not, I just get a random number and multiply it by the number of questions. Then there is a do-while function which makes sure that if the questionsDone-Array already contains the number it will get a new number and after getting a number which is not in the array it will be stored in the array.
This is what the app does when I click on random mode:
It always shows a random question but sometimes one questions are asked twice and more. And suddenly it stops (the app is loading the result page) but the stop does not come with a pattern. When I have 7 questions, each question is minimum asked once and sometimes it stops after 15, sometimes after 20 questions and so on.
Does someone know why?
Since I wanted to give an update to both of you, I posted an anwser. Thank you guys for the good input. It works now and this is how I got it working:
First I created an array:
public ArrayList<Integer> availableQuestions = new ArrayList<>();
Then i put integers in the array starting by 0 and ending maxQuestions-1 and I shuffled the values:
if (startNumber == -1) {
Integer i = 0;
do {
availableQuestions.add(i);
i++;
}while (i < maxQuestions);
Collections.shuffle(availableQuestions);
}
Then on every button click this function starts to work (j was declared as an integer with the value 0 before):
if (startNumber == -1) {
nQuestionNumber = availableQuestions.get(j);
j++;
}
Instead of keeping QuestionsDone, I suggest to keep QuestionAvailable (i.e. not yet answered), as indices of questions in the library
List<Integer> availableQuestions = null;
...................................................
if (startNumber == -1) {
if (availableQuestions.isEmpty()) {
finishQuiz();
availableQuestions = null;
} else {
if (availableQuestions == null) {
// Starting a new quiz
availableQuestions = new List<Integer>(maxQuestions);
for (int i=0; i<maxQuestons; i++) availableQuestions.Add(i);
}
int nQuestionOrder = (int) (Math.random() * availableQuestions.size());
nQuestionNumber = availableQuestions.get(nQuestionOrder);
availableQuestions.removeAt(nQuestionOrder);
}
}
This is the problem:
You have maps of parts of the space station, each starting at a prison exit and ending at the door to an escape pod. The map is represented as a matrix of 0s and 1s, where 0s are passable space and 1s are impassable walls. The door out of the prison is at the top left (0,0) and the door into an escape pod is at the bottom right (w-1,h-1).
Write a function answer(map) that generates the length of the shortest path from the prison door to the escape pod, where you are allowed to remove one wall as part of your remodeling plans. The path length is the total number of nodes you pass through, counting both the entrance and exit nodes. The starting and ending positions are always passable (0). The map will always be solvable, though you may or may not need to remove a wall. The height and width of the map can be from 2 to 20. Moves can only be made in cardinal directions; no diagonal moves are allowed.
To Summarize the problem: It is a simple rat in a maze problem with rat starting at (0,0) in matrix and should reach (w-1,h-1). Maze is a matrix of 0s and 1s. 0 means path and 1 means wall.You have the ability to remove one wall(change it from 0 to 1). Find the shortest path.
I've solved the problem but 3 of 5 testcases fail and I don't know what those test cases are. and I'm unable to figure out why. Any help would be greatly appreciated.Thanks in Advance. Here is my code:
import java.util.*;
class Maze{//Each cell in matrix will be this object
Maze(int i,int j){
this.flag=false;
this.distance=0;
this.x=i;
this.y=j;
}
boolean flag;
int distance;
int x;
int y;
}
class Google4_v2{
public static boolean isPresent(int x,int y,int r,int c)
{
if((x>=0&&x<r)&&(y>=0&&y<c))
return true;
else
return false;
}
public static int solveMaze(int[][] m,int x,int y,int loop)
{
int r=m.length;
int c=m[0].length;
int result=r*c;
int min=r*c;
Maze[][] maze=new Maze[r][c];//Array of objects
for(int i=0;i<r;i++)
{
for(int j=0;j<c;j++)
{
maze[i][j]=new Maze(i,j);
}
}
Queue<Maze> q=new LinkedList<Maze>();
Maze start=maze[x][y];
Maze[][] spare=new Maze[r][c];
q.add(start);//Adding source to queue
int i=start.x,j=start.y;
while(!q.isEmpty())
{
Maze temp=q.remove();
i=temp.x;j=temp.y;
int d=temp.distance;//distance of a cell from source
if(i==r-1 &&j==c-1)
{
result=maze[i][j].distance+1;
break;
}
maze[i][j].flag=true;
if(isPresent(i+1,j,r,c)&&maze[i+1][j].flag!=true)//check down of current cell
{
if(m[i+1][j]==0)//if there is path, add it to queue
{
maze[i+1][j].distance+=1+d;
q.add(maze[i+1][j]);
}
if(m[i+1][j]==1 && maze[i+1][j].flag==false && loop==0)//if there is no path, see if breaking the wall gives a path.
{
int test=solveMaze(m,i+1,j,1);
if(test>0)
{
test+=d+1;
min=(test<min)?test:min;
}
maze[i+1][j].flag=true;
}
}
if(isPresent(i,j+1,r,c)&&maze[i][j+1].flag!=true)//check right of current cell
{
if(m[i][j+1]==0)
{
maze[i][j+1].distance+=1+d;
q.add(maze[i][j+1]);
}
if(m[i][j+1]==1 && maze[i][j+1].flag==false && loop==0)
{
int test=solveMaze(m,i,j+1,1);
if(test>0)
{
test+=d+1;
min=(test<min)?test:min;
}
maze[i][j+1].flag=true;
}
}
if(isPresent(i-1,j,r,c)&&maze[i-1][j].flag!=true)//check up of current cell
{
if(m[i-1][j]==0)
{
maze[i-1][j].distance+=1+d;
q.add(maze[i-1][j]);
}
if(m[i-1][j]==1 && maze[i-1][j].flag==false && loop==0)
{
int test=solveMaze(m,i-1,j,1);
if(test>0)
{
test+=d+1;
min=(test<min)?test:min;
}
maze[i-1][j].flag=true;
}
}
if(isPresent(i,j-1,r,c)&&maze[i][j-1].flag!=true)//check left of current cell
{
if(m[i][j-1]==0)
{
maze[i][j-1].distance+=1+d;
q.add(maze[i][j-1]);
}
if(m[i][j-1]==1 && maze[i][j-1].flag==false && loop==0)
{
int test=solveMaze(m,i,j-1,1);
if(test>0)
{
test+=d+1;
min=(test<min)?test:min;
}
maze[i][j-1].flag=true;
}
}
}
return ((result<min)?result:min);
}
public static int answer(int[][] m)
{
int count;
int r=m.length;
int c=m[0].length;
count=solveMaze(m,0,0,0);
return count;
}
public static void main(String[] args)
{
Scanner sc=new Scanner(System.in);
System.out.println("enter row size ");
int m=sc.nextInt();
System.out.println("enter column size ");
int n=sc.nextInt();
int[][] maze=new int[m][n];
System.out.println("Please enter values for maze");
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
maze[i][j]=sc.nextInt();
}
}
int d=answer(maze);
System.out.println("The maze can be solved in "+d+" steps");
}
}
Found the problem. maze[i][j].flag=true; needs to be put as soon as the cell is visited, inside the if(m[i+1][j]==0) condition. Otherwise, the distance for same cell can be added by more than one cells
Unfortunately it's quite hard to help you because your code is very difficult to read. The variables are generally single characters which makes it impossible to know what they are supposed to represent. Debugging it would be more help than most of us are willing to give :-)
I suggest you go about debugging your code as follows:
Split your solveMaze method into a number of smaller methods that each perform much simpler functions. For example, you have very similar code repeated 4 times for each direction. Work to get that code in a single method which can be called 4 times. Move your code to create the array into a new method. Basically each method should do one simple thing. This approach makes it much easier to find problems when they arise.
Write unit tests to ensure each of those methods do exactly what you expect before attempting to calculate the answer for entire mazes.
Once all the methods are working correctly, generate some mazes starting from very simple cases to very complex cases.
When a case fails, use an interactive debugger to walk through your code and see where it is going wrong.
Good luck.
I have looked everywhere for answers for fixing my code but after long hours spent trying to debug it I find myself hopelessly stuck. The problem is that my minimax function will not return the correct values for the best possible move, I even attempted to fix it by storing the best first moves (when depth = 0), but if the solution is not obvious, then the algorithm fails horribly. I also tried modifying the return values from the base cases in order to prioritize earlier wins, but this didn't solve the problem.
Currently I am testing the function on a TicTacToe board and the helper classes (Eg getMoves() or getWinner are working properly), I know my style is not the most efficient but I needed the code to be fairly explicit.
By adding a bunch of print statements I realized that under some circumstances my bestFinalMoves ArrayList was not modified, so this may be related to the issue. Another related problem is that unless the algorithm finds a direct win (in the next move), then instead of choosing a move that may lead to a future win or tie by blocking a square that leads to an immediate block, it just yields the space for the minimizing player to win.
For example on the board:
aBoard= new int[][] {
{0,1,0}, // 1 is MAX (AI), -1 is MIN (Human)
{-1,0,0},
{-1,0,0}
};
Yields the incorrect result of 2,0, where it is obvious that it should be 0,0, so that it blocks the win for the minimizing player, and the bestFinalMoves ArrayList is empty.
private result miniMaxEnd2(Board tmpGame, int depth){
String winner = tmpGame.whoWon();
ArrayList<Move> myMoves = tmpGame.getMoves();
if (winner == 'computer'){ //Base Cases
return new result(1000);
}else if (winner == 'human'){
return new result(-1000);
}
else if (winner == 'tie'){
return new result(0);
}
if (tmpGame.ComputerTurn) {//MAX
bestScore = -99999;
for (Move m : tmpGame.getMoves()){
Board newGame = new Board(tmpGame,!tmpGame.ComputerTurn, m);
result aScore = miniMaxEnd2(newGame, depth+1);
if (aScore.score > bestScore) {
bestScore = aScore.score;
bestMove = m;
if (depth == 0) {
bestFinalMoves.add(m);
}
}
}
return new result(bestScore, bestMove);
} else {//MIN
bestScore = 99999;
for (Move m : tmpGame.getMoves()) {
Board newGame = new Board(tmpGame,!tmpGame.ComputerTurn, m);
result aScore = miniMaxEnd2(newGame, depth + 1);
if (aScore.score < bestScore) {
bestScore = aScore.score;
bestMove = m;
}
}
return new result(bestScore,bestMove);
}
}
I know this was a long post, but I really appreciate your help. The full code can be accessed at https://github.com/serch037/UTC_Connect
The bestScore and bestMove variables must be declared as local variables inside the miniMaxEnd2 method for this logic to work properly.
Those variables' values are being replaced by the recursive call.
I'm trying some Java recently and look for some review of my style. If You like to look at this exercise placed in the image, and tell me if my style is good enought? Or maybe it is not good enought, so You can tell me on what aspect I should work more, so You can help me to improve it?
exercise for my question
/*
* File: MathQuiz.java
*
* This program produces Math Quiz.
*/
import acm.program.*;
import acm.util.*;
public class MathQuiz extends ConsoleProgram {
/* Class constants for Quiz settings. */
private static final int CHANCES = 3;
private static final int QUESTIONS = 5;
private static final int MIN = 0;
private static final int MAX = 20;
/* Start program. Number of questions to ask is assigned here. */
public void run() {
println("Welcome to Math Quiz");
while(answered != QUESTIONS) {
produceNumbers();
askForAnswer();
}
println("End of program.");
}
/* Ask for answer, and check them. Number of chances includes
* first one, where user is asked for reply. */
private void askForAnswer() {
int answer = -1;
if(type)
answer = readInt("What is " + x + "+" + y + "?");
else
answer = readInt("What is " + x + "-" + y + "?");
for(int i = 1; i < CHANCES+1; i++) {
if(answer != solution) {
if(i == CHANCES) {
println("No. The answer is " + solution + ".");
break;
}
answer = readInt("That's incorrect - try a different answer: ");
} else {
println("That's the answer!");
break;
}
}
answered++;
}
/* Produces type and two numbers until they qualify. */
private void produceNumbers() {
produceType();
produceFirst();
produceSecond();
if(type)
while(x+y >= MAX) {
produceFirst();
produceSecond();
}
else
while(x-y <= MIN) {
produceFirst();
produceSecond();
}
calculateSolution();
}
/* Calculates equation solution. */
private void calculateSolution() {
if(type) solution = x + y;
else solution = x - y;
}
/* Type of the equation. True is from plus, false is for minus. */
private void produceType() {
type = rgen.nextBoolean();
}
/* Produces first number. */
private void produceFirst() {
x = rgen.nextInt(0, 20);
}
/* Produces second number. */
private void produceSecond() {
y = rgen.nextInt(0, 20);
}
/* Class variables for numbers and type of the equation. */
private static boolean type;
private static int x;
private static int y;
/* Class variables for equation solution. */
private static int solution;
/* Class variable counting number of answered equations,
* so if it reaches number of provided questions, it ends */
private static int answered = 0;
/* Random generator constructor. */
RandomGenerator rgen = new RandomGenerator();
}
One thing I noticed was that all of your methods take no parameters and return void.
I think it would be clearer if you use method parameters and return values to show the flow of data through your program instead of using the object's state to store everything.
There are a few things you should do differently, and a couple you could do differently.
The things you should do differently:
Keep all fields together.
static fields should always be in THIS_FORM
you've used the static modifier for what clearly look like instance fields. (type,x,y,solution, answered). This means you can only ever run one MathsQuiz at a time per JVM. Not a big deal in this case, but will cause problems for more complex programs.
produceFirst and produceSecond use hardcoded parameters to nextInt rather than using MAX and MIN as provided by the class
There is no apparent need for answered to be a field. It could easily be a local variable in run.
Things you should do differently:
There is a small possibility (however tiny), that produceNumbers might not end. Instead of producing two random numbers and hoping they work. Produce one random number and then constrain the second so that a solution will always be formed. eg. say we are doing and addition and x is 6 and max is 20. We know that y cannot be larger than 14. So instead of trying nextInt(0,20), you could do nextInt(0,14) and be assured that you would get a feasible question.
For loop isn't really the right construct for askForAnswer as the desired behaviour is to ask for an answer CHANCES number of times or until a correct answer is received, whichever comes first. A for loop is usually used when you wish to do something a set number of times. Indeed the while loop in run is a good candidate for a for loop. A sample while loop might look like:
int i = 1;
boolean correct = (solution == readInt("What is " + x + "+" + y + "?"));
while (i < CHANCES && !correct) {
correct = (solution == readInt("Wrong, try again."));
i++;
}
if (correct) {
println("Well done!");
} else {
println("Nope, the answer is: "+solution);
}
Looks like a very clean program style. I would move all variables to the top instead of having some at the bottom, but other than that it is very readable.
Here is something I'd improve: the boolean type that is used to indicate whether we have an addition or subtraction:
private void produceType() {
type = rgen.nextBoolean();
}
produceType tells, that something is generated and I'd expect something to be returned. And I'd define enums to represent the type of the quiz. Here's my suggestion:
private QuizType produceType() {
boolean type = rgen.nextBoolean();
if (type == true)
return QuizType.PLUS;
else
return QuizType.MINUS;
}
The enum is defined like this:
public enum QuizType { PLUS, MINUS }
Almost good I have only a few improvements:
variables moves to the top
Inside produceNumbers and your while you have small repeat. I recommend refactor this
Small advice: Code should be like books - easy readable - in your run() method firstly you call produceNumber and then askForAnswer. So it will be better if in your code you will have the same order in definitions, so implementation askForAnswer before produceNumber. But it isn't necessary
Pay attention to have small methods. A method shouldn't have much to do - I think that askForAnswer you could split to two methods
I have an assignment where I am supposed to be able to display the path of a maze from the entrance to the exit and I have gotten it to work to a degree but when the maze gets more complicated with dead ends and such the program goes into infinite recursion. If you could give me any help to point me in the right direction it would be much appreciated.
Mu current theory can be found in the Room class.
Here is the Room class where the references to each room connecting the maze are stored, kind of like a linked list linked in 6 directions, north, south, east, west, up, and down.
import java.util.ArrayList;
public class OurRoom
{
private OurRoom exits[];
private String name;
private static ArrayList<OurRoom> list;
public OurRoom()
{
this(null);
}
public OurRoom(String name)
{
this.name = name;
this.list = new ArrayList<OurRoom>();
exits = new OurRoom[Direction.values().length];
for(OurRoom exit : exits)
{
exit = null;
}
}
public void connectTo(OurRoom theOtherRoom, Direction direction)
{
exits[direction.ordinal()] = theOtherRoom;
theOtherRoom.exits[direction.getOpposite().ordinal()] = this;
}
public OurRoom getExit(Direction direction)
{
return exits[direction.ordinal()];
}
public boolean lookExit(Direction direction)
{
return exits[direction.ordinal()] != null;
}
public String getName() {
return name;
}
public OurRoom solveRecursively(OurRoom exit) {
list.add(this);
if(this == exit) {
return this;
}else {
OurRoom temp = null;
if(lookExit(Direction.east)) {
temp = exits[Direction.east.ordinal()].solveRecursively(exit);
}
else if(lookExit(Direction.up)) {
temp = exits[Direction.up.ordinal()].solveRecursively(exit);
}
else if(lookExit(Direction.south)) {
temp = exits[Direction.south.ordinal()].solveRecursively(exit);
}
else if(lookExit(Direction.down)) {
temp = exits[Direction.down.ordinal()].solveRecursively(exit);
}
else if(lookExit(Direction.west)) {
temp = exits[Direction.west.ordinal()].solveRecursively(exit);
}
else if(lookExit(Direction.north)) {
temp = exits[Direction.north.ordinal()].solveRecursively(exit);
}
return temp;
}
}
public ArrayList<OurRoom> getList() {
return list;
}
}
Here is the Direction enum
public enum Direction
{
north, south, east, west, up, down;
public Direction getOpposite()
{
switch(this)
{
case north:
return south;
case south:
return north;
case east:
return west;
case west:
return east;
case up:
return down;
case down:
return up;
default:
return this;
}
}
}
And here is an example of how the maze is built.
import java.util.ArrayList;
import java.util.Iterator;
public class OurMaze
{
private OurRoom entrance, exit;
public OurMaze()
{
this(1);
}
public OurMaze(int mazeNumber)
{
entrance = null;
exit = null;
switch(mazeNumber)
{
case 0:
break;
case 1:
this.buildMaze1();
break;
default:
}
}
public OurRoom getEntrance()
{
return entrance;
}
public OurRoom getExit()
{
return exit;
}
public Iterator<OurRoom> findPathRecursively() {
entrance.solveRecursively(exit);
ArrayList<OurRoom> list = entrance.getList();
return list.iterator();
}
private void buildMaze1()
{
OurRoom room1, room2;
room1 = new OurRoom("Room 1");
room2 = new OurRoom("Room 2");
room1.connectTo(room2, Direction.north);
entrance = room1;
exit = room2;
}
public static void main(String[] args) {
OurMaze maze = new OurMaze(1);
}
}
You just need to keep two-dimensional array with values indicating whether the cell was visited or not: you don't want to go through the same cell twice.
Apart from that, it's just breadth-first-search (depth-first-search is fine too, if you don't want shortest path).
Some links
http://en.wikipedia.org/wiki/Flood_fill
http://en.wikipedia.org/wiki/Breadth-first_search
http://en.wikipedia.org/wiki/Depth-first_search
Sample search routine.
void dfs(int i, int j) {
if cell(i, j) is outside of maze or blocked {
return
}
if visited[i][j] {
return
}
visited[i][j] = true;
dfs(i + 1, j);
dfs(i - 1, j);
dfs(i, j + 1);
dfs(i, j - 1);
}
Path itself can be found if, like with visited, for each cell you keep cell from which you came to it. So, printing would look like this (just a pseudocode).
var end = exit_point;
while (end != start_point) {
print end;
end = came_from[end];
}
edit
The code above is for two-dimensional maze and I just noticed that you have three-dimensional version. But it's easy to introduce third coordinate in the example above.
Let me know if there're any difficulties.
Others have described appropriate approaches to solving this problem, but I think it's worth pointing out exactly why your program won't scale to more complex mazes.
As duffymo hinted, the problem is that your algorithm doesn't do any backtracking correctly - when it takes a branch that turns out to be a dead end, and returns to a previous square, it doesn't remember this at all. And since it tries the exits in a fixed order, it will always retry that failed exit immediately.
Look at how the solveRecursively function is defined, and you'll see that from any given room, only one direction would ever be tried. If a room has an exit to the east, then it doesn't even matter if it has any other exits since the if-else block would never consider them.
So as it turns out, your solving logic will fail (i.e go into an infinite loop between two rooms) in any case where the correct solution isn't the "first" exit from each room in the order you've defined there.
(A quick fix would be to store a simple boolean flag against each room/direction. Set it before you call the recursive call, then if you end up back in that room again, you know that direction doesn't work out and can let the if block fall through to try one of the other exits. Refactoring this to use typical BFS logic, as Nikita suggests, would be better overall)
I'd bet you need a tree of some kind to keep track of where you've been.
When recursion fails, it usually means that the person writing the method didn't expression the stopping condition properly. What's yours?
I think this was the first game I ever encountered on a computer. It was an IBM mainframe at the school where I got my undergraduate degree. The I/O was on a paper teletype. Many salt tears were wept at the account dollars that were flushed away playing this maze game. Great fun.
When solving the maze, represent it as a 6-ary graph where each node is a room and each edge represents travel in one of the six directions. You can then apply some of the well known algorithms for finding shortest paths.
This page describes various solutions for finding paths through graphs that are structured as such. Your graph is easier than the ones that describe real-world maps, since the cost of traveling down any edge is equal to the cost of traveling down any other edge.
Be especially sure to look at Dijkstra's algorithm.