104 lines
3.7 KiB
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);
|
|
}
|
|
}
|
|
}
|