Maze.java


Below is the syntax highlighted version of Maze.java from §4.1 Undirected Graphs.


/******************************************************************************
 *  Compilation:  javac Maze.java
 *  Execution:    java Maze.java cols rows
 *  Dependencies: StdDraw.java
 *
 *  Generates a perfect rows-by-rows maze using depth-first search with a stack.
 *
 *  % java Maze 16 16
 *
 *  % java Maze 32 18
 *
 *  Note: this program generalizes nicely to finding a random tree
 *        in a graph.
 *
 ******************************************************************************/

public class Maze {
    private final int cols, rows;   // dimension of maze
    private boolean[][] north;      // is there a wall to north of cell (col, row)
    private boolean[][] east;
    private boolean[][] south;
    private boolean[][] west;
    private boolean[][] visited;
    private boolean isDone = false;

    public Maze(int cols, int rows) {
        this.cols = cols;
        this.rows = rows;
        int height = 800;
        int width = (int) Math.round(1.0 * height * cols / rows);
        StdDraw.setCanvasSize(width, height);

        StdDraw.setXscale(0, cols + 2);
        StdDraw.setYscale(0, rows + 2);
        init();
        generate();
    }

    private void init() {
        // initialize border cells as already visited
        visited = new boolean[cols + 2][rows + 2];
        for (int col = 0; col < cols + 2; col++) {
            visited[col][0] = true;
            visited[col][rows + 1] = true;
        }
        for (int row = 0; row < rows + 2; row++) {
            visited[0][row] = true;
            visited[cols + 1][row] = true;
        }


        // initialize all walls as present
        north = new boolean[cols + 2][rows + 2];
        east = new boolean[cols + 2][rows + 2];
        south = new boolean[cols + 2][rows + 2];
        west = new boolean[cols + 2][rows + 2];
        for (int col = 0; col < cols + 2; col++) {
            for (int row = 0; row < rows + 2; row++) {
                north[col][row] = true;
                east[col][row] = true;
                south[col][row] = true;
                west[col][row] = true;
            }
        }
    }


    // generate the maze
    private void generate(int col, int row) {
        visited[col][row] = true;

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

            // pick random neighbor (could use Knuth's trick instead)
            while (true) {
                double r = StdRandom.uniformInt(4);
                if (r == 0 && !visited[col][row + 1]) {
                    north[col][row] = false;
                    south[col][row + 1] = false;
                    generate(col, row + 1);
                    break;
                }
                else if (r == 1 && !visited[col + 1][row]) {
                    east[col][row] = false;
                    west[col + 1][row] = false;
                    generate(col + 1, row);
                    break;
                }
                else if (r == 2 && !visited[col][row - 1]) {
                    south[col][row] = false;
                    north[col][row - 1] = false;
                    generate(col, row - 1);
                    break;
                }
                else if (r == 3 && !visited[col - 1][row]) {
                    west[col][row] = false;
                    east[col - 1][row] = false;
                    generate(col - 1, row);
                    break;
                }
            }
        }
    }

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

/*
        // delete some random walls
        for (int i = 0; i < rows; i++) {
            int col = 1 + StdRandom.uniformInt(cols - 1);
            int row = 1 + StdRandom.uniformInt(rows - 1);
            north[col][row] = false;
            south[col][row + 1] = false;
        }

        // add some random walls
        for (int i = 0; i < cols; i++) {
            int col = cols / 2 + StdRandom.uniformInt(cols / 2);
            int row = rows / 2 + StdRandom.uniformInt(rows / 2);
            east[col][row] = west[col + 1][row] = true;
        }
*/
    }


    // solve the maze using depth-first search
    private void solve(int col, int row) {
        if (isDone) return;
        if (col == 0 || row == 0 || col == cols + 1 || row == rows + 1) return;
        if (visited[col][row]) return;
        visited[col][row] = true;

        StdDraw.setPenColor(StdDraw.BLUE);
        StdDraw.filledCircle(col + 0.5, row + 0.5, 0.25);
        StdDraw.show();
        StdDraw.pause(250);

        // reached middle
        if (col == cols / 2 && row == rows / 2) isDone = true;

        if (!north[col][row]) solve(col, row + 1);
        if (!east[col][row]) solve(col + 1, row);
        if (!south[col][row]) solve(col, row - 1);
        if (!west[col][row]) solve(col - 1, row);

        if (isDone) return;

        StdDraw.setPenColor(StdDraw.GRAY);
        StdDraw.filledCircle(col + 0.5, row + 0.5, 0.25);
        StdDraw.show();
        StdDraw.pause(250);
    }

    // solve the maze starting from the start state
    public void solve() {
        for (int col = 1; col <= cols; col++)
            for (int row = 1; row <= rows; row++)
                visited[col][row] = false;
        isDone = false;
        solve(1, 1);
    }

    // draw the maze
    public void draw() {
        int targetCol = cols / 2;
        int targetRow = rows / 2;
        StdDraw.setPenColor(StdDraw.RED);
        StdDraw.filledCircle(targetCol + 0.5, targetRow + 0.5, 0.375);
        StdDraw.filledCircle(1.5, 1.5, 0.375);

        StdDraw.setPenColor(StdDraw.BLACK);
        for (int col = 1; col <= cols; col++) {
            for (int row = 1; row <= rows; row++) {
                if (south[col][row]) StdDraw.line(col, row, col + 1, row);
                if (north[col][row]) StdDraw.line(col, row + 1, col + 1, row + 1);
                if (west[col][row]) StdDraw.line(col, row, col, row + 1);
                if (east[col][row]) StdDraw.line(col + 1, row, col + 1, row + 1);
            }
        }
        StdDraw.show();
        StdDraw.pause(1000);
    }


    // a test client
    public static void main(String[] args) {
        int cols = Integer.parseInt(args[0]);
        int rows = Integer.parseInt(args[1]);
        Maze maze = new Maze(cols, rows);
        StdDraw.enableDoubleBuffering();
        maze.draw();
        maze.solve();
    }

}


Copyright © 2000–2019, Robert Sedgewick and Kevin Wayne.
Last updated: Thu Aug 11 09:22:35 EDT 2022.