package visualizationElements; import java.awt.Graphics; import java.util.Stack; import java.util.Vector; /** * Represents a maze to visualize. * @author MSchaefer * */ public class Maze extends VisualizationElement{ private static final int CELL_WIDTH = 30; private static final int CELL_HEIGTH = 30; private static final int START_X_POS = 20; private static final int START_Y_POS = 20; private Cell[][] maze; private Cell currentCell; private int heigth; private int width; private int xpos; private int ypos; private Stack cellStack; /** * Creates a new Maze. * @param heigth Height of the Maze. * @param width Width of the Maze. */ public Maze(int heigth, int width){ super(); this.setHeigth(heigth); this.setWidth(width); this.cellStack = new Stack(); this.maze = new Cell[width][heigth]; initMaze(); } @Override public void draw(Graphics g) { xpos = START_X_POS; ypos = START_Y_POS; for(int column = 0; column < width; column++){ for(int row = 0; row < heigth; row++){ Cell currentCell = maze[column][row]; if(currentCell.hasNorthWall()){ g.drawLine(xpos, ypos, xpos + CELL_WIDTH, ypos); } if(currentCell.hasSouthWall()){ g.drawLine(xpos, ypos + CELL_HEIGTH, xpos + CELL_WIDTH, ypos + CELL_HEIGTH); } if(currentCell.hasWestWall()){ g.drawLine(xpos, ypos, xpos, ypos + CELL_HEIGTH); } if(currentCell.hasEastWall()){ g.drawLine(xpos + CELL_WIDTH, ypos, xpos + CELL_WIDTH, ypos + CELL_HEIGTH); } ypos = ypos + CELL_HEIGTH; } ypos = START_Y_POS; xpos = xpos + CELL_WIDTH; } } /** * @param maze the maze to set */ public void setMaze(Cell[][] maze) { this.maze = maze; } /** * @return the maze */ public Cell[][] getMaze() { return maze; } /** * @param heigth the heigth to set */ public void setHeigth(int heigth) { this.heigth = heigth; } /** * @return the heigth */ public int getHeigth() { return heigth; } /** * @param width the width to set */ public void setWidth(int width) { this.width = width; } /** * @return the width */ public int getWidth() { return width; } /** * Initializes the maze. */ private void initMaze() { initMazeWithAllWalls(); generateWays(); } /** * Initializes a Maze full of walls. */ private void initMazeWithAllWalls() { xpos = START_X_POS; ypos = START_Y_POS; for(int column = 0; column < width; column++){ for(int row = 0; row < heigth; row++){ maze[column][row] = new Cell(column, row); ypos = ypos + CELL_HEIGTH; } xpos = xpos + CELL_WIDTH; } } /** * Generates ways through a maze full of walls. */ private void generateWays(){ /* -Pick a room randomly, and push it onto the stack. This is the starting room. -Pick a room adjacent to the first room, and open a door between them. This room is the current room. -Push the starting room onto the stack. -While there's at least one room left on the stack, repeat the following steps: -If there are any unconnected rooms next to the current room, -Pick one randomly, and open a door between it and the current room. -Push the current room onto the stack; the new room becomes the current room. -Go back to the top of the loop. -If there are no unconnected rooms next to the current room, -Pop a room off of the stack; it becomes the current room. -Go back to the top of the loop. -When the stack is empty, the maze is complete.*/ boolean goalMarked = false; currentCell = maze[0][0]; currentCell.setAsStart(); currentCell.removeNorthWall(); cellStack.push(currentCell); while(cellStack.size() > 0){ currentCell.markAsVisited(); if(!goalMarked){ if(currentCell.getRow() == maze.length - 1){ currentCell.setAsGoal(); currentCell.removeSouthWall(); goalMarked = true; } else if(currentCell.getColumn() == maze[0].length - 1){ currentCell.setAsGoal(); currentCell.removeEastWall(); goalMarked = true; } } Vector unvisitedNeighbours = getUnvisitedCellNeighbors(currentCell); if(unvisitedNeighbours.size() != 0){ int randomUnvisitedNeighbourIndex = (int) (Math.random() * unvisitedNeighbours.size()); Cell randomUnvisitedNeighbour = unvisitedNeighbours.get(randomUnvisitedNeighbourIndex); removeWallBetweenCurrentCellAndNeighbour(currentCell, randomUnvisitedNeighbour); cellStack.push(currentCell); currentCell = randomUnvisitedNeighbour; } else{ currentCell = cellStack.pop(); } } } /** * Removes a cell's south wall. * @param column Column of the cell. * @param row Row of the cell. */ private void removeCellsSouthWall(int column, int row){ maze[column][row].removeSouthWall(); // removes WestWall of neighbor if(row < maze[column].length && maze[column][row + 1].hasNorthWall()){ removeCellsNorthWall(column, row + 1); } } /** * Removes a cell's north wall. * @param column Column of the cell. * @param row Row of the cell. */ private void removeCellsNorthWall(int column, int row){ maze[column][row].removeNorthWall(); // removes WestWall of neighbor if(row < maze[column].length && maze[column][row - 1].hasSouthWall()){ removeCellsSouthWall(column, row - 1); } } /** * Removes a cell's east wall. * @param column Column of the cell. * @param row Row of the cell. */ private void removeCellsEastWall(int column, int row) { maze[column][row].removeEastWall(); // removes WestWall of neighbor if(column < maze.length - 1 && maze[column + 1][row].hasWestWall()){ removeCellsWestWall(column + 1, row); } } /** * Removes a cell's west wall. * @param column Column of the cell. * @param row Row of the cell. */ private void removeCellsWestWall(int column, int row){ maze[column][row].removeWestWall(); // removes WestWall of neighbor if(column < maze.length - 1 && maze[column - 1][row].hasEastWall()){ removeCellsEastWall(column - 1, row); } } /** * Removes the wall between two cells. * @param cell Cell to remove a wall. * @param neighbor The Cell's neighbour to remove the corresponding wall. */ private void removeWallBetweenCurrentCellAndNeighbour(Cell cell, Cell neighbor) { if(neighbor.getRow() == cell.getRow()){ if(neighbor.getColumn() < cell.getColumn()){ removeCellsWestWall(cell.getColumn(), cell.getRow()); } else{ removeCellsEastWall(cell.getColumn(), cell.getRow()); } } else if(neighbor.getRow() < cell.getRow()){ removeCellsNorthWall(cell.getColumn(), cell.getRow()); } else{ removeCellsSouthWall(cell.getColumn(), cell.getRow()); } } /** * Gets all unvisited neighbors of a cell. * @param currentCell The cell to get the neighbors. * @return The cell's unvisited neighbors. */ private Vector getUnvisitedCellNeighbors(Cell currentCell) { Vector neighbours = getAllCellNeighbours(currentCell); Vector unvisitedNeighbours = new Vector(); for(Cell neighbour : neighbours){ if(!neighbour.isVisited()){ unvisitedNeighbours.add(neighbour); } } return unvisitedNeighbours; } /** * Gets all neighbors of a cell. * @param currentCell The cell to get the neighbors. * @return The cell's neighbors. */ private Vector getAllCellNeighbours(Cell currentCell) { Vector neighbours = new Vector(); int currentCellRow = currentCell.getRow(); int currentCellColumn = currentCell.getColumn(); if(currentCellRow < heigth-1){ neighbours.add(maze[currentCellColumn][currentCellRow+1]); } if(currentCellRow != 0){ neighbours.add(maze[currentCellColumn][currentCellRow-1]); } if(currentCellColumn < width-1){ neighbours.add(maze[currentCellColumn+1][currentCellRow]); } if(currentCellColumn != 0){ neighbours.add(maze[currentCellColumn-1][currentCellRow]); } return neighbours; } }