Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java - Algorithm for generating random path in 2d char array

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?

like image 844
NonameSL Avatar asked Nov 09 '22 21:11

NonameSL


1 Answers

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;
like image 53
valdem Avatar answered Nov 14 '22 23:11

valdem