Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

This project asks you to write a program that can: Generate a random two-dimensi

ID: 3842546 • Letter: T

Question

This project asks you to write a program that can: Generate a random two-dimensional square maze whose size is specified by the user, and Read in a maze from a given text file (more about this later) Once the program has generated a random maze or read in a maze from a file, the program would solve the maze by finding a path from the startposition to the goal position in the maze. Because of the how the maze is generated (to be discussed next) or specified in the file, there will always be a path from the starting position to the goal position in the maze. The maze structure: The maze is an nxn grid of cells (which we also call rooms), where n is a positive integer specified by the user. Each room in the maze can have at most 4 doors, each of which (if open) can lead to the adjacent room to the north, south, east, or west. There is a passage way between two adjacent rooms if and only if both doors connecting the two adjacent rooms are open. There are two special rooms in the maze called the start room and the goal room. The start room is always the upper-left room in the maze and the goal room is always the bottom-right room in the maze. The start room has its door leading to the north always open while the goal room has its door leading to the south always open. Rooms on the boundary of the maze (except the start and goal rooms) have their doors leading out of the maze always close. As an example, the following figure shows a randomly generated 5 x 5 maze rendered in ASCII characters where each horizontal or vertical line character denotes closed door(s). I I I I I I I I I Maze generation: To randomly generate an nxn maze, we use the algorithm below. The algorithm assumes that the rooms are uniquely numbered from left to right, top to bottom. The numbering of rooms starts from 0 and ends with N-1, where N is the total number of rooms in the maze (i.e., N n For the example maze given above, the start room is numbered 0 while the goal room is numbered 24

Explanation / Answer

Here is the Java code for random 2D square maze generation,

import java.util.ArrayList;

import java.util.Collections;

import java.util.Comparator;

import java.util.Random;

public class TwodimMaze {

    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 TwodimMaze(int aDimension) {

        // Initialize

        this(aDimension, aDimension);

    }

    // constructor

    public TwodimMaze(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 += " ";

        }

        return output;

    }

    // run it

    public static void main(String[] args) {

        TwodimMaze maze = new TwodimMaze(50);

        maze.solve();

        System.out.print(maze);

    }

}

Hire Me For All Your Tutoring Needs
Integrity-first tutoring: clear explanations, guidance, and feedback.
Drop an Email at
drjack9650@gmail.com
Chat Now And Get Quote