328 lines
7.8 KiB
Java
328 lines
7.8 KiB
Java
package visualizationElements;
|
|
|
|
import java.awt.*;
|
|
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<Cell> 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<Cell>();
|
|
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<Cell> 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<Cell> getUnvisitedCellNeighbors(Cell currentCell) {
|
|
Vector<Cell> neighbours = getAllCellNeighbours(currentCell);
|
|
Vector<Cell> unvisitedNeighbours = new Vector<Cell>();
|
|
|
|
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<Cell> getAllCellNeighbours(Cell currentCell) {
|
|
Vector<Cell> neighbours = new Vector<Cell>();
|
|
|
|
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;
|
|
}
|
|
}
|