java中的生长树算法无法正常工作

时间:2016-12-29 12:05:26

标签: java algorithm libgdx

我试图创建一个地下城生成器。首先,我随意放置房间,然后在它们之间的空隙中生长迷宫。但是我遇到了一个问题,每次我运行我的迷宫算法时,它都会产生奇怪的无法到达的走廊:

Look at the red square

Same as above

看看那里的红色方块。玩家无法进入这些走廊。如何避免它们?这是一些代码:

public void createMaze(byte[][] dungeon) {

    for (int y = 1; y < dungeon.length; y += 2) {

          for (int x = 1; x < dungeon[0].length; x += 2) {

              Vector2 pos = new Vector2(x, y);

              if (dungeon[y][x] != 2 && dungeon[y][x] != 1){
                  growMaze(pos, dungeon);
              }
          }
    }
}

public void growMaze(Vector2 pos, byte[][] dungeon) {

    // Initialize some Lists and Vars

    int lastDir = 0;

    ArrayList<Vector2> cells = new ArrayList<Vector2>();

    // Adding the startPosition to the cell list.

    cells.add(pos);

    // When the position is in the Grid

    if(pos.y < dungeon.length - 2 && pos.y > 0 + 2 && pos.x < dungeon[0].length - 2 && pos.x > 0 + 2){

        // And no walls or floors are around it

        if(isPlaceAble(dungeon , pos)){

            // Then place a corridor tile

            dungeon[(int) pos.y][(int) pos.x] = 4;
        }
    }

    // Here comes the algorithm.

    while(!cells.isEmpty()){    

        // choose the latest cell

        Vector2 choosedCell = cells.get(cells.size() - 1);

       // Check again if the cell is in the grid.


        if(choosedCell.y < dungeon.length - 2 && choosedCell.y > 0 + 2 && choosedCell.x < dungeon[0].length - 2 && choosedCell.x > 0 + 2){


            // When that's true, then check in which directions the cell is able to move


            boolean canGoNorth = dungeon[(int) (choosedCell.y + 1)][(int) choosedCell.x] == 0 && dungeon[(int) (choosedCell.y + 2)][(int) choosedCell.x] == 0;

            boolean canGoSouth = dungeon[(int) (choosedCell.y - 1)][(int) choosedCell.x] == 0 && dungeon[(int) (choosedCell.y - 2)][(int) choosedCell.x] == 0;


            boolean canGoEast = dungeon[(int) (choosedCell.y)][(int) choosedCell.x + 1] == 0 && dungeon[(int) (choosedCell.y)][(int) choosedCell.x + 2] == 0;

            boolean canGoWest = dungeon[(int) (choosedCell.y)][(int) choosedCell.x - 1] == 0 && dungeon[(int) (choosedCell.y)][(int) choosedCell.x - 2] == 0;

            // When there's no available direction, then remove the cell and break the loop...

            if(!canGoNorth && !canGoSouth && !canGoEast && !canGoWest ){

                cells.remove(cells.size() - 1);

                break;
            }
            else{

                // But if there's a available direction, then remove the cell from the list.

                Vector2 savedCell = cells.get(cells.size() - 1);

                cells.remove(cells.get(cells.size() - 1));

                boolean placed = false;

                // And place a new one into a new direction. This will happen as long as one is placed.

                while(!placed){

                    // pick a random direction

                    int randomDirection = MathUtils.random(0,3);

                    int rdm = randomDirection;

                    // Init the length of the cells.

                    int length = 2;

                    // And now begin, if the direction and the random number fits, then dig the corridor. If no direction/number fits, then redo this until it works.

                    if(canGoNorth && rdm == 0 ){ 

                        int ycoord = 0;

                        for(int y = (int) choosedCell.y; y < choosedCell.y + length; y++){ 

                            dungeon[(int) y][(int) choosedCell.x] = 4;
                        }

                        Vector2 newCell = new Vector2(choosedCell.x, choosedCell.y + length);

                        cells.add(newCell);

                        lastDir = 0;

                        placed = true;
                    }

                    if(canGoSouth && rdm == 1 ){

                        int ycoord = 0;

                        for(int y = (int) choosedCell.y; y > choosedCell.y - length; y--){

                            dungeon[(int) y][(int) choosedCell.x] = 4;
                        }

                        Vector2 newCell = new Vector2(choosedCell.x, choosedCell.y - length); 

                        cells.add(newCell);

                        lastDir = 1;

                        placed = true;
                    }

                    if(canGoEast && rdm == 2 ){

                        int xcoord = 0;

                        for(int x = (int) choosedCell.x; x < choosedCell.x + length; x++){

                            dungeon[(int) choosedCell.y][x] = 4;
                        } 

                        Vector2 newCell = new Vector2(choosedCell.x + length, choosedCell.y );

                        cells.add(newCell);

                        lastDir = 2;

                        placed = true;
                    }

                    if(canGoWest && rdm == 3 ){

                        int xcoord = 0;

                        for(int x = (int) choosedCell.x; x > choosedCell.x - length; x--){

                            dungeon[(int) choosedCell.y][x] = 4;
                        } 

                        Vector2 newCell = new Vector2(choosedCell.x - length, choosedCell.y ); 

                        cells.add(newCell);

                        lastDir = 3;

                        placed = true;
                    }
                }
            }
        }
        else{

            cells.remove(cells.size() - 1);
        }
    }   

    // And finally delete dead end cells :) (Those who only got 3 Wall/Floor neighbours or 4)

    killDeadEnds(dungeon);
}

那么如何避免这些无法到达的迷宫?

1 个答案:

答案 0 :(得分:1)

您可以使用union-find结构快速查找和删除未连接到房间的所有单元格。请参阅:https://en.wikipedia.org/wiki/Disjoint-set_data_structure

您最初为每个走廊或房间单元创建一个不相交的集合,然后将相邻房间或单元格的每对集合联合起来。最后,删除与房间不在同一集合中的所有道路单元格。

Union-find也是一个很好的迷宫生成算法的基础,它只是Kruskal用于在应用于网格的图形(https://en.wikipedia.org/wiki/Kruskal%27s_algorithm)中查找生成树的算法。请参阅:http://weblog.jamisbuck.org/2011/1/3/maze-generation-kruskal-s-algorithm

在应用死角移除之前,您可以首先使用此算法生成迷宫。但它会改变迷宫的性格,所以也许你不想这样做。