208 lines
7.4 KiB
Java
208 lines
7.4 KiB
Java
package ch.fritteli.maze.generator.renderer.text;
|
|
|
|
import ch.fritteli.maze.generator.model.Direction;
|
|
import ch.fritteli.maze.generator.model.Maze;
|
|
import ch.fritteli.maze.generator.model.Tile;
|
|
import io.vavr.control.Option;
|
|
import lombok.AccessLevel;
|
|
import lombok.NonNull;
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
|
class Generator {
|
|
@NonNull
|
|
private final Maze maze;
|
|
private final boolean renderSolution;
|
|
private int x = 0;
|
|
private int y = 0;
|
|
// Each row has three lines:
|
|
// - The top border
|
|
// - The central pathway
|
|
// - The bottom border
|
|
// The bottom border of one row is identical to the top border of the following row. We use this variable here in
|
|
// order to be able to render the bottom border of the last row, which obviously does not have a following row with
|
|
// a top border.
|
|
private int line = 0;
|
|
|
|
boolean hasNext() {
|
|
return this.y < this.maze.getHeight();
|
|
}
|
|
|
|
String next() {
|
|
final Tile currentTile = this.maze.getTileAt(this.x, this.y).get();
|
|
final Option<Tile> topTile = this.maze.getTileAt(this.x, this.y - 1);
|
|
final Option<Tile> rightTile = this.maze.getTileAt(this.x + 1, this.y);
|
|
final Option<Tile> bottomTile = this.maze.getTileAt(this.x, this.y + 1);
|
|
final Option<Tile> leftTile = this.maze.getTileAt(this.x - 1, this.y);
|
|
final String s;
|
|
switch (this.line) {
|
|
case 0:
|
|
s = this.renderTopLine(currentTile, leftTile, topTile);
|
|
break;
|
|
case 1:
|
|
s = this.renderCenterLine(currentTile, topTile, rightTile, bottomTile, leftTile);
|
|
break;
|
|
case 2:
|
|
s = this.renderBottomLine(currentTile, leftTile);
|
|
break;
|
|
default:
|
|
s = "";
|
|
break;
|
|
}
|
|
this.prepareNextStep();
|
|
return s;
|
|
}
|
|
|
|
private void prepareNextStep() {
|
|
// do some magic ...
|
|
this.x++;
|
|
if (this.x == this.maze.getWidth()) {
|
|
// Reached the end of the row?
|
|
// On to the next line then!
|
|
this.x = 0;
|
|
this.line++;
|
|
}
|
|
if (this.line == 2 && this.y < this.maze.getHeight() - 1) {
|
|
// Finished rendering the center line, and more rows available?
|
|
// On to the next row then!
|
|
this.line = 0;
|
|
this.y++;
|
|
}
|
|
if (this.line == 3) {
|
|
// Finished rendering the bottom line (of the last row)?
|
|
// Increment row counter one more time => this is the exit condition.
|
|
this.line = 0;
|
|
this.y++;
|
|
}
|
|
}
|
|
|
|
private String renderTopLine(@NonNull final Tile currentTile, @NonNull final Option<Tile> leftTile, @NonNull final Option<Tile> topTile) {
|
|
final CharDefinition charDef1 = new CharDefinition();
|
|
final CharDefinition charDef2 = new CharDefinition();
|
|
final CharDefinition charDef3 = new CharDefinition();
|
|
String result;
|
|
if (currentTile.hasWallAt(Direction.LEFT)) {
|
|
charDef1.down();
|
|
}
|
|
if (currentTile.hasWallAt(Direction.TOP)) {
|
|
charDef1.right();
|
|
charDef2.horizontal();
|
|
charDef3.left();
|
|
} else {
|
|
if (this.isSolution(currentTile) && (this.isSolution(topTile) || topTile.isEmpty())) {
|
|
charDef2.solution().vertical();
|
|
}
|
|
}
|
|
if (currentTile.hasWallAt(Direction.RIGHT)) {
|
|
charDef3.down();
|
|
}
|
|
if (this.hasWallAt(leftTile, Direction.TOP)) {
|
|
charDef1.left();
|
|
}
|
|
if (this.hasWallAt(topTile, Direction.LEFT)) {
|
|
charDef1.up();
|
|
}
|
|
if (this.hasWallAt(topTile, Direction.RIGHT)) {
|
|
charDef3.up();
|
|
}
|
|
result = charDef1.toString() + charDef2;
|
|
if (this.x == this.maze.getWidth() - 1) {
|
|
result += charDef3 + "\n";
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private String renderCenterLine(@NonNull final Tile currentTile,
|
|
@NonNull final Option<Tile> topTile,
|
|
@NonNull final Option<Tile> rightTile,
|
|
@NonNull final Option<Tile> bottomTile,
|
|
@NonNull final Option<Tile> leftTile) {
|
|
final CharDefinition charDef1 = new CharDefinition();
|
|
final CharDefinition charDef2 = new CharDefinition();
|
|
final CharDefinition charDef3 = new CharDefinition();
|
|
String result;
|
|
if (currentTile.hasWallAt(Direction.LEFT)) {
|
|
charDef1.vertical();
|
|
} else {
|
|
if (this.isSolution(currentTile) && this.isSolution(leftTile)) {
|
|
charDef1.solution().horizontal();
|
|
}
|
|
}
|
|
if (this.isSolution(currentTile)) {
|
|
charDef2.solution();
|
|
if (!currentTile.hasWallAt(Direction.LEFT) && (this.isSolution(leftTile) || leftTile.isEmpty())) {
|
|
charDef2.left();
|
|
}
|
|
if (!currentTile.hasWallAt(Direction.TOP) && (this.isSolution(topTile) || topTile.isEmpty())) {
|
|
charDef2.up();
|
|
}
|
|
if (!currentTile.hasWallAt(Direction.RIGHT) && (this.isSolution(rightTile) || rightTile.isEmpty())) {
|
|
charDef2.right();
|
|
}
|
|
if (!currentTile.hasWallAt(Direction.BOTTOM) && (this.isSolution(bottomTile) || bottomTile.isEmpty())) {
|
|
charDef2.down();
|
|
}
|
|
}
|
|
if (currentTile.hasWallAt(Direction.RIGHT)) {
|
|
charDef3.vertical();
|
|
} else {
|
|
if (this.isSolution(currentTile) && this.isSolution(rightTile)) {
|
|
charDef3.solution().horizontal();
|
|
}
|
|
}
|
|
|
|
result = charDef1.toString() + charDef2;
|
|
|
|
if (this.x == this.maze.getWidth() - 1) {
|
|
result += charDef3 + "\n";
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private String renderBottomLine(@NonNull final Tile currentTile, @NonNull final Option<Tile> leftTile) {
|
|
String result;
|
|
final CharDefinition charDef1 = new CharDefinition();
|
|
final CharDefinition charDef2 = new CharDefinition();
|
|
final CharDefinition charDef3 = new CharDefinition();
|
|
|
|
if (currentTile.hasWallAt(Direction.LEFT)) {
|
|
charDef1.up();
|
|
}
|
|
if (currentTile.hasWallAt(Direction.BOTTOM)) {
|
|
charDef1.right();
|
|
charDef2.horizontal();
|
|
charDef3.left();
|
|
} else {
|
|
if (this.isSolution(currentTile)) {
|
|
charDef2.solution().vertical();
|
|
}
|
|
}
|
|
if (currentTile.hasWallAt(Direction.RIGHT)) {
|
|
charDef3.up();
|
|
}
|
|
if (this.hasWallAt(leftTile, Direction.BOTTOM)) {
|
|
charDef1.left();
|
|
}
|
|
|
|
result = charDef1.toString() + charDef2;
|
|
|
|
if (this.x == this.maze.getWidth() - 1) {
|
|
result += charDef3;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private boolean hasWallAt(@NonNull final Option<Tile> tile, @NonNull final Direction direction) {
|
|
return tile.map(t -> t.hasWallAt(direction)).getOrElse(false);
|
|
}
|
|
|
|
private boolean isSolution(@NonNull final Tile tile) {
|
|
return this.renderSolution && tile != null && tile.isSolution();
|
|
}
|
|
|
|
private boolean isSolution(@NonNull final Option<Tile> tile) {
|
|
return this.renderSolution && tile.map(Tile::isSolution).getOrElse(false);
|
|
}
|
|
}
|