1

我尝试生成迷宫,在对面有两个条目。

但是当我尝试运行这个测试程序时,我发现ArrayIndexOutOfBoundsException

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
    at maze.variation.MyMaze.generateMaze(MyMaze.java:61)
    at maze.variation.MyMaze.generateMaze(MyMaze.java:63)
    at maze.variation.MyMaze.generateMaze(MyMaze.java:51)
    at maze.variation.MyMaze.generateMaze(MyMaze.java:51)
    at maze.variation.MyMaze.generateMaze(MyMaze.java:51)
    at maze.variation.MyMaze.generateMaze(MyMaze.java:51)
    at maze.variation.MyMaze.generateMaze(MyMaze.java:35)
    at maze.variation.MyMaze.<init>(MyMaze.java:14)
    at maze.variation.MyMaze.main(MyMaze.java:137)

我不知道出了什么问题。这是一些简单的失败,但我找不到弱点。

代码:

public class MyMaze {
    private int dimension; // dimension of maze
    private char[][] grid;
    private boolean[][] marked;
    private boolean[][] visited;
    private boolean done = false;

    public MyMaze(int aDimension) {
        dimension = aDimension;
        grid = new char[dimension][dimension];
        init();
        generateMaze();
    }

    private void init() {
        // initialize border cells as already visited
        visited = new boolean[dimension + 2][dimension + 2];
        for (int x = 0; x < dimension + 2; x++)
            visited[x][0] = visited[x][dimension + 1] = true;
        for (int y = 0; y < dimension + 2; y++)
            visited[0][y] = visited[dimension + 1][y] = true;

        // initialze all walls as present
        visited = new boolean[dimension + 2][dimension + 2];
        marked = new boolean[dimension + 2][dimension + 2];
        for (int x = 0; x < dimension + 2; x++)
            for (int y = 0; y < dimension + 2; y++)
                marked[x][y] = true;
    }

    // generate the maze starting from lower left
    private void generateMaze() {
        generateMaze(1, 1);
    }

    // generate the maze
    private void generateMaze(int x, int y) {
        visited[x][y] = true;

        // while there is an unvisited neighbor
        while (!visited[x][y + 1] || !visited[x + 1][y] || !visited[x][y - 1]
                || !visited[x - 1][y]) {

            // pick random neighbor
            while (true) {
                double r = Math.random();
                if (r < 0.25 && !visited[x][y + 1]) {
                    marked[x][y] = marked[x][y + 1] = false;
                    generateMaze(x, y + 1);            // 51 line
                    break;
                } else if (r >= 0.25 && r < 0.50 && !visited[x + 1][y]) {
                    marked[x][y] = marked[x + 1][y] = false;
                    generateMaze(x + 1, y);
                    break;
                } else if (r >= 0.5 && r < 0.75 && !visited[x][y - 1]) {
                    marked[x][y] = marked[x][y - 1] = false;
                    generateMaze(x, y - 1);
                    break;
                } else if (r >= 0.75 && r < 1.00 && !visited[x - 1][y]) { // 61 line
                    marked[x][y] = marked[x - 1][y] = false;
                    generateMaze(x - 1, y);
                    break;
                }
            }
        }
    }

    // solve the maze starting from the start state
    public void solve() {
        for (int x = 1; x <= dimension; x++)
            for (int y = 1; y <= dimension; y++)
                visited[x][y] = false;
        done = false;
        solve(1, 1);
    }    

    // draw the maze
    public void draw() {

        for (int x = 1; x <= dimension; x++) {
            for (int y = 1; y <= dimension; y++) {
                if (marked[x][y]) {
                    grid[x][y] = '*';                       
                }
            }
        }
            System.out.print(this.grid);
    }

    /**
     * Overridden method to generate a human readable maze state.
     */
    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer(1024);
        for (int x = 0; x < this.dimension; x++) {
            for (int y = 0; y < this.dimension; y++) {
                sb.append(this.grid[x][y]);
            }
            sb.append("\n");
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        int userDimension = 40;
        MyMaze maze = new MyMaze(userDimension);
        maze.draw();
    }
}

我不知道如何找到在相对两侧(北和南)创建两个条目的解决方案,它们需要与迷宫连接。有什么建议么?

  • 如何解决这个错误麻烦?

编辑:

我将此检查添加到 while 循环中:

    // pick random neighbor
    while (true) {
       if (x == 0 || x == 1 || y == 0 || y == 1) continue;
       // method's rest

现在看起来没问题。如果你运行这段代码,你会发现没有输出。但它应该是。
为什么这不能正确打印迷宫?

4

3 回答 3

2

我在你的脚本中注意到的第一件事是init()你初始化visited并使用 for 循环来初始化它,然后在你再次初始化之后立即并且不要将其中的任何值设置为 true。我认为这会导致墙壁被消除。然后稍后在生成期间的脚本中,可能会出现在墙上,因为最初没有标记为已访问。然后在迷宫生成期间,因为它位于数组的“边缘”,它可能会尝试引用数组外部的单元格,这是你的错误。所以去掉第二次初始化visited

您应该警惕的另一件事是无限 while 循环的可能性。确定有邻居后的while循环有可能在理论上不断地不选择正确的r来获取邻居,但它确实有可能大大减慢速度。相反,我会找到所有邻居,将它们放入一个 `ArrayList 中,然后随机选择一个。请参阅下面的来源。

draw方法中存在另一个错误。for 循环应该使用 and 而不是 的条件x < this.dimension进行y < this.dimension迭代<=。否则你会得到 ArrayIndexOutOfBounds 错误。

最后,println我们将打印一个不包含其内容信息的数组的价值较低的表示。改为这样做:

System.out.println(Arrays.deepToString(this.grid));

这是我的代码的最终版本。有一个内部类Cell和一个字段Cell[][] cellsMyMaze其中取代了对桥梁的需求和用于跟踪细胞状态的原始变量。外部的 2 行和列被删除,因为ArrayIndexOutOfBoundsException被捕获并在超出范围时getCell返回。null生成的想法已经改变,以防止过度调用以避免在大型板上出现堆栈溢出。板子现在可以是矩形的。求解器基于 A-star 算法实现。输出已更新以产生有意义的表示。“X”是墙壁,“*”是解决的路径。它已被评论,因此应该能够阅读正在发生的事情

这是我的最终来源:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;

public class MyMaze {
  private int dimensionX, dimensionY; // dimension of maze
  private int gridDimensionX, gridDimensionY; // dimension of output grid
  private char[][] grid; // output grid
  private Cell[][] cells; // 2d array of Cells
  private Random random = new Random(); // The random object

  // initialize with x and y the same
  public MyMaze(int aDimension) {
      // Initialize
      this(aDimension, aDimension);
  }
  // constructor
  public MyMaze(int xDimension, int yDimension) {
      dimensionX = xDimension;
      dimensionY = yDimension;
      gridDimensionX = xDimension * 4 + 1;
      gridDimensionY = yDimension * 2 + 1;
      grid = new char[gridDimensionX][gridDimensionY];
      init();
      generateMaze();
  }

  private void init() {
      // create cells
      cells = new Cell[dimensionX][dimensionY];
      for (int x = 0; x < dimensionX; x++) {
          for (int y = 0; y < dimensionY; y++) {
              cells[x][y] = new Cell(x, y, false); // create cell (see Cell constructor)
          }
      }
  }

  // inner class to represent a cell
  private class Cell {
    int x, y; // coordinates
    // cells this cell is connected to
    ArrayList<Cell> neighbors = new ArrayList<>();
    // solver: if already used
    boolean visited = false;
    // solver: the Cell before this one in the path
    Cell parent = null;
    // solver: if used in last attempt to solve path
    boolean inPath = false;
    // solver: distance travelled this far
    double travelled;
    // solver: projected distance to end
    double projectedDist;
    // impassable cell
    boolean wall = true;
    // if true, has yet to be used in generation
    boolean open = true;
    // construct Cell at x, y
    Cell(int x, int y) {
        this(x, y, true);
    }
    // construct Cell at x, y and with whether it isWall
    Cell(int x, int y, boolean isWall) {
        this.x = x;
        this.y = y;
        this.wall = isWall;
    }
    // add a neighbor to this cell, and this cell as a neighbor to the other
    void addNeighbor(Cell other) {
        if (!this.neighbors.contains(other)) { // avoid duplicates
            this.neighbors.add(other);
        }
        if (!other.neighbors.contains(this)) { // avoid duplicates
            other.neighbors.add(this);
        }
    }
    // used in updateGrid()
    boolean isCellBelowNeighbor() {
        return this.neighbors.contains(new Cell(this.x, this.y + 1));
    }
    // used in updateGrid()
    boolean isCellRightNeighbor() {
        return this.neighbors.contains(new Cell(this.x + 1, this.y));
    }
    // useful Cell representation
    @Override
    public String toString() {
        return String.format("Cell(%s, %s)", x, y);
    }
    // useful Cell equivalence
    @Override
    public boolean equals(Object other) {
        if (!(other instanceof Cell)) return false;
        Cell otherCell = (Cell) other;
        return (this.x == otherCell.x && this.y == otherCell.y);
    }
    // should be overridden with equals
    @Override
    public int hashCode() {
        // random hash code method designed to be usually unique
        return this.x + this.y * 256;
    }
  }
  // generate from upper left (In computing the y increases down often)
  private void generateMaze() {
      generateMaze(0, 0);
  }
  // generate the maze from coordinates x, y
  private void generateMaze(int x, int y) {
      generateMaze(getCell(x, y)); // generate from Cell
  }
  private void generateMaze(Cell startAt) {
      // don't generate from cell not there
      if (startAt == null) return;
      startAt.open = false; // indicate cell closed for generation
      ArrayList<Cell> cells = new ArrayList<>();
      cells.add(startAt);

      while (!cells.isEmpty()) {
          Cell cell;
          // this is to reduce but not completely eliminate the number
          //   of long twisting halls with short easy to detect branches
          //   which results in easy mazes
          if (random.nextInt(10)==0)
              cell = cells.remove(random.nextInt(cells.size()));
          else cell = cells.remove(cells.size() - 1);
          // for collection
          ArrayList<Cell> neighbors = new ArrayList<>();
          // cells that could potentially be neighbors
          Cell[] potentialNeighbors = new Cell[]{
              getCell(cell.x + 1, cell.y),
              getCell(cell.x, cell.y + 1),
              getCell(cell.x - 1, cell.y),
              getCell(cell.x, cell.y - 1)
          };
          for (Cell other : potentialNeighbors) {
              // skip if outside, is a wall or is not opened
              if (other==null || other.wall || !other.open) continue;
              neighbors.add(other);
          }
          if (neighbors.isEmpty()) continue;
          // get random cell
          Cell selected = neighbors.get(random.nextInt(neighbors.size()));
          // add as neighbor
          selected.open = false; // indicate cell closed for generation
          cell.addNeighbor(selected);
          cells.add(cell);
          cells.add(selected);
      }
  }
  // used to get a Cell at x, y; returns null out of bounds
  public Cell getCell(int x, int y) {
      try {
          return cells[x][y];
      } catch (ArrayIndexOutOfBoundsException e) { // catch out of bounds
          return null;
      }
  }

  public void solve() {
      // default solve top left to bottom right
      this.solve(0, 0, dimensionX - 1, dimensionY -1);
  }
  // solve the maze starting from the start state (A-star algorithm)
  public void solve(int startX, int startY, int endX, int endY) {
      // re initialize cells for path finding
      for (Cell[] cellrow : this.cells) {
          for (Cell cell : cellrow) {
              cell.parent = null;
              cell.visited = false;
              cell.inPath = false;
              cell.travelled = 0;
              cell.projectedDist = -1;
          }
      }
      // cells still being considered
      ArrayList<Cell> openCells = new ArrayList<>();
      // cell being considered
      Cell endCell = getCell(endX, endY);
      if (endCell == null) return; // quit if end out of bounds
      { // anonymous block to delete start, because not used later
          Cell start = getCell(startX, startY);
          if (start == null) return; // quit if start out of bounds
          start.projectedDist = getProjectedDistance(start, 0, endCell);
          start.visited = true;
          openCells.add(start);
      }
      boolean solving = true;
      while (solving) {
          if (openCells.isEmpty()) return; // quit, no path
          // sort openCells according to least projected distance
          Collections.sort(openCells, new Comparator<Cell>(){
              @Override
              public int compare(Cell cell1, Cell cell2) {
                  double diff = cell1.projectedDist - cell2.projectedDist;
                  if (diff > 0) return 1;
                  else if (diff < 0) return -1;
                  else return 0;
              }
          });
          Cell current = openCells.remove(0); // pop cell least projectedDist
          if (current == endCell) break; // at end
          for (Cell neighbor : current.neighbors) {
              double projDist = getProjectedDistance(neighbor,
                      current.travelled + 1, endCell);
              if (!neighbor.visited || // not visited yet
                      projDist < neighbor.projectedDist) { // better path
                  neighbor.parent = current;
                  neighbor.visited = true;
                  neighbor.projectedDist = projDist;
                  neighbor.travelled = current.travelled + 1;
                  if (!openCells.contains(neighbor))
                      openCells.add(neighbor);
              }
          }
      }
      // create path from end to beginning
      Cell backtracking = endCell;
      backtracking.inPath = true;
      while (backtracking.parent != null) {
          backtracking = backtracking.parent;
          backtracking.inPath = true;
      }
  }
  // get the projected distance
  // (A star algorithm consistent)
  public double getProjectedDistance(Cell current, double travelled, Cell end) {
      return travelled + Math.abs(current.x - end.x) + 
              Math.abs(current.y - current.x);
  }

  // draw the maze
  public void updateGrid() {
      char backChar = ' ', wallChar = 'X', cellChar = ' ', pathChar = '*';
      // fill background
      for (int x = 0; x < gridDimensionX; x ++) {
          for (int y = 0; y < gridDimensionY; y ++) {
              grid[x][y] = backChar;
          }
      }
      // build walls
      for (int x = 0; x < gridDimensionX; x ++) {
          for (int y = 0; y < gridDimensionY; y ++) {
              if (x % 4 == 0 || y % 2 == 0)
                  grid[x][y] = wallChar;
          }
      }
      // make meaningful representation
      for (int x = 0; x < dimensionX; x++) {
          for (int y = 0; y < dimensionY; y++) {
              Cell current = getCell(x, y);
              int gridX = x * 4 + 2, gridY = y * 2 + 1;
              if (current.inPath) {
                  grid[gridX][gridY] = pathChar;
                  if (current.isCellBelowNeighbor())
                      if (getCell(x, y + 1).inPath) {
                          grid[gridX][gridY + 1] = pathChar;
                          grid[gridX + 1][gridY + 1] = backChar;
                          grid[gridX - 1][gridY + 1] = backChar;
                      } else {
                          grid[gridX][gridY + 1] = cellChar;
                          grid[gridX + 1][gridY + 1] = backChar;
                          grid[gridX - 1][gridY + 1] = backChar;
                      }
                  if (current.isCellRightNeighbor())
                      if (getCell(x + 1, y).inPath) {
                          grid[gridX + 2][gridY] = pathChar;
                          grid[gridX + 1][gridY] = pathChar;
                          grid[gridX + 3][gridY] = pathChar;
                      } else {
                          grid[gridX + 2][gridY] = cellChar;
                          grid[gridX + 1][gridY] = cellChar;
                          grid[gridX + 3][gridY] = cellChar;
                      }
              } else {
                  grid[gridX][gridY] = cellChar;
                  if (current.isCellBelowNeighbor()) {
                      grid[gridX][gridY + 1] = cellChar;
                      grid[gridX + 1][gridY + 1] = backChar;
                      grid[gridX - 1][gridY + 1] = backChar;
                  }
                  if (current.isCellRightNeighbor()) {
                      grid[gridX + 2][gridY] = cellChar;
                      grid[gridX + 1][gridY] = cellChar;
                      grid[gridX + 3][gridY] = cellChar;
                  }
              }
          }
      }
  }

  // simply prints the map
  public void draw() {
      System.out.print(this);
  }
  // forms a meaningful representation
  @Override
  public String toString() {
      updateGrid();
      String output = "";
      for (int y = 0; y < gridDimensionY; y++) {
          for (int x = 0; x < gridDimensionX; x++) {
              output += grid[x][y];
          }
          output += "\n";
      }
      return output;
  }

  // run it
  public static void main(String[] args) {
      MyMaze maze = new MyMaze(20);
      maze.solve();
      System.out.print(maze);
  }
}
于 2013-08-25T12:55:34.907 回答
1

创建一个将存储迷宫的类,并编写一个受保护的 getter ArrayIndexOutOfBoundsException

class Maze {
int maze[][];
int getValue(int x, int y){
  if (x<0 || x>maze.length) {
    return 0;
  }
  if (y<0 || y>maze[0].length) {
    return 0;
  }
  return maze[x][y];
}

还提供一个以类似方式工作的 setter。

对迷宫的每个二维数组成员重复相同的工作。

顺便说一句:考虑使用枚举而不是字符 - 你会发现它们更强大。编辑:枚举使用代码示例

enum Tile {
  WALL('*', false), EMPTY(' ', true), WATER('~', false), GRASS('_', true);

  private Tile (char representation, boolean passable) {
    this.representation=representation;
    this.passable=passable;
  }

  char representation;
  boolean passable;

  public char getRepresentation() { return representation; }
  public boolean isPassable() { return passable; }
}

Tile maze[][];
于 2013-08-23T06:57:55.883 回答
1

在第 30 行,在 的开头generateMaze(int x, int y),添加一个System.err.println("dimension is " + dimension + " x is " + x + " y is " + y);

在错误控制台中,您可能会看到 x 或 y 有时在边界之外或附近。请注意,您不能在离外边界太近的地方进行交互,因为您可以直接访问第 49、50、53、54、57、58、61 和 62 行的邻居。

于 2013-08-23T07:07:49.770 回答