maze-generator/src/main/java/ch/fritteli/maze/generator/algorithm/RandomDepthFirst.java

104 lines
3.7 KiB
Java

package ch.fritteli.maze.generator.algorithm;
import ch.fritteli.maze.generator.model.Direction;
import ch.fritteli.maze.generator.model.Maze;
import ch.fritteli.maze.generator.model.Position;
import ch.fritteli.maze.generator.model.Tile;
import io.vavr.control.Option;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Random;
import lombok.NonNull;
public class RandomDepthFirst {
@NonNull
private final Maze maze;
@NonNull
private final Random random;
@NonNull
private final Deque<Position> positions = new LinkedList<>();
public RandomDepthFirst(@NonNull final Maze maze) {
this.maze = maze;
this.random = new Random(maze.getRandomSeed());
}
public void run() {
this.preDig();
this.dig();
this.postDig();
}
private void preDig() {
final Position end = this.maze.getEnd();
final Tile endTile = this.maze.getEndTile();
if (end.getY() == 0) {
endTile.enableDiggingToOrFrom(Direction.TOP);
endTile.digFrom(Direction.TOP);
} else if (end.getX() == 0) {
endTile.enableDiggingToOrFrom(Direction.LEFT);
endTile.digFrom(Direction.LEFT);
} else if (end.getY() == this.maze.getHeight() - 1) {
endTile.enableDiggingToOrFrom(Direction.BOTTOM);
endTile.digFrom(Direction.BOTTOM);
} else if (end.getX() == this.maze.getWidth() - 1) {
endTile.enableDiggingToOrFrom(Direction.RIGHT);
endTile.digFrom(Direction.RIGHT);
}
this.positions.push(end);
}
private void dig() {
while (!this.positions.isEmpty()) {
final Position currentPosition = this.positions.peek();
final Tile currentTile = this.maze.getTileAt(currentPosition).get();
final Option<Direction> directionToDigTo = currentTile.getRandomAvailableDirection(this.random);
if (directionToDigTo.isDefined()) {
final Direction digTo = directionToDigTo.get();
final Direction digFrom = digTo.invert();
final Position neighborPosition = currentPosition.move(digTo);
final Tile neighborTile = this.maze.getTileAt(neighborPosition).get();
if (currentTile.digTo(digTo) && neighborTile.digFrom(digFrom)) {
// all ok!
this.positions.push(neighborPosition);
if (neighborPosition.equals(this.maze.getStart())) {
this.markSolution();
}
} else {
// Hm, didn't work.
currentTile.undigTo(digTo);
currentTile.preventDiggingToOrFrom(digTo);
}
} else {
this.positions.pop();
}
}
}
private void markSolution() {
this.positions.forEach(position -> this.maze.getTileAt(position).get().setSolution());
}
private void postDig() {
final Position start = this.maze.getStart();
final Tile startTile = this.maze.getStartTile();
if (start.getY() == 0) {
startTile.enableDiggingToOrFrom(Direction.TOP);
startTile.digTo(Direction.TOP);
} else if (start.getX() == 0) {
startTile.enableDiggingToOrFrom(Direction.LEFT);
startTile.digTo(Direction.LEFT);
} else if (start.getY() == this.maze.getHeight() - 1) {
startTile.enableDiggingToOrFrom(Direction.BOTTOM);
startTile.digTo(Direction.BOTTOM);
} else if (start.getX() == this.maze.getWidth() - 1) {
startTile.enableDiggingToOrFrom(Direction.RIGHT);
startTile.digTo(Direction.RIGHT);
}
}
}