5

I'm trying to generate a random path from a point to another one in a 2 dimensional char array, but so it'd follow these rules:

  • The only chars allowed are:
    • O = Open path
    • - = Accepts paths from it's left or from it's right
    • | = Accepts paths from it's top or from it's bottom
    • \ = Accepts paths from: top to left, left to top, bottom to right, and right to bottom.
    • / = Accepts paths from: bottom to left, left to bottom, right to top, and top to right
    • "Accepts paths" means that other paths (/-|\) can only connect from the sides speficied.

Here's an image to understand the chars and what they do: (- in red, \ in blue, / in green, | in orange)

The chars and their meanings.

  • The path cannot cross over itself - it can only go in open places (Open paths: O). Imagine the outcome path as snake, the beloved game - it cannot go through itself.
  • The 2d array can be any size
  • The ending can lead anywhere - It isn't marked with X or any character like it, but it has to go somewhere logical.

Correct outputs:

Start: (0, 0), End: (3, 3)

START-> - - \ O
        O O \ \
        O / - /
        O \ - \ <- END

The first example is basically this: The first example as an image.

Start: (1, 0), End: (1, 4)

START v
    O - \ O O
    / - / O O
    \ - - - \
    O O O O |
    O - - - / 
      ^ END

The second example is basically this: The second example as an image.

I'm trying to accompilsh this using this code, but for some reason, it's not working:

Code:

int x, y, mapsize;
char[][] map;
public Random rand = new Random();
public boolean findPath(int x, int y, int xGoal, int yGoal){
    if(x==xGoal&&y==yGoal)return true;
    int[] avilableMovement = avilableMovement(x, y);
    if(avilableMovement==null)return false;
    int moveX = avilableMovement[0];
    int moveY = avilableMovement[1];
    map[moveX][moveY]=mark(x, y, moveX, moveY);
    if(findPath(moveX, moveY, xGoal, yGoal))return true;
    return false;
}
public char mark(int fromX, int fromY, int toX, int toY){
    //If moved to up/down and <>, mark |
    //If moved to <> and left/right, mark -
    //If moved to up and left, or to down and right, mark \
    //If moved to up and right, or to down and left, mark /
    boolean toUp = fromY<toY;
    boolean toDown = fromY>toY;
    boolean toRight = fromX<toX;
    boolean toLeft = fromX>toX;
    if((toUp||toDown)&&!(toLeft||toRight)){
        return '|';
    }
    if((toLeft||toRight)&&!(toUp||toDown)){
        return '-';
    }
    if((toUp&&toLeft)||(toDown&&toRight)){
        return '\\';
    }
    if((toUp&&toRight)||(toDown&&toLeft)){
        return '/';
    }
    return '?';
}
private boolean onMap(int x, int y){
    return x>0&&y>0&&x<mapsize&&y<mapsize;
}
private int[] avilableMovement(int x, int y){
    ArrayList<Integer> numsX = new ArrayList<>(Arrays.asList(-1+x, 0+x, 1+x));
    ArrayList<Integer> numsY = new ArrayList<>(Arrays.asList(-1+y, 0+y, 1+y));
    Collections.shuffle(numsX);
    Collections.shuffle(numsY);
    //^^ Making it random instead of going in same order every timee
    for(int lx : numsX){
        for(int ly : numsY){
            if(onMap(y+ly, x+lx)&&map[y+ly][x+lx]=='O'){
                return new int[]{x+lx, y+ly};
            }
        }
    }
    return null;
}

When I run the code using this code,

private void initMap(int mapsize){
    this.mapsize=mapsize;
     map = new char[mapsize][mapsize];
    for(int i = 0; i<mapsize; i++){
        for(int j = 0; j<mapsize; j++){
            map[i][j]='O';
        }
    }
}
public static void main(String[] args){
    Main main = new Main();
    main.initMap(4);
    System.out.println(main.findPath(0, 0, 3, 3));
    for(char[] ch : main.map){
        System.out.println(ch);
    }
}

It keeps outputting wrong & illogical paths, such as:

OOOO
O/OO
O-OO
O-OO

Or (with map size 6):

OOOOOO
O/OOOO
O-OOOO
OOO/OO
OOOOOO
OOOOOO

I don't know why this happens. Can anyone tell me what's wrong with my code and help me solve this?

Thanks in advance!

P.S: Before you ask, no, this is not a homework question.

EDIT: I updated my code, and now the method does return true and it reaches the ending, but there's a problem. My updated code:

public Random rand = new Random();
public boolean findPath(int x, int y, int xGoal, int yGoal){
    if(x==xGoal&&y==yGoal)return true;
    int[] avilableMovement = avilableMovement(x, y);
    if(avilableMovement==null)return false;
    int moveX = avilableMovement[0];
    int moveY = avilableMovement[1];
    map[moveX][moveY]=mark(x, y, moveX, moveY);
    if(findPath(moveX, moveY, xGoal, yGoal))return true;
    return false;
}
public char mark(int fromX, int fromY, int toX, int toY){
    //If moved to up/down and <>, mark |
    //If moved to <> and left/right, mark -
    //If moved to up and left, or to down and right, mark \
    //If moved to up and right, or to down and left, mark /
    boolean toUp = fromY<toY;
    boolean toDown = fromY>toY;
    boolean toRight = fromX<toX;
    boolean toLeft = fromX>toX;
    if((toUp||toDown)&&!(toLeft||toRight)){
        return '|';
    }
    if((toLeft||toRight)&&!(toUp||toDown)){
        return '-';
    }
    if((toUp&&toLeft)||(toDown&&toRight)){
        return '\\';
    }
    if((toUp&&toRight)||(toDown&&toLeft)){
        return '/';
    }
    return 'O';
}
private boolean onMap(int x, int y){
    return x>0&&y>0&&x<mapsize&&y<mapsize;
}
private int[] avilableMovement(int x, int y){
    ArrayList<Integer> numsX = new ArrayList<>(Arrays.asList(-1+x, x, 1+x));
    ArrayList<Integer> numsY = new ArrayList<>(Arrays.asList(-1+y, y, 1+y));
    Collections.shuffle(numsX);
    Collections.shuffle(numsY);
    //^^ Making it random instead of going in same order every timee
    for(int lx : numsX){
        for(int ly : numsY){
            if(onMap(ly, lx)&&map[ly][lx]=='O'){
                return new int[]{lx, ly};
            }
        }
    }
    return null;
}

My main code:

Main main = new Main();
    main.initMap(4);
    boolean b = main.findPath(0, 0, 3, 3);
    while(!b)b = main.findPath(0, 0, 3, 3);
    for(int i = 0; i<main.mapsize; i++){
        for(int j = 0; j<main.mapsize; j++){
            System.out.print(main.map[j][i]);
        }
        System.out.println();
    }

I can see in the output that it reaches it's final destenation, but it doesn't show the beginning. Why is that?

Here are some example outputs from the new, updated code:

OOOO
O/OO
O/OO
O---

OOOO
O//-
OO/O
OOO|

As you can see, the outputs still dont make sense, but it's closer than before :P It doesn't exactly follow the rules and it doesn't show the beginning. Why is that?

NonameSL
  • 1,405
  • 14
  • 27
  • Your notation for the pathing is not very clear to me, could you maybe add a small example with the path you would follow written out? Your examples don't mesh with my understanding of the rules, so clearly I'm missing something – Kevin Jan 22 '16 at 20:07
  • 1
    I'm not sure the examples posted follow the rules you specified. In the rules you say '\' accepts from top left, but in the picture its accepting form the left in the hand drawn picture. Did I miss something? (Assuming top left refers to the array position of (x-1, y-1)) – Sh4d0wsPlyr Jan 22 '16 at 20:50
  • If I understand the rules correctly, it appears you already broke the rules in both of your examples where it's only suppose to accept top left or bottom right and you go in from the left or top. – DigitalNinja Jan 22 '16 at 20:50
  • @DigitalNinja I added a drawing and a better explenation for the rules so people can actually understand what each character does. – NonameSL Jan 22 '16 at 21:16
  • @NonameSL Okay, thanks. The rules are clearer and it makes more sense now. – DigitalNinja Jan 22 '16 at 21:50

2 Answers2

1

I see several mistakes (I didn't run your code).

1.Main problem is that you probably mixed x and y coordinates in the your logic and output to the screen, try to change

for(char[] ch : main.map){
    System.out.println(ch);
}

to something like

for(int i = 0; i<mapsize; i++){
    for(int j = 0; j<mapsize; j++){
        System.out.print(map[j][i]);
    }
    System.out.println();
}

I.e. change looping through x and y coordinates for output

2.You probably incorrectly calculate next coordinates from x,y in function avilableMovement. lx and ly already contains x and y, i.e. you add x and y 2 times in x+lx, y+ly:

ArrayList<Integer> numsX = new ArrayList<>(Arrays.asList(-1+x, 0+x, 1+x));
ArrayList<Integer> numsY = new ArrayList<>(Arrays.asList(-1+y, 0+y, 1+y));
Collections.shuffle(numsX);
Collections.shuffle(numsY);
for(int lx : numsX){
    for(int ly : numsY){
        if(onMap(y+ly, x+lx)&&map[y+ly][x+lx]=='O'){
            return new int[]{x+lx, y+ly};

3.You don't return 'O' mark for cell if path is not finded in current cell, and you don't check next possible move:

map[moveX][moveY]=mark(x, y, moveX, moveY);
if(findPath(moveX, moveY, xGoal, yGoal))return true;
return false;
valdem
  • 755
  • 5
  • 10
  • Didn't notice 1 and 2, thanks. Not sure what you quite mean in 3 tho, can you explain yourself? – NonameSL Jan 22 '16 at 21:04
  • Maybe it is not a problem at all. As I understand, you need to find path from one cell to another cell, so if you go randomly, you probably not find it of course, so you need something like backpropagation with restoring information in your field and trying to go to another direction – valdem Jan 22 '16 at 21:07
  • there's a small chance I won't find it, which is why in the new code I made the code keep searching for paths when it doesn't find one. It usually will find the path eventually since I'm dodging previous paths so there's no way I'll end up going in circles. Take a look at my new code - It should work, and I see it's reaching it's destination, but it doesn't show the beginning! Why is that? – NonameSL Jan 22 '16 at 21:23
  • Maybe you need to try `map[x][y]=mark(x, y, moveX, moveY);` except `map[moveX][moveY]=mark(x, y, moveX, moveY);` ? – valdem Jan 22 '16 at 21:29
  • I think that you missed correct '/' and '\' characters because your y=0 coordinate lie on first row, not last row as you see on the screen and intuitively should be. You need to rearrange '\' '/' characters, and probably it should solve the problem – valdem Jan 22 '16 at 21:47
0

Here's another idea for your consideration. Starting with a simple path, for example, straight along the x-axis then straight along the y-axis (or vice versa) from start to end,

start--\
       |
       |    
       |
       \end

we can modify the path using a pre-set dictionary of options. For any one cell that is between two others in the path (which excludes the start and end cells) we define 6 possibilities, each with two ways to modify it. In the following two examples, we are concerned only with modifying the middle cell,x:

(1) cell is connected from west and east:

  o x o

we can move x either north or south:

  / x \
  o   o  or  o   o
             \ x /

(2) cell is connected from west and south:

  o x
    o

we can move x either north or east:

  / x
  o |  or  o - x
    o        o /

Since cells cannot be connected via diagonals, there are only 4 choose 2 = 6 configurations to consider. We can easily create a dictionary of options to randomly try, based on delta configurations of cells in the path. The two examples above would be:

(dictionary key)   (dictionary value)
Δx1 Δy1 Δx2 Δy2
 1   0  -1   0  => try north or south
 1   0   0  -1  => try north or east
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61