Recursively finding a path on a 2d array [Java] - java

I want to write a programm in java that finds a path on a 2d Array with recursion.
The 2d Array named "gitter" consists of Objects of the type 'field'.
Every field will be initialized with a random number in the range of 100-999. If a field was initialized with a number, in which one of the digits is a prime, it is a "trap". The fields can be imagined as squares, so i can move only in 4 directions:
public class Field {
int number;
boolean visited;
Field() {
Random rn = new Random();
this.number = rn.nextInt((999+1) - 100) + 100;
this.visited = false;
}
boolean isTrap() {
String str = String.valueOf(number);
if(str.contains("2") | str.contains("3") | str.contains("5") | str.contains("7")) {
return true;
} return false;
}
}
The start-field and the end-field of the path shall have a manhattan-distance of more than 2. The problem so far is the recursion. The more i think about it, the if cases in it are just getting longer and longer. I added also a boolean variable "visited", for not visiting the same field twice, but no success. Is the while-loop necessary for the recursion? If not (what i guess), what is the easiest way to make the recursion stop, after i found a path? i tried it with and without a while-loop, but couldn't find any solution.
public class Gitter {
Field[][] gitter = new Field[10][10];
List<Field> path = new ArrayList<Field>();
public Field[] getStartAndGoal() {
boolean notFound = true;
Field[] startGoal = new Field[2];
while(notFound) {
Random x0 = new Random();
Random y0 = new Random();
Random x1 = new Random();
Random y1 = new Random();
int row0 = x0.nextInt((9)+1);
int line0 = y0.nextInt((9)+1);
int row1 = x1.nextInt((9)+1);
int line1 = y1.nextInt((9)+1);
int distance = Math.abs(row1-row0) + Math.abs(line1-line0);
if(distance>2){
if(gitter[row0][line0].isTrap() == false && gitter[row1][line1].isTrap() ==false) {
notFound = false;
Field start = gitter[row0][line0];
Field goal = gitter[row1][line1];
startGoal[0] = start;
startGoal[1] = goal;
}
}
}
return startGoal;
}
public boolean findPath(Field start, Field goal) {
boolean solved = false;
for(int i = 0; i < 10; i++) {
for(int j = 0; j < 10; j++) {
if(gitter[i][j].equals(start)) {
gitter[i][j].visited=true;
while(solved==false){
if((i+1)<10 && gitter[i+1][j].isTrap()==false && gitter[i+1][j].visited == false && findPath(gitter[i+1][j], goal)){
gitter[i+1][j].visited = true;
path.add(gitter[i+1][j]);
return true;
}
if((i-1)>0 && gitter[i-1][j].isTrap()==false && gitter[i-1][j].visited == false && findPath(gitter[i-1][j], goal)){
gitter[i-1][j].visited = true;
path.add(gitter[i-1][j]);
return true;
}
if((j+1)<10 && gitter[i][j+1].isTrap()==false && gitter[i][j+1].visited == false && findPath(gitter[i][j+1], goal)){
gitter[i][j+1].visited = true;
path.add(gitter[i][j+1]);
return true;
}
if((j-1)>10 && gitter[i][j-1].isTrap()==false && gitter[i][j-1].visited == false && findPath(gitter[i][j-1], goal)){
gitter[i][j-1].visited = true;
path.add(gitter[i][j-1]);
return true;
}
for(i=0; i<path.size(); i++) {
if(path.get(i).equals(goal)){
solved = true;
break;
}
}
}
}
}
} return false;
}
Does anybody got a hint for me?

Looking at the problem, there are many variables that aren't necessarily too important to consider yet would allow me to create a simple solution quicker. If you use concepts from my solution, just take notes that there are a variety of restraints completely ignored which can easily be implemented.
Recursion
Typically recursion is used to replace the iterative behaviour of loops. With this problem you want to search every possible path if there exists a path that can take you from point A to point B, or up until you have determined there exists a path.
The more i think about it, the if cases in it are just getting longer and longer
Easy. Create a helper method that does the calculation for you so that you avoid repetitive use of the same code.
Is the while-loop necessary for the recursion?
I'm not entirely sure what you're doing with your loops, but it would definitely be possible (and much more elegant) to do away with loops altogether for this solution.
what is the easiest way to make the recursion stop, after i found a path?
Once you have found a solution, return true. The previous recursive step will receive a true value and then understand that somewhere along the line you have reached the end so it too should return true. This is where the recursive aspect can produce such an elegant solution.
Solution
public static void main (String args[]) {
int n = 10;
int[][] map = new int[n][n]; // Generate random data however you want
boolean[][] visited = new boolean[n][n]; // Defaults to false
int[2] start = new int[]{0,0}; // Top left corner
int[2] end = new int[]{n-1, n-1}; // Bottom right corner
boolean path = pathExists(map, visited, start, end);
}
public static boolean pathExists(int[][] map, boolean[][] visited, int[] current, int[] end) {
int x = current[0];
int y = current[1];
// If not in bounds, not a valid route
if (!inBounds(map, visited, current)) return false;
// If this is the end, return true!
if (current[0] == end[0] && current[1] == end[1]) return true;
// Attempt each cardinal direction. If you find a return, return true
if (pathExists(map, markPresent(visited, current), new int[]{x-1,y}, end)) return true;
if (pathExists(map, markPresent(visited, current), new int[]{x+1,y}, end)) return true;
if (pathExists(map, markPresent(visited, current), new int[]{x,y+1}, end)) return true;
if (pathExists(map, markPresent(visited, current), new int[]{x,y-1}, end)) return true;
// There is no solution down this path
return false;
}
public static boolean[][] markPresent(boolean[][] visited, int[] current) {
// Make a deep copy - Is needed to prevent interferance
boolean[][] copy = new boolean[visited.length][visited[0].length];
for (int i = 0; i < copy.length; i++)
copy[i] = Arrays.copyOf(visited[i], visited[i].length);
// Mark where you currently are
copy[current[0]][current[1]] = true;
return copy;
}
public static boolean inBounds(int[][] map, boolean[][] visited, int[] position) {
int x = position[0];
int y = position[1];
// Make sure it is within the bounds of the map
if (x < 0 || y < 0 || x >= map.length || y >= map.length) return false;
// Check if the current block is a barrier
if (isBarrier(map, position)) return false;
// If you have visited this path before, don't do it again
if (visited[x][y]) return false;
// Otherwise, check the path!
return true;
}
public static boolean isBarrier(int[][] map, int[] position) {
// Return your definition of a barrier (I used modulo 10 for testing
}
I've done minimal testing, so if you can see any glearing issues feel free to leave a comment.
From what I can gather, you are wanting to detect whether a path exists between to points. If you are wanting to detect the shortest distance (using the Manhatten metric), instance of returning boolean values you could return integer value. If you want to find the path itself, you can return an array of points and recursively append all points to another array. Here's a modified version that finds the shortest path:
public static ArrayList<int[]> findDistance(int[][] map, boolean[][] visited, int[] current, int[] end) {
int x = current[0];
int y = current[1];
ArrayList<int[]> ret = new ArrayList<>();
ret.add(current);
if (!inBounds(map, visited, current)) return new ArrayList<>(); // Return empty array
if (current[0] == end[0] && current[1] == end[1]) return ret; // Return current location
ArrayList<ArrayList<int[]>> paths = new ArrayList<>();
paths.add(findDistance(map, markPresent(visited, current), new int[]{x-1,y}, end));
paths.add(findDistance(map, markPresent(visited, current), new int[]{x+1,y}, end));
paths.add(findDistance(map, markPresent(visited, current), new int[]{x,y+1}, end));
paths.add(findDistance(map, markPresent(visited, current), new int[]{x,y-1}, end));
// Find the shortest path that leads somewhere
paths.removeIf((ArrayList<int[]> data) -> data.size() == 0);
paths.sort(Comparator.comparing(ArrayList::size));
// If the size of the paths is 0, no path was found
if (paths.size() == 0) {
return new ArrayList<>();
} else {
//Append the found path to the current location and return the list
ret.addAll(paths.get(0));
return ret;
}
}
EDIT
I had a terrible feeling I had forgotten something obvious - I had. You will need to clone the visited array so that you don't interfere with each recursive step. I've added a method to do that.

Adding some extra things to make things easier:
start and goal are attributes of Gitter class
findPath has been divided in tho methods, one public and another private
Code
public class Gitter {
Field[][] gitter = new Field[10][10];
List<Field> path = new ArrayList<Field>();
private Field start = null;
private Field goal = null;
// i've omited initializing too
public boolean findPath(Field start, Field goal) {
this.start = start;
this.goal = goal;
// Instead of having one recursive method, I divided it in two, one public and another private
for(int i = 0; i < 10; i++) {
for(int j = 0; j < 10; j++) {
if(gitter[i][j].equals(start)) {
return findPath(i,j);
}
}
} return false;
}
/*
* Check if available path exist from Field [i][j]
*/
private boolean findPath(int i, int j){
boolean solved = false;
// This check makes if-else chain cleaner
if(i < 0 || i >= 10 || j < 0 || j >= 10){
return false;
}
// Don't check already visited cells
if(!gitter[i][j].visited){
gitter[i][j].visited=true;
// If its possible to have a trap in goal Field, this check must be first
if(gitter[i][j].equals(goal)){
path.add(gitter[i][j]);
return true;
}
// If start Field mustn't be a trap, first condition should be removed
if(!gitter[i][j].equals(start) && gitter[i][j].isTrap()){
return false;
}
// Down
if(findPath(i+1,j)){
solved = true;
}
// Up
else if(findPath(i-1,j)){
solved = true;
}
// Right
else if(findPath(i,j+1)){
solved = true;
}
// Left
else if(findPath(i,j-1)){
solved = true;
}
// If any direction check found a path, this cell is part of that path
if(solved){
path.add(gitter[i][j]);
}
return solved;
}
return false;
}
}
I've tried with small numbers (from 1 to 9) and it should work
This one can not be a perfect nor efficient solution, but I've tried to make code easier to read and understand. I think with this kind of recursive problems that's a point to be highly considered.

Related

Shortest path maze solving algorithm

I have this method that solves the first possible path in a 2D maze matrix (works fine)
public boolean findFirstPath() {
boolean found = false;
path = new ArrayList<Coordinate>(); // restarts the path in case the algorithm has been solved previously
Coordinate coor = new Coordinate();
coor.i = startI; coor.j = startJ; coor.direction = 0; // instance of the first Coordinate object
path.add(coor); // the first coordinate is added to the path
while (!found && path.size() > 0) {
path.get(path.size()-1).direction++; // accesses the last element and increments the direction by 1
if (path.get(path.size()-1).direction <= 4) {
Coordinate coor_next = setNextCell(path.get(path.size()-1)); // we assign the last element of the path
if (isValidSpot(coor_next)) {
path.add(coor_next); // add to the path the valid cell
if (coor_next.i == endI && coor_next.j == endJ) { // if it reaches the end
found = true;
}
}
} else { // if it has checked all 4 directions go back
path.remove(path.size()-1); // deletes the last position in backtracking
}
}
return found;
}
I need to make modifications to find the shortest possible path and I can't find an optimal solution, I've tried something like this:
public boolean findShortestPath() {
boolean found = false;
int steps = 0;
path = new ArrayList<Coordinate>();
ArrayList<Coordinate> shortest = new ArrayList<Coordinate>(); // arrayList where the shortest path will be stored
Coordinate coor = new Coordinate();
coor.i = startI; coor.j = startJ; coor.direction = 0;
path.add(coor);
while (!found && path.size() > 0) {
path.get(path.size()-1).direction++;
if (path.get(path.size()-1).direction <= 4) {
Coordinate coor_next = setNextCell(path.get(path.size()-1));
if (isValidSpot(coor_next)) {
steps++;
if (steps < path.size()) {
shortest.add(path.get(path.size()-1));
} else {
path.add(coor_next);
}
if (coor_next.i == endI && coor_next.j == endJ) {
found = true;
}
}
} else {
path.remove(path.size()-1);
}
}
return found;
}
but it does not even enter the if (steps < path.size()) comprobation, any suggestions? Thanks.

Unable to get random number to be generated every time object is created

I am creating an object with the same parameters multiple times over and over again. The object has a random method (using Math.random()) in it which I know should return a different number every time, but each time within the program I create a new object of that class and call the method on it, it returns the same value. How should I fix this?
place where I call the method contract:
for (int i = 0; i < 212000; i++){
Contractions c = new Contractions(a, b);
temp = c.contract();
if (temp < min){
min = temp;
}
if (i%1000 == 0){
System.out.println(min);
}
}
method:
while (vertices.size() > 2){
Edge randEdge = edges.get((int) (Math.random()*edges.size()));
vertices.remove(randEdge.getTwo());
for (int i = edges.size() - 1; i >= 0; i--){
if (edges.get(i).getOne() == randEdge.getTwo()){
edges.get(i).setOne(randEdge.getOne());
}
if (edges.get(i).getTwo() == randEdge.getTwo()){
edges.get(i).setTwo(randEdge.getOne());
}
}
edges.remove(randEdge);
removeSelfLoops();
return edges.size();
edge class:
package Contractions;
public class Edge {
Vertex one;
Vertex two;
public Edge(Vertex one, Vertex two){
this.one = one;
this.two = two;
}
public boolean isEqual(Edge other){
if (other.one == this.one && other.two == this.two){
return true;
}
if (other.two == this.one && other.one == this.two){
return true;
}
return false;
}
public Vertex getOne(){
return one;
}
public Vertex getTwo(){
return two;
}
public void setOne (Vertex v){
one = v;
}
public void setTwo (Vertex v){
two = v;
}
public String toString(){
return one + "; " + two;
}
}
Try using Java Random with its nextInt(int bound) method. Let the bound be the length of the list that is holding the edges. This will return a random integer between 0 inclusive and bound exclusive.
The method you are using now returns a number between 0 inclusive and 1 exclusive. Then you are casting the result to an int. It seems likely that you are not getting the kind of distribution that you expect because of the generator that you are using.
as far as I can tell, you are returning edges.size() not randEdge. assuming you're not changing the edges array, you will always return the same thing.

This method that determines the winner of a Mancala does not work

Mancala is a fascinating game that I programmed in Java. On the image below we see a Mancala gameboard. For my problem you need to know that a A1-A6,B1-B6 are called pits and the big pits are called kalahs.
(source: pressmantoy.com)
The pits A1-A6 and right kalah belongs to player A and pits B1-B6 and left kalah to player B.
The game ends when all six pits on one side of the gameboard are empty or when there are more than 24 pits in one player's kalah. This is what I tried to program in a boolean method (which returns true if there is a winner so I can use other method to tell who it is):
public boolean isThereAWinner() {
ArrayList <SuperPit> pitsOfOwner = owner.getmyPits();
ArrayList <SuperPit> pitsOfOpponent = owner.getopponent().getmyPits();
for (int i = 0; i < pitsOfOwner.size(); i++) {
if (pitsOfOwner.get(i).isValidPlayOption() == true)
return false;
}
for (int i = 0; i < pitsOfOpponent.size(); i++) {
if (pitsOfOpponent.get(i).isValidPlayOption() == true)
return false;
}
if (owner.getKalah().getSeed() > 24) return true;
return false;
}
Where:
protected int seed;
public int getSeed() {
return seed;
}
public boolean isValidPlayOption() {
if (getSeed() > 0) return true;
else return false;
}
The oppositepit() and nextPit() methods work. The myPits ArrayLists contain the pits that belong to the two respective players.
I thought that this method should work since if one player no longer has seeds in his pit the game should stop. The method isThereAWinner() is run every time a player makes a move.
Unfortunately, the method always returns false. I have no idea why and hope someone here can provide me with some insight.
It's always returning false because of :
for (int i = 0; i < pitsOfOwner.size(); i++) {
if (pitsOfOwner.get(i).isValidPlayOption() == true)
return false;
}
The moment any pit has seeds, it returns false, even if the other board is completely empty.
How about:
int sum1 = 0;
for (int i = 0; i < pitsOfOwner.size(); i++) {
sum1 += pitsOfOwner.get(i).getSeed();
}
if (sum1 == 0) return true; // all pits are empty
If one player has one valid play option, you already return a value. You need to continue checking.
You don't return true if a player doesn't have a move.
What about checking the opponent's kalah?
== true is redundant.
Code:
public boolean isThereAWinner() {
ArrayList <SuperPit> pitsOfOwner = owner.getmyPits();
ArrayList <SuperPit> pitsOfOpponent = owner.getopponent().getmyPits();
boolean hasLost = true;
for (int i = 0; i < pitsOfOwner.size() && hasLost; i++) {
if (pitsOfOwner.get(i).isValidPlayOption())
hasLost = false;
}
if (hasLost) return true;
hasLost = true;
for (int i = 0; i < pitsOfOpponent.size() && hasLost; i++) {
if (pitsOfOpponent.get(i).isValidPlayOption())
hasLost = false;
}
if (hasLost) return true;
if (owner.getKalah().getSeed() > 24) return true;
if (owner.getopponent().getKalah().getSeed() > 24) return true;
return false;
}
The && hasLost is just an optimization to stop the loop once you find a move.
With less redundancy:
private boolean hasLost(Player player)
{
boolean hasLost = true;
for (int i = 0; i < player.getmyPits().size() && hasLost; i++) {
if (player.getmyPits().get(i).isValidPlayOption())
hasLost = false;
}
return (hasLost || player.getopponent().getKalah().getSeed() > 24);
}
public boolean isThereAWinner() {
if (hasLost(owner)) return true;
if (hasLost(owner.getopponent())) return true;
return false;
}

Finding all hamiltonian cycles

I'm trying to implement a method for adding all possible Hamiltonian cycles to a list using recursion. So far my stopping condition isn't sufficient and I get "OutOfMemoryError: Java heap space" in the line that adds a vertex to a list:
private boolean getHamiltonianCycles(int first, int v, int[] parent,
boolean[] isVisited, List<List<Integer>> cycles) {
isVisited[v] = true;
if (allVisited(isVisited) && neighbors.get(v).contains(new Integer(first))) {
ArrayList<Integer> cycle = new ArrayList<>();
int vertex = v;
while (vertex != -1) {
cycle.add(vertex);
vertex = parent[vertex];
}
cycles.add(cycle);
return true;
} else if (allVisited(isVisited)) {
isVisited[v] = false;
return false;
}
boolean cycleExists = false;
for (int i = 0; i < neighbors.get(v).size(); i++) {
int u = neighbors.get(v).get(i);
parent[u] = v;
if (!isVisited[u]
&& getHamiltonianCycles(first, u, parent, isVisited, cycles)) {
cycleExists = true;
}
}
//if (!cycleExists) {
isVisited[v] = false; // Backtrack
//}
return cycleExists;
}
Can someone please suggest me what I'm doing wrong or is my approach completely incorrect?
EDIT:
As suggested in comments, the culprit was the parent array, causing an infinite loop. I wasn't able to correct it and I changed the array to store the child element. Now everything seems to work:
private boolean getHamiltonianCycles(int first, int v, int[] next,
boolean[] isVisited, List<List<Integer>> cycles) {
isVisited[v] = true;
if (allVisited(isVisited) && neighbors.get(v).contains(first)) {
ArrayList<Integer> cycle = new ArrayList<>();
int vertex = first;
while (vertex != -1) {
cycle.add(vertex);
vertex = next[vertex];
}
cycles.add(cycle);
isVisited[v] = false;
return true;
}
boolean cycleExists = false;
for (int u : neighbors.get(v)) {
next[v] = u;
if (!isVisited[u]
&& getHamiltonianCycles(first, u, next, isVisited, cycles)) {
cycleExists = true;
}
}
next[v] = -1;
isVisited[v] = false; // Backtrack
return cycleExists;
}
If you is looking for Disjoint Hamiltonian Cycles here have an implementation using Backtracking.

Checking to see if a string is a palindrome or not

I am very close to finishing my one practice problem that deals with a palindrome and a string parameter and I am stuck with the main method to call the method. Every time I compile my code it compiles, but then when I go to input data, it keeps on running and does not give me a result. Can anyone aid in me in what I need to do to get it to return the result? The problem just asks to create a method that checks if it is a palindrome, my main method to test it is what is giving me trouble.
This is my code:
import java.util.*;
public class TestisPalindrome
{
public static boolean isPalindrome(String str) {
int left = 0;
int right = str.length() -1;
while(left < right) {
if(str.charAt(left) != str.charAt(right)) {
return false;
}
}
left ++;
right --;
return true;
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("Enter a string to see if it is a palindrome or not: ");
String st1 = scan.nextLine();
System.out.println(isPalindrome(st1));
}
}
right & left increment should be in while loop
while(left < right)
{
if(str.charAt(left) != str.charAt(right))
{
return false;
}
left ++;
right --;
}
You've created an infinite loop. You have a while loop, but never change the conditions.
while(left < right)
{
if(str.charAt(left) != str.charAt(right))
{
return false;
}
}
Assuming left < right when you start, this will never change.
You have lines to increment left and right, but your code will never reach those lines, since it never gets out of the while loop.
You are overthinking this. Look at StringBuffer:
StringBuffer input = new StringBuffer(str);
return str.equals(input.reverse()).toString);
Please note that there is a performance impact of your implementation:
while(left < right) { //multiply inner operations by n/2
if(str.charAt(left) != str.charAt(right)) { //three operations
return false;
}
//This need to be inside your while loop
left ++; //one operation
right --; //one operation
}
This leads to an O(n) = (n * 5) / 2. On the other hand, if you simply reverse a string, it's only O(n) = n in the worst case. This is not a significant impact, but can add up depending on how you're accessing this.
You can also solve it like this:
public static boolean isPalindrome (String str){
String convertedStr = "";
for (int i = 0; i <str.length(); i++){
if (Character.isLetterOrDigit(str.charAt(i)))
convertedStr += Character.toLowerCase(str.charAt(i));
}
if (convertedStr.equals(reverseString(convertedStr)))
return true;
else
return false;
} //End of isPalindrome
Here is the code I used to determine whether a string is Palindrome String or not:
private static boolean isPalindromeString(String str){
if (str == null)
return false;
int len = str.length();
for (int i=0; i<len/2 ; i++){
if (str.charAt(i) != str.charAt(len - i - 1)){
return false;
}
}
return true;
}
I hope this can help you.

Categories