I am trying to learn DFS by creating a program that navigates my ogre through a maze (2d array).This is similar to a dailyprogramming challenge, but I am doing it with just a 1x1 ogre.
My maze:
static int[][] maze = {
{2,1,0,0,0,0,0,0,0,0},
{0,0,1,0,0,0,0,0,0,0},
{1,0,0,0,0,1,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,1,1,0,0,0,0,0,0},
{0,0,1,0,0,0,0,1,0,1},
{1,1,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,1,1,0,0,0},
{0,0,0,0,0,1,0,0,0,3}};
Where 2 is my hero (0,0), 3 is my goal (9,9), 1s are obstacles, and 0s are traverseable space.
Since I am new to this, I doubt it will be needed, but ill include the whole program for easy duplication and troubleshooting.
import java.awt.Point;
import java.util.ArrayList;
public class OgrePath {
static int[][] maze = {
{2,1,0,0,0,0,0,0,0,0},
{0,0,1,0,0,0,0,0,0,0},
{1,0,0,0,0,1,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,1,1,0,0,0,0,0,0},
{0,0,1,0,0,0,0,1,0,1},
{1,1,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,1,1,0,0,0},
{0,0,0,0,0,1,0,0,0,3}};
public static boolean[][] visited = new boolean[maze.length][maze[0].length];
static ArrayList<Point> neighbors = new ArrayList<Point>();
public static void main(String[] args) {
OgrePath OP = new OgrePath();
for (int i=0;i<maze.length;i++){
for (int j=0;j<maze[i].length;j++){
visited[j][i] = false;
}
}
visited[getOgre(maze).x][getOgre(maze).y] = true;
System.out.println("Ogre: " + getOgre(maze));
dfs(maze, getOgre(maze));
}
public static boolean dfs(int[][] maze, Point p){
neighbors = getNeighbors(maze,p);
if (maze[p.x][p.y] == 3){
System.out.println("FOUND IT");
return true;
}
if (neighbors.isEmpty()){
return false;
}
for (int i=0;i<neighbors.size();i++){
System.out.println("Nieghbors: " + neighbors);
System.out.println(i + "(" + p.x + "," + p.y + ")");
visited[neighbors.get(i).x][neighbors.get(i).y] = true;
dfs(maze, neighbors.get(i));
}
return false;
}
public static ArrayList<Point> getNeighbors(int[][] maze, Point p){
ArrayList<Point> neighbors = new ArrayList<Point>();
Point left = new Point();
Point right = new Point();
Point down = new Point();
Point up = new Point();
down.x = p.x - 1;
down.y = p.y;
if (valid(maze,down)) neighbors.add(down);
up.x = p.x + 1;
up.y = p.y;
if (valid(maze,up)) neighbors.add(up);
left.x = p.x;
left.y = p.y - 1;
if (valid(maze,left)) neighbors.add(left);
right.x = p.x;
right.y = p.y + 1;
if (valid(maze,right)) neighbors.add(right);
return neighbors;
}
public static boolean valid(int[][] maze, Point p){
if (inMaze(maze,p) && canGo(maze,p) && visited[p.x][p.y] == false) return true;
else return false;
}
public static boolean inMaze(int[][] maze, Point p){
if (p.x < (maze[0].length - 1) && p.x > -1 && p.y < (maze.length - 1) && p.y > -1){
return true;
} else return false;
}
public static boolean canGo(int[][] maze, Point p){
if (maze[p.x][p.y] != 1 && maze[p.x][p.y] != 4) return true;
else return false;
}
public static Point getOgre(int[][] maze){
Point ogre = new Point();
for (int i=0;i<maze.length;i++){
for (int j=0;j<maze[i].length;j++){
if (maze[i][j] == 2){
ogre.x = j;
ogre.y = i;
}
}
}
return ogre;
}
}
I want to be able to recursively call DFS, but something about the way I wrote it makes the program stop after it has explored 1 possible line and failed.
Okay, so there a few issues I see that would prevent your code from working properly so lets look at them one at a time.
First, you dfs function will not iterate through the 'for' loop because it will immediately return. Try changing
dfs(maze, neighbors.get(i));
to
if(dfs(maze, neighbors.get(i))){
return true;
}
This fixes part of your issue with only searching a single path.
The second issue is with your neighbors. When your dfs does fully explore a path, it should go back a step and check all neighbors. You only have a single top-level neighbors variable, so when your branch terminates with zero neighbors, it thinks all earlier nodes have zero neighbors.
Remove your static neighbors variable
static ArrayList<Point> neighbors = new ArrayList<Point>();
And put a non-static version in getNeighbors
ArrayList<Point> neighbors = new ArrayList<Point>();
This almost completely fixes the search, but for your maze, you will still not find the end.
Your inMaze function is checking bounds incorrectly. You were checking for if x or y was less than length minus one. You only need to use 'less than' for checking the boundary.
if (p.x < maze[0].length && p.x > -1 && p.y < maze.length && p.y > -1)
Related
I am relatively new to java, and I have an idea for a mechanic I want to implement in to my game. However, I have no idea how to go about solving this problem. My snake game works on a basic coordinate system. I want it to be so that when the snake makes a closed loop (a rectangle or square) the game will detect it has made a loop. I have tried writing a method to locate the part of the snake's body that is the most upper-left, and then checking from there, but it seems to not work very well. Here is the method I attempted to write, if It helps at all. Thank you for any help!!
public boolean checkRing()
{
int topLeftX = 5000;
int topLeftY = 5000;
for(int i = bodyParts;i>0;i--)
{
// Finds coordinates of top left box
if(x[i] < topLeftX)
{
topLeftX = x[i];
}
if(y[i] < topLeftY)
{
topLeftY = y[i];
}
}
// Use isBody() method below (not bug tested) to check for rectangle
boolean lineFoundVert = false;
int checkingX = topLeftX;
int checkingY = topLeftY;
int vertCounter = 1;
while(!lineFoundVert)
{
if(isBody(checkingX, checkingY))
{
vertCounter++;
checkingX++;
}
else
lineFoundVert = true;
}
boolean lineFoundHori = false;
checkingX = topLeftX;
checkingY = topLeftY;
int horiCounter = 1;
while(!lineFoundHori)
{
if(isBody(checkingX, checkingY))
{
horiCounter++;
checkingY++;
}
else
lineFoundHori = true;
}
debug1X = topLeftX + 1;
debug1Y = topLeftY + vertCounter;
debug2X = topLeftX + horiCounter;
debug2Y = topLeftY + 1;
if(isBody(topLeftX + 1, topLeftY + vertCounter) && isBody(topLeftX + horiCounter, topLeftY + 1))
{
return true;
}
return false;
}```
Here is an approximate solution:
private boolean isEdgeCoordinate(Coordinate[] bodyparts, int index) {
// for every bodypart check that its neighbours (bodypart one before and
// bodypart one after) dont share X axis and dont share Y axis. As long
// as that is the case it is an edge.
//additionally for the last bodypart you needto check that it has first
// bodypart as a neighbour and check them as neighbours otherwise no
// rectangle to begin with
}
using this method check the amount of edges in your bodyparts array. If the total number of edges == 4 you have got a square/rectangle
I am using Breadth First Search Algorithm to solve a maze. My algorithm successfully finds the shortest path but it doesn't store the shortest path. It just tells me the number of steps used to complete this path but saves all the squares that have been checked no matter if they belong to the shortest path or not. I really tried quite a few ways to store the shortest path but I'm getting errors with the path where squares that are not in the shortest path are also included. If you could find me a way to store the shortest path in an ArrayList or ArrayQueue or array or anything. The correctPath() is what i did so i can decide which squares are in the shortest path but it's wrong. I dont think that is so complicated just i cannot figure out how to do it. Thanks for your time.
Squares have as attributes a x and y position and also the distance from the destination.
public class BreathAlgorithm {
// Java program to find the shortest path between a given source cell to a destination cell.
static int ROW;
static int COL;
// These arrays are used to get row and column numbers of 4 neighbours of a given cell
static int[] rowNum = {-1, 0, 0, 1};
static int[] colNum = {0, -1, 1, 0};
// check whether given cell (row, col) is a valid cell or not.
static boolean isValid(int row, int col)
{
// return true if row number and column number is in range
return (row > 0) && (row <= ROW) && (col > 0) && (col <= COL);
}
// Checks if a square is an adjacent to another square
static boolean isNearSquare(Square a,Square b){
int x = 1;
int y = 0;
if((Math.abs((a.getX()+x) - (b.getX()+x))) + (Math.abs((a.getY()+y) - (b.getY()+y))) != 1){
return false;
}
x = -1;
y = 0;
if((Math.abs((a.getX()+x) - (b.getX()+x))) + (Math.abs((a.getY()+y) - (b.getY()+y))) != 1){
return false;
}
x = 0;
y = 1;
if((Math.abs((a.getX()+x) - (b.getX()+x))) + (Math.abs((a.getY()+y) - (b.getY()+y))) != 1){
return false;
}
x = 0;
y = -1;
return (Math.abs((a.getX() + x) - (b.getX() + x))) + (Math.abs((a.getY() + y) - (b.getY() + y))) == 1;
}
// returns the Square of the ending position
public static Square findEnd(int[][] mat){
for (int i=0;i<mat.length;i++){
for(int j=0;j<mat[0].length;j++){
if(mat[i][j] == 9)
return new Square(i,j,0);
}
}
return new Square(1,1,0);
}
/*
In this method i tried to define which squares are to be deleted from the fullPath
and return a new path with only the squares who are actually used in the shortest path.
This method doesn't work for all examples it just works for some so i guess it is lacking.
*/
public static ArrayQueue<Square> correctPath(ArrayList<Square> path) throws QueueFullException {
int i=0;
while(i<path.size()-1){
if (path.get(i).getDistance() == path.get(i+1).getDistance()){
if (path.get(i+2)!=null && path.get(i-1)!=null && (!isNearSquare(path.get(i),path.get(i+2)) || !isNearSquare(path.get(i),path.get(i+2)))){
path.remove(i);
}
else if (path.get(i+2)!=null && path.get(i-1)!=null && (!isNearSquare(path.get(i+1),path.get(i-1)) || !isNearSquare(path.get(i+1),path.get(i+2)))){
path.remove(i+1);
}
else if (!isNearSquare(path.get(i),path.get(i+1))){
path.remove(i);
}
}
i++;
}
ArrayQueue<Square> correctPath = new ArrayQueue<>(path.size());
while(i>=0){
correctPath.enqueue(path.get(i));
i--;
}
return correctPath;
}
static void printCorrectPath(ArrayQueue<Square> correctPath) throws QueueEmptyException {
Square[] originalPath = new Square[correctPath.size()];
for(int i=originalPath.length-1;i>=0;i--){
originalPath[i] = correctPath.dequeue();
}
int i=0;
while(i<originalPath.length-1){
if(i == 0) System.out.println(originalPath[i]+" is the starting point.");
System.out.println("From "+originalPath[i]+"to "+originalPath[i+1]);
i++;
if(i == originalPath.length-1) System.out.println(originalPath[i]+" is the ending point.");
}
}
public static void searchPath(int[][] mat,Square start) throws QueueEmptyException, QueueFullException {
//mat is the maze where 1 represents a wall,0 represent a valid square and 9 is the destination
// When a square is visited from 0 it becomes a 2
ROW=mat.length;
COL=mat[0].length;
Square dest = findEnd(mat); // search for the number 9 and make a new Square and put it in dest
int dist = BFS(mat, start, dest); // find the least distance
if (dist != Integer.MAX_VALUE)
System.out.println("\nShortest Path is " + dist+" steps.");
else
System.out.println("Shortest Path doesn't exist");
}
// function to find the shortest path between a given source cell to a destination cell.
static int BFS(int[][] mat, Square src, Square dest) throws QueueFullException, QueueEmptyException {
ArrayList<Square> fullPath = new ArrayList<>(); // path of all the squares checked
boolean [][]visited = new boolean[ROW][COL]; // if a square is visited then visited[x][y] = true
ArrayQueue<Square> q = new ArrayQueue<>(mat.length*mat[0].length); // Create a queue for BFS
// check source and destination cell of the matrix have value 1
if (mat[src.getY()][src.getX()] != 0 || mat[dest.getX()][dest.getY()] != 9) {
return -1;
}
mat[src.getY()][src.getX()] = 2; // Mark the source cell as visited
visited[src.getX()][src.getY()] = true;
q.enqueue(src); // Enqueue source cell
fullPath.add(src); // Add source to the full path
while (!q.isEmpty()) // Do a BFS starting from source cell
{
Square curr = q.front();
if (curr.getX() == dest.getX() && curr.getY() == dest.getY()) { // If we have reached the destination cell we are done
printCorrectPath(correctPath(fullPath));
return curr.getDistance();
}
q.dequeue(); // Otherwise dequeue the front cell in the queue and enqueue its adjacent cells
for (int i = 0; i < 4; i++){
int row = curr.getX() + rowNum[i];
int col = curr.getY() + colNum[i];
// if adjacent cell is valid, has path and not visited yet, enqueue it.
if (isValid(row, col) && mat[row][col] == 0 || mat[row][col] == 9 && !visited[row][col]){
mat[row][col] = 2;
visited[row][col] = true; // mark cell as visited and enqueue it
Square Adjcell = new Square(row,col, curr.getDistance() + 1 );
q.enqueue(Adjcell);
fullPath.add(Adjcell);
}
}
}
return -1; // Return -1 if destination cannot be reached
}
}
Here is the class where i do the testings.
public class MazeRunner {
// Maze is a 2d array and it has to be filled with walls peripherally
// with walls so this algorithm can work. Our starting position in this
// will be (1,1) and our destination will be flagged with a 9 which in
// this occasion is (11,8).
private int[][] maze ;
private final List<Integer> path = new ArrayList<>();
public long startTime,stopTime;
public MazeRunner(int [][] maze){
this.maze = maze;
}
public void runBFSAlgorithm(int startingX,int startingY) throws QueueEmptyException, QueueFullException {
startTime = System.nanoTime();
BreathAlgorithm.searchPath(maze,new Square(startingX,startingY,0));
stopTime = System.nanoTime();
System.out.printf("Time for Breath First Algorithm: %.5f milliseconds.\n",(stopTime-startTime)*10e-6);
}
public void printMaze(){
for (int[] ints : maze) {
for (int anInt : ints) {
System.out.print(anInt + " ");
}
System.out.println();
}
}
public static void main(String[] args) throws FileNotFoundException, QueueEmptyException, QueueFullException {
int [][] maze = {{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,0,1,0,1,0,1,0,0,0,0,0,1},
{1,0,1,0,0,0,1,0,1,1,1,0,1},
{1,0,0,0,1,1,1,0,0,0,0,0,1},
{1,0,1,0,0,0,0,0,1,1,1,0,1},
{1,0,1,0,1,1,1,0,1,0,0,0,1},
{1,0,1,0,1,0,0,0,1,1,1,0,1},
{1,0,1,0,1,1,1,0,1,0,1,0,1},
{1,0,0,0,0,0,0,0,0,0,1,9,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1}};
int[] startingPoint = {1,1};
MazeRunner p = new MazeRunner(maze);
p.printMaze();
p.runBFSAlgorithm(startingPoint[0],startingPoint[1]);
}
}
My execution would look like this:
The execution output
Give an instance of Square an extra property: Square cameFrom;.
Then in your BFS change:
q.enqueue(Adjcell);
to:
Adjcell.cameFrom = curr;
q.enqueue(Adjcell);
Then, change correctPath so it takes dest as argument and builds the path as an ArrayList<Square> from following the linked list formed by the cameFrom property.
This list will then just need to be reversed to get it in the right order.
I'm trying to write a program that outputs (in the console) all possible paths (in (x1,x1) --> (x2,x2) --> etc.. format) for navigating a grid of size NxN, from top-left to bottom-right; i.e. from (0,0) to (N-1,N-1). You can only move down or right; i.e. from (0,0) to (0,1) or to (1,0). I want the program to output each time a full path set is found (i.e. all moves from top-left to bottom-right), and what that path set is.
It seems as though the best way to write this is with a recursive method inputting each move into an arrayList (see the buildPath method - the last method in the program), which is where I'm having trouble.
To make it slightly more complicated, I'm also generating random grid positions that are "off-limits" and as such can't be passed through.
That said, I can probably work that part out for myself once we/I figure out how to actually get the thing working with any paths at all.
How would I implement a recursive method to determine which paths are possible? Any help is appreciated (even pseudo-code would be better than nothing)!
Here is my code so far (the simple bits are in pseudo-code to make it easier to work through, but let me know if I should put the full code in):
import java.util.*;
public class RecursiveAlgorithm {
public static ArrayList<Integer> allPaths = new ArrayList<Integer>();
public static ArrayList<String> pathSet = new ArrayList<String>();
public static int path;
public static int N, M, x = 0, y = 0;
public static String nString, mString;
public static boolean grid[][];
public static int right, down;
#SuppressWarnings("resource")
public static void main(String[] args) {
//sets the current position to (0,0)
right = 0;
down = 0;
Input value of N (size of grid)
Input value of M (number of off-limits locations)
offLimits(N, M); //calls offLimits method to randomly generate off-limits locations
buildPath(right, down, allPaths, N); //calls buildPath method
}
public static void offLimits (int N, int M) {
int mCount = 0;
if (M == 0){
} else {
while (mCount < (M + 1)) {
//int range1 = (max - min) + 1;
int range1 = ((N-1) - 1) + 1;
int range2 = ((N-1) - 0) + 1;
int random1 = (int)((Math.random() * range1) + 1);
int random2 = (int)(Math.random() * range2);
//if an 'off-limits' point is generated at the finish point, move it to either 1 place to the left or 1 place above
if ((random1 == N-1) && (random2 == N-1)) {
int switchSelect = (int)(Math.random() * 2);
while (switchSelect > 0) {
switch (switchSelect){
case 1: random1--;
break;
case 2: random2--;
break;
}
}
}
//sets a grid position to 'off-limits' (i.e. random1 = 1, random2 = 2 --> (1, 2) is 'off-limits')
grid[random1][random2] = true;
//counts the amount of off-limits grid locations generated
mCount++;
}
}
}
public static ArrayList<String> buildPath (int right, int down, ArrayList<Integer> allPaths, int N) {
//Updates path with current location (right, down)
/***** FROM HERE ON IS WHERE I AM HAVING TROUBLE *****/
//Stopping Condition
if ((right == N-1) && (down == N-1)) { //robot cannot go right
allPaths.add(path);
return pathSet;
}
//Recursive Steps
if (right == N-1) { //robot cannot go right
buildPath (right, down + 1, allPaths, N);
} else if (down == N-1) { //robot cannot go down
buildPath (right + 1, down, allPaths, N);
} else { //robot CAN go right or go down
buildPath (right + 1, down, allPaths, N);
//pathSet.add(Integer.toString(right));
//pathSet.add(Integer.toString(down));
buildPath (right, down + 1, allPaths, N);
if (grid[x][y] == false) {
//valid new position (substitute x and y for proposed new path step)
} else if (grid[x][y] == true) {
//off-limits position (substitute x and y for proposed new path step)
}
}
return pathSet;
}
}
You're on the right track, but headed toward a solution more complex than needed. Here's one approach that finds them allowing all 4 compass directions (not just right and down). See how simple you can make it by removing code.
import java.util.LinkedHashSet;
class Experimental {
static class PathFinder {
final int gridSize;
final boolean [] [] isBlocked;
final Coord goal;
final LinkedHashSet<Coord> path = new LinkedHashSet<>();
final Random gen = new Random();
PathFinder(int gridSize, int nBlocked) {
this.gridSize = gridSize;
this.isBlocked = new boolean[gridSize][gridSize];
this.goal = new Coord(gridSize - 1, gridSize - 1);
// This gets really inefficient if nBlocked is too big.
for (int n = 0; n < nBlocked; ++n) {
int x, y;
do {
x = gen.nextInt(gridSize);
y = gen.nextInt(gridSize);
} while (isBlocked[x][y] || (x == gridSize - 1 && y == gridSize - 1));
isBlocked[x][y] = true;
}
}
void searchFrom(Coord coord) {
if (path.contains(coord)) return;
path.add(coord);
if (coord.equals(goal)) System.out.println(path);
if (coord.x > 0 && !isBlocked[coord.x - 1][coord.y])
searchFrom(new Coord(coord.x - 1, coord.y));
if (coord.y > 0 && !isBlocked[coord.x][coord.y - 1])
searchFrom(new Coord(coord.x, coord.y - 1));
if (coord.x < gridSize - 1 && !isBlocked[coord.x + 1][coord.y])
searchFrom(new Coord(coord.x + 1, coord.y));
if (coord.y < gridSize - 1 && !isBlocked[coord.x][coord.y + 1])
searchFrom(new Coord(coord.x, coord.y + 1));
path.remove(coord);
}
void printAllPaths() {
searchFrom(new Coord(0, 0));
}
static class Coord {
final int x, y;
public Coord(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public boolean equals(Object obj) {
if (obj instanceof Coord) {
Coord other = (Coord) obj;
return x == other.x && y == other.y;
}
return false;
}
#Override
public int hashCode() {
return Integer.hashCode(x) ^ Integer.hashCode(-y);
}
#Override
public String toString() {
return '(' + Integer.toString(x) + ',' + Integer.toString(y) + ')';
}
}
}
public static void main(String[] args) {
new PathFinder(4, new boolean [] [] {
{ false, false, false, false },
{ false, false, true, false },
{ true, false, false, false },
{ false, false, false, false },
}).printAllPaths();
}
}
One hint: The linked hash set is a reasonable choice for the path here because we need to look "backward" at each step to make sure we're not about to visit a location already visited. The set makes the lookups O(1), while the linking ensures order is maintained, which normal hash sets don't. Your problem is different.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I was given this question in a programming test for an IT company.
I will try my best to explain it.
The problem is as follows:
Given an Ant at origin (0,0) which moves only in clockwise direction( takes only right turns) on a given path array. so for example if the path array is {2,3,4,5,7} the ant moves 2 units left, then moves 3 units down , then moves 4 units right, then moves 5 units up and then 7 units left so on and so forth.
So write a code which displays the ant's final position(coordinates) and state if the ant intersects it's path in the format:
Ant: (x1,y1) :(yes / no)
for example:
(1) array={1,6,3,5,4}
output: Ant: (2,-1) :yes
showing it graphically
(0, 0)__(1,0)
|
(-2,-1) __ __ __ __(2,-1)
| |
| |
| |
| |
| |
(-2,-6) __ __ __ (1,-6)
here the ant is intersecting its path at (1,-1)
(2) array={2,2,2,1}
output: Ant: (0,-1) :no
showing it graphically
(0, 0)__ __(2,0)
.(0,-1) |
| |
(0,-2)__ __(2,-2)
here the ant doesn't intersect its path.
I wrote a code to find the final position:
public class Ant {
static void findAnt(int arr[])
{
int count = 0;
int x=0,y=0;
for(int element: arr){
if(count>3)
count = 0;
switch(count++){
case 0: x=x+element;
break;
case 1: y=y-element;
break;
case 2: x=x-element;
break;
case 3: y=y+element;
break;
}
}
System.out.println("Ant: "+x+" "+y);
}
public static void main(String[] args)
{
int arr[] = new int[]{2,2,2,1};
findAnt(arr);
}
}
However I cannot devise an algorithm that shows if the ant intersects or not.
Please advise.
It will horizontally intersect if arr[1] <= arr[3] and vertically if arr[0] <= arr[2] you just need to check these positions.
for (int i = 0; i < arr.length; i++){
if (i == arr.length-2)
return false;//prevents indexoutofbounds
if (arr[i] <= arr[i+2])
return true;//intersects
}
this should check to see if p0 is less than p2, p1, is less than p3, and p2 is less than p4, and so on.
boolean intersect = false;
for (int i = 0; i < arr.length; i++){
if (arr[i] == arr[arr.length-2]){//i changed this
intersect = false;//prevents indexoutofbounds
break;
}
if (arr[i] <= arr[i+2])
intersect = true;//intersects
break;
}
and then print out intersect
One solution that doesn't keep a grid in memory, is to keep a set of visited locations in memory. This has the advantage that you don't need to know the boundary of the ant's potential path in advance. Whether it takes more or less memory than a grid, depends on the size of the grid, and the length of the ant's journey.
public class VisitedTileLog {
Set visitedTiles = new HashSet<Coordinates>();
boolean hasIntersected = false;
public void logVisit(Coordinates c) {
if(! visitedTiles.add(c)) {
hasIntersected = true;
}
}
public boolean hasIntersected() {
return hasIntersected;
}
}
Of course you need a Coordinates class with equals() and hashCode():
public class Coordinates {
private int x,y;
public Coordinates(int x, int y) {
this.x = x;
this.y = y;
}
public boolean equals(Object o) {
// Let your IDE write this, or read up on best practice.
}
public int hashCode() {
// Let your IDE write this, or read up on best practice.
}
// Examples of other methods this might have...
public int getX() { ... }
public int getY() { ... }
public Coordinates move(int distance, Direction direction);
}
Now you can take your ant for a walk, and each time it moves, update hasIntersected:
VisitedTileLog log = new VisitedTileLog();
for(int distance : distances) {
...
log.logVisit(...);
...
}
This class could be enhanced with convenience methods that log a whole step's line -- logVisit(Coordinates from, Coordinates to) or logVisit(Coordinates start, int distance, CompassPoint direction).
Depending on the interviewer, a solution like this might get you extra credit for being object-oriented. Indeed, this class could be enhanced to solve the whole of the problem, if it also maintained a currentPosition field.
One way to achieve this is to draw the line during each move for reference. And check before every move that if it is encountering the same coordinate that is already drawn. Below is the code for this approach. You can definitely fine tune it , but here is one way to tackle it.
Steps :
Create Coordinate type to store coordinates.
Create Ant that can hold :
current coordinate: this will hold the Ant Current Coordinate at any time
Direction to Move next : right , left , up or down
data set to keep track of traversed coordinate
data structure to hold all coordinates that are revisited
Now on every move of ant, it knows what direction to move next. And in each move , we draw all coordinates in between the current coordinate and the end point , and store them in traversed coordinate set. If there is hit, we store it in intersected coordinate set instead.
At the end, current coordinate of ant gives us the final coordinate and the line crosses over if the intersected set is not empty.
Here is the long code , that I assume is working fine.
public class PathCross {
public static void main(String[] args) {
int[] movementArray = { 2, 2, 2, 1 };// {1,6,3,5,4};
PathCross driver = new PathCross();
Ant ant = driver.new Ant();
for (int i : movementArray) {
ant.move(i);
}
System.out.println("Ant: (" + ant.currentCoordinate.getX() + "," + ant.getCurrentCoordinate().getY() + ") :"
+ !ant.getIntersectingCoordinates().isEmpty());
}
class Ant {
Coordinate currentCoordinate = new Coordinate(0, 0);
Direction nextDirection = Direction.RIGHT;
Set<Coordinate> intersectingCoordinates = new HashSet<>();
Set<Coordinate> traversedCoordinateSet = new HashSet<>();
public Ant() {
traversedCoordinateSet.add(new Coordinate(0, 0));
}
public Coordinate getCurrentCoordinate() {
return currentCoordinate;
}
public void setCurrentCoordinate(Coordinate currentCoordinate) {
this.currentCoordinate = currentCoordinate;
}
public Direction getNextDirection() {
return nextDirection;
}
public void setNextDirection(Direction nextDirection) {
this.nextDirection = nextDirection;
}
public Set<Coordinate> getIntersectingCoordinates() {
return intersectingCoordinates;
}
public void setIntersectingCoordinates(Set<Coordinate> intersectingCoordinates) {
this.intersectingCoordinates = intersectingCoordinates;
}
public Set<Coordinate> getTraversedCoordinateSet() {
return traversedCoordinateSet;
}
public void setTraversedCoordinateSet(Set<Coordinate> traversedCoordinateSet) {
this.traversedCoordinateSet = traversedCoordinateSet;
}
public void move(int distance) {
Coordinate newCoordinate = null;
switch (nextDirection) {
case RIGHT:
newCoordinate = new Coordinate(currentCoordinate.getX() + distance, currentCoordinate.getY());
for (int i = currentCoordinate.getX() + 1; i <= (currentCoordinate.getX() + distance); i++) {
if (!traversedCoordinateSet.add(new Coordinate(i, currentCoordinate.getY()))) {
intersectingCoordinates.add(new Coordinate(i, currentCoordinate.getY()));
}
}
nextDirection = Direction.DOWN;
break;
case DOWN:
newCoordinate = new Coordinate(currentCoordinate.getX(), currentCoordinate.getY() - distance);
for (int i = currentCoordinate.getY() - 1; i >= (currentCoordinate.getY() - distance); i--) {
if (!traversedCoordinateSet.add(new Coordinate(currentCoordinate.getX(), i))) {
intersectingCoordinates.add(new Coordinate(currentCoordinate.getX(), i));
}
}
nextDirection = Direction.LEFT;
break;
case LEFT:
newCoordinate = new Coordinate(currentCoordinate.getX() - distance, currentCoordinate.getY());
for (int i = currentCoordinate.getX() - 1; i >= (currentCoordinate.getX() - distance); i--) {
if (!traversedCoordinateSet.add(new Coordinate(i, currentCoordinate.getY()))) {
intersectingCoordinates.add(new Coordinate(i, currentCoordinate.getY()));
}
}
nextDirection = Direction.UP;
break;
case UP:
newCoordinate = new Coordinate(currentCoordinate.getX(), currentCoordinate.getY() + distance);
for (int i = currentCoordinate.getY() + 1; i <= (currentCoordinate.getY() + distance); i++) {
if (!traversedCoordinateSet.add(new Coordinate(currentCoordinate.getX(), i))) {
intersectingCoordinates.add(new Coordinate(i, currentCoordinate.getY()));
}
}
nextDirection = Direction.RIGHT;
break;
default:
System.err.println("ERRor");
}
this.currentCoordinate = newCoordinate;
}
}
enum Direction {
LEFT, DOWN, RIGHT, UP;
}
class Coordinate {
int x;
int y;
public Coordinate() {
}
public Coordinate(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getOuterType().hashCode();
result = prime * result + x;
result = prime * result + y;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Coordinate other = (Coordinate) obj;
if (!getOuterType().equals(other.getOuterType()))
return false;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
private PathCross getOuterType() {
return PathCross.this;
}
#Override
public String toString() {
return "x=" + x + ", y=" + y;
}
}
}
The problem is hard to find out whether it intersect the previous paths. I create a boolean to record whether it is increase the circle or not. if it is always increasing, it will not intersect the previous paths. If it change to the decreasing, once it began to increasing again, it will intersects the paths. Otherwise, it will not intersects the path
def ant(arr):
length = len(arr)
x = sum(arr[::4]) - sum(arr[2:][::4])
y = sum(arr[3:][::4]) - sum(arr[1:][::4])
if length < 4:
return x, y, False
t1, (t2, t3, t4) = 0, arr[:3]
increase = (t2 < t4)
for i in xrange(3, length):
t5 = arr[i]
if increase and t3 >= t5:
if t1 + t5 - t3 < 0 or i+1 < length and arr[i+1] + t2 - t4 < 0:
increase = False
elif i + 1 < length:
return x, y, True
elif not increase and t3 <= t5:
return x, y, True
t1, t2, t3, t4 = t2, t3, t4, t5
return x, y, False
Having some trouble building an equals method that compares two dimensional coordinate points in a list based on distance from point zero (0,0) -equation included.
public double distanceToOrigin() {
return distance(zero);
}
public double distance(Point that) {
return Math.sqrt(Math.pow((x - that.getX()), 2) + Math.pow((y - that.getY()), 2));
}
boolean equals(List<Point> lst){
boolean eq = true;
for (int i=0; i<lst.size(); i++)//accounts for first element-to-compare.
{
for (int q = 1; q < lst.size(); q++)//accounts for second element-to-compare.
{
if(lst.distanceToOrigin(i) == (lst).distanceToOrigin(q)))
{
eq = false;
}
}
}
return eq;
}
I may be over-interpreting the if statement: is there a more efficient way to compare both elements (in a single line of code)?
For reference:
static Point zero = new Point(0, 0);
public int getX(){
return x;
}
public int getY(){
return y;
}
Assistance heartily appreciated.
Examples of lists:
List<Point> lst = new ArrayList<Point>();
The corrected equals method would appear similar to the following (somewhat clumsy implementation currently):
boolean equals(List<Point> lst){
boolean eq = true;
for (int i=0; i<lst.size(); i++)//accounts for first element-to-compare.
{
for (int q = 1; q < lst.size(); q++)//accounts for second element-to-compare.
{
if(lst.get(i).distanceToOrigin() == lst.get(q).distanceToOrigin()){
eq = false;
}
}
}
return eq;
}
The equals method should return boolean true or false based on whether or not element-to-compare(1) is identical to element-to-compare(2).
If you are looking for equal distances of two points you are likely better off just comparing the sum of the squares of the coordinates. That avoids comparing floats and is more efficient:
class Point {
public boolean isSameDistanceFromOrigin(Point other) {
return x * x + y * y == other.x * other.x + other.y * other.y;
}
}
If I'm interpreting your loop correctly you want to return false if any two points in a list are the same distance from the origin. Here's an algorithm for doing that in one line (sort of) using Java 8:
public boolean areAllDifferentDistancesFromOrigin(List<Point> points) {
return points.stream().noneMatch(point ->
points.stream().filter(p -> p != point)
.anyMatch(p-> point.isSameDistanceFromOrigin(p)));
}