ProjektGraph/visualizationElements/Maze.java
2024-07-02 21:13:30 +02:00

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;
}
}