Compare commits
13 commits
Author | SHA1 | Date | |
---|---|---|---|
b18e7fba9e | |||
49725fe7df | |||
4db011fd92 | |||
7eb8d09ae8 | |||
1e190150ef | |||
724f0f1a53 | |||
65084659c2 | |||
79f5620aaa | |||
684ecdbe53 | |||
2ab7f9dc5f | |||
e3bc8d8452 | |||
4dd91b0277 | |||
aa27d5f138 |
36 changed files with 300 additions and 322 deletions
|
@ -3,7 +3,7 @@ type: docker
|
||||||
name: default
|
name: default
|
||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: build
|
||||||
image: maven:3.8-openjdk-18-slim
|
image: maven:3.9-eclipse-temurin-21
|
||||||
commands:
|
commands:
|
||||||
- mvn clean install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
|
- mvn clean install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
|
||||||
when:
|
when:
|
||||||
|
@ -13,7 +13,7 @@ steps:
|
||||||
- refs/head/feature/**
|
- refs/head/feature/**
|
||||||
- refs/tags/**
|
- refs/tags/**
|
||||||
- name: test
|
- name: test
|
||||||
image: maven:3.8-openjdk-18-slim
|
image: maven:3.9-eclipse-temurin-21
|
||||||
commands:
|
commands:
|
||||||
- mvn test -B
|
- mvn test -B
|
||||||
when:
|
when:
|
||||||
|
@ -22,7 +22,7 @@ steps:
|
||||||
- master
|
- master
|
||||||
- feature/*
|
- feature/*
|
||||||
- name: deploy
|
- name: deploy
|
||||||
image: maven:3.8-openjdk-18-slim
|
image: maven:3.9-eclipse-temurin-21
|
||||||
environment:
|
environment:
|
||||||
REPO_TOKEN:
|
REPO_TOKEN:
|
||||||
from_secret: repo-token
|
from_secret: repo-token
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
</server>
|
</server>
|
||||||
<server>
|
<server>
|
||||||
<id>ossrh</id>
|
<id>ossrh</id>
|
||||||
<username>fritteli</username>
|
<username>4etdRvZF</username>
|
||||||
<password>${env.REPO_TOKEN_OSSRH}</password>
|
<password>${env.REPO_TOKEN_OSSRH}</password>
|
||||||
</server>
|
</server>
|
||||||
</servers>
|
</servers>
|
||||||
|
|
14
pom.xml
14
pom.xml
|
@ -5,12 +5,12 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ch.fritteli</groupId>
|
<groupId>ch.fritteli</groupId>
|
||||||
<artifactId>fritteli-build-parent</artifactId>
|
<artifactId>fritteli-build-parent</artifactId>
|
||||||
<version>5.0.0</version>
|
<version>5.1.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>ch.fritteli.a-maze-r</groupId>
|
<groupId>ch.fritteli.a-maze-r</groupId>
|
||||||
<artifactId>maze-generator</artifactId>
|
<artifactId>maze-generator</artifactId>
|
||||||
<version>0.1.0</version>
|
<version>0.2.2-SNAPSHOT</version>
|
||||||
|
|
||||||
<description>A-Maze-R, The Maze Generator. It is a library for generating mazes in various output formats.
|
<description>A-Maze-R, The Maze Generator. It is a library for generating mazes in various output formats.
|
||||||
</description>
|
</description>
|
||||||
|
@ -43,10 +43,9 @@
|
||||||
<connection>scm:git:https://gittr.ch/java/maze-generator.git</connection>
|
<connection>scm:git:https://gittr.ch/java/maze-generator.git</connection>
|
||||||
<developerConnection>scm:git:ssh://git@gittr.ch/java/maze-generator.git</developerConnection>
|
<developerConnection>scm:git:ssh://git@gittr.ch/java/maze-generator.git</developerConnection>
|
||||||
<url>https://gittr.ch/java/maze-generator</url>
|
<url>https://gittr.ch/java/maze-generator</url>
|
||||||
<tag>v0.1.0</tag>
|
<tag>HEAD</tag>
|
||||||
</scm>
|
</scm>
|
||||||
|
|
||||||
|
|
||||||
<distributionManagement>
|
<distributionManagement>
|
||||||
<snapshotRepository>
|
<snapshotRepository>
|
||||||
<id>ossrh</id>
|
<id>ossrh</id>
|
||||||
|
@ -56,10 +55,10 @@
|
||||||
</distributionManagement>
|
</distributionManagement>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<jackson.version>2.15.0</jackson.version>
|
<jackson.version>2.17.1</jackson.version>
|
||||||
<jsonschema2pojo-maven-plugin.version>1.2.1</jsonschema2pojo-maven-plugin.version>
|
<jsonschema2pojo-maven-plugin.version>1.2.1</jsonschema2pojo-maven-plugin.version>
|
||||||
<maven-site-plugin.version>4.0.0-M8</maven-site-plugin.version>
|
<maven-site-plugin.version>4.0.0-M8</maven-site-plugin.version>
|
||||||
<pdfbox.version>2.0.28</pdfbox.version>
|
<pdfbox.version>3.0.2</pdfbox.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -93,12 +92,10 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
<version>${slf4j.version}</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ch.qos.logback</groupId>
|
<groupId>ch.qos.logback</groupId>
|
||||||
<artifactId>logback-classic</artifactId>
|
<artifactId>logback-classic</artifactId>
|
||||||
<version>${logback.version}</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
@ -107,7 +104,6 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.assertj</groupId>
|
<groupId>org.assertj</groupId>
|
||||||
<artifactId>assertj-core</artifactId>
|
<artifactId>assertj-core</artifactId>
|
||||||
<version>3.24.2</version>
|
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
|
@ -9,17 +9,18 @@ import ch.fritteli.maze.generator.renderer.jsonfile.JsonFileRenderer;
|
||||||
import ch.fritteli.maze.generator.renderer.pdffile.PDFFileRenderer;
|
import ch.fritteli.maze.generator.renderer.pdffile.PDFFileRenderer;
|
||||||
import ch.fritteli.maze.generator.renderer.text.TextRenderer;
|
import ch.fritteli.maze.generator.renderer.text.TextRenderer;
|
||||||
import ch.fritteli.maze.generator.renderer.textfile.TextFileRenderer;
|
import ch.fritteli.maze.generator.renderer.textfile.TextFileRenderer;
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
public static void main(@NonNull final String[] args) {
|
public static void main(@NotNull final String[] args) {
|
||||||
final int width = 20;
|
final int width = 20;
|
||||||
final int height = 30;
|
final int height = 30;
|
||||||
final Maze maze = new Maze(width, height/*, 0*/);
|
final Maze maze = new Maze(width, height/*, 0*/);
|
||||||
|
@ -58,7 +59,7 @@ public class Main {
|
||||||
log.info("PDF rendering to file:\n{}", pdfFileRenderer.render(maze));
|
log.info("PDF rendering to file:\n{}", pdfFileRenderer.render(maze));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getBaseFilename(@NonNull final Maze maze) {
|
private static String getBaseFilename(@NotNull final Maze maze) {
|
||||||
return "maze-" + maze.getWidth() + "x" + maze.getHeight() + "-" + maze.getRandomSeed();
|
return "maze-" + maze.getWidth() + "x" + maze.getHeight() + "-" + maze.getRandomSeed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,21 +5,22 @@ import ch.fritteli.maze.generator.model.Maze;
|
||||||
import ch.fritteli.maze.generator.model.Position;
|
import ch.fritteli.maze.generator.model.Position;
|
||||||
import ch.fritteli.maze.generator.model.Tile;
|
import ch.fritteli.maze.generator.model.Tile;
|
||||||
import io.vavr.control.Option;
|
import io.vavr.control.Option;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import lombok.NonNull;
|
|
||||||
|
|
||||||
public class RandomDepthFirst {
|
public class RandomDepthFirst {
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private final Maze maze;
|
private final Maze maze;
|
||||||
@NonNull
|
@NotNull
|
||||||
private final Random random;
|
private final Random random;
|
||||||
@NonNull
|
@NotNull
|
||||||
private final Deque<Position> positions = new LinkedList<>();
|
private final Deque<Position> positions = new LinkedList<>();
|
||||||
|
|
||||||
public RandomDepthFirst(@NonNull final Maze maze) {
|
public RandomDepthFirst(@NotNull final Maze maze) {
|
||||||
this.maze = maze;
|
this.maze = maze;
|
||||||
this.random = new Random(maze.getRandomSeed());
|
this.random = new Random(maze.getRandomSeed());
|
||||||
}
|
}
|
||||||
|
@ -34,16 +35,16 @@ public class RandomDepthFirst {
|
||||||
final Position end = this.maze.getEnd();
|
final Position end = this.maze.getEnd();
|
||||||
final Tile endTile = this.maze.getEndTile();
|
final Tile endTile = this.maze.getEndTile();
|
||||||
|
|
||||||
if (end.getY() == 0) {
|
if (end.y() == 0) {
|
||||||
endTile.enableDiggingToOrFrom(Direction.TOP);
|
endTile.enableDiggingToOrFrom(Direction.TOP);
|
||||||
endTile.digFrom(Direction.TOP);
|
endTile.digFrom(Direction.TOP);
|
||||||
} else if (end.getX() == 0) {
|
} else if (end.x() == 0) {
|
||||||
endTile.enableDiggingToOrFrom(Direction.LEFT);
|
endTile.enableDiggingToOrFrom(Direction.LEFT);
|
||||||
endTile.digFrom(Direction.LEFT);
|
endTile.digFrom(Direction.LEFT);
|
||||||
} else if (end.getY() == this.maze.getHeight() - 1) {
|
} else if (end.y() == this.maze.getHeight() - 1) {
|
||||||
endTile.enableDiggingToOrFrom(Direction.BOTTOM);
|
endTile.enableDiggingToOrFrom(Direction.BOTTOM);
|
||||||
endTile.digFrom(Direction.BOTTOM);
|
endTile.digFrom(Direction.BOTTOM);
|
||||||
} else if (end.getX() == this.maze.getWidth() - 1) {
|
} else if (end.x() == this.maze.getWidth() - 1) {
|
||||||
endTile.enableDiggingToOrFrom(Direction.RIGHT);
|
endTile.enableDiggingToOrFrom(Direction.RIGHT);
|
||||||
endTile.digFrom(Direction.RIGHT);
|
endTile.digFrom(Direction.RIGHT);
|
||||||
}
|
}
|
||||||
|
@ -86,16 +87,16 @@ public class RandomDepthFirst {
|
||||||
final Position start = this.maze.getStart();
|
final Position start = this.maze.getStart();
|
||||||
final Tile startTile = this.maze.getStartTile();
|
final Tile startTile = this.maze.getStartTile();
|
||||||
|
|
||||||
if (start.getY() == 0) {
|
if (start.y() == 0) {
|
||||||
startTile.enableDiggingToOrFrom(Direction.TOP);
|
startTile.enableDiggingToOrFrom(Direction.TOP);
|
||||||
startTile.digTo(Direction.TOP);
|
startTile.digTo(Direction.TOP);
|
||||||
} else if (start.getX() == 0) {
|
} else if (start.x() == 0) {
|
||||||
startTile.enableDiggingToOrFrom(Direction.LEFT);
|
startTile.enableDiggingToOrFrom(Direction.LEFT);
|
||||||
startTile.digTo(Direction.LEFT);
|
startTile.digTo(Direction.LEFT);
|
||||||
} else if (start.getY() == this.maze.getHeight() - 1) {
|
} else if (start.y() == this.maze.getHeight() - 1) {
|
||||||
startTile.enableDiggingToOrFrom(Direction.BOTTOM);
|
startTile.enableDiggingToOrFrom(Direction.BOTTOM);
|
||||||
startTile.digTo(Direction.BOTTOM);
|
startTile.digTo(Direction.BOTTOM);
|
||||||
} else if (start.getX() == this.maze.getWidth() - 1) {
|
} else if (start.x() == this.maze.getWidth() - 1) {
|
||||||
startTile.enableDiggingToOrFrom(Direction.RIGHT);
|
startTile.enableDiggingToOrFrom(Direction.RIGHT);
|
||||||
startTile.digTo(Direction.RIGHT);
|
startTile.digTo(Direction.RIGHT);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,17 +7,11 @@ public enum Direction {
|
||||||
LEFT;
|
LEFT;
|
||||||
|
|
||||||
public Direction invert() {
|
public Direction invert() {
|
||||||
switch (this) {
|
return switch (this) {
|
||||||
case TOP:
|
case TOP -> BOTTOM;
|
||||||
return BOTTOM;
|
case RIGHT -> LEFT;
|
||||||
case RIGHT:
|
case BOTTOM -> TOP;
|
||||||
return LEFT;
|
case LEFT -> RIGHT;
|
||||||
case BOTTOM:
|
};
|
||||||
return TOP;
|
|
||||||
case LEFT:
|
|
||||||
return RIGHT;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Programming error: Not all enum values covered in enum Direction#invert()!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@ package ch.fritteli.maze.generator.model;
|
||||||
import io.vavr.control.Option;
|
import io.vavr.control.Option;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
@ToString
|
@ToString
|
||||||
|
@ -26,7 +26,7 @@ public class Maze {
|
||||||
this(width, height, System.nanoTime());
|
this(width, height, System.nanoTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Maze(final int width, final int height, @NonNull final Position start, @NonNull final Position end) {
|
public Maze(final int width, final int height, @NotNull final Position start, @NotNull final Position end) {
|
||||||
this(width, height, System.nanoTime(), start, end);
|
this(width, height, System.nanoTime(), start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,17 +34,17 @@ public class Maze {
|
||||||
this(width, height, randomSeed, new Position(0, 0), new Position(width - 1, height - 1));
|
this(width, height, randomSeed, new Position(0, 0), new Position(width - 1, height - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Maze(final int width, final int height, final long randomSeed, @NonNull final Position start, @NonNull final Position end) {
|
public Maze(final int width, final int height, final long randomSeed, @NotNull final Position start, @NotNull final Position end) {
|
||||||
if (width <= 1 || height <= 1) {
|
if (width <= 1 || height <= 1) {
|
||||||
throw new IllegalArgumentException("width and height must be >1");
|
throw new IllegalArgumentException("width and height must be >1");
|
||||||
}
|
}
|
||||||
if (start.equals(end)) {
|
if (start.equals(end)) {
|
||||||
throw new IllegalArgumentException("'start' must not be equal to 'end'");
|
throw new IllegalArgumentException("'start' must not be equal to 'end'");
|
||||||
}
|
}
|
||||||
if (start.getX() != 0 && start.getX() != width - 1 && start.getY() != 0 && start.getY() != height - 1) {
|
if (start.x() != 0 && start.x() != width - 1 && start.y() != 0 && start.y() != height - 1) {
|
||||||
throw new IllegalArgumentException("'start' must be at the edge of the maze");
|
throw new IllegalArgumentException("'start' must be at the edge of the maze");
|
||||||
}
|
}
|
||||||
if (end.getX() != 0 && end.getX() != width - 1 && end.getY() != 0 && end.getY() != height - 1) {
|
if (end.x() != 0 && end.x() != width - 1 && end.y() != 0 && end.y() != height - 1) {
|
||||||
throw new IllegalArgumentException("'end' must be at the edge of the maze");
|
throw new IllegalArgumentException("'end' must be at the edge of the maze");
|
||||||
}
|
}
|
||||||
this.width = width;
|
this.width = width;
|
||||||
|
@ -59,7 +59,7 @@ public class Maze {
|
||||||
/**
|
/**
|
||||||
* INTERNAL API. Exists only for deserialization. Not to be called from user code.
|
* INTERNAL API. Exists only for deserialization. Not to be called from user code.
|
||||||
*/
|
*/
|
||||||
private Maze(@NonNull final Tile[][] field, final int width, final int height, final long randomSeed) {
|
private Maze(@NotNull final Tile[][] field, final int width, final int height, final long randomSeed) {
|
||||||
this.field = field;
|
this.field = field;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
|
@ -71,7 +71,7 @@ public class Maze {
|
||||||
/**
|
/**
|
||||||
* INTERNAL API. Exists only for deserialization. Not to be called from user code.
|
* INTERNAL API. Exists only for deserialization. Not to be called from user code.
|
||||||
*/
|
*/
|
||||||
private Maze(@NonNull final Tile[][] field, final int width, final int height, @NonNull final Position start, @NonNull final Position end, final long randomSeed) {
|
private Maze(@NotNull final Tile[][] field, final int width, final int height, @NotNull final Position start, @NotNull final Position end, final long randomSeed) {
|
||||||
this.field = field;
|
this.field = field;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
|
@ -80,12 +80,12 @@ public class Maze {
|
||||||
this.end = end;
|
this.end = end;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public Option<Tile> getTileAt(@NonNull final Position position) {
|
public Option<Tile> getTileAt(@NotNull final Position position) {
|
||||||
return this.getTileAt(position.getX(), position.getY());
|
return this.getTileAt(position.x(), position.y());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public Option<Tile> getTileAt(final int x, final int y) {
|
public Option<Tile> getTileAt(final int x, final int y) {
|
||||||
if (x < 0 || y < 0 || x >= this.width || y >= this.height) {
|
if (x < 0 || y < 0 || x >= this.width || y >= this.height) {
|
||||||
return Option.none();
|
return Option.none();
|
||||||
|
@ -93,12 +93,12 @@ public class Maze {
|
||||||
return Option.of(this.field[x][y]);
|
return Option.of(this.field[x][y]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public Tile getStartTile() {
|
public Tile getStartTile() {
|
||||||
return this.getTileAt(this.start).get();
|
return this.getTileAt(this.start).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public Tile getEndTile() {
|
public Tile getEndTile() {
|
||||||
return this.getTileAt(this.end).get();
|
return this.getTileAt(this.end).get();
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ public class Maze {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void hardenWalls(@NonNull final Tile tile, final int x, final int y) {
|
private void hardenWalls(@NotNull final Tile tile, final int x, final int y) {
|
||||||
if (x == 0) {
|
if (x == 0) {
|
||||||
tile.preventDiggingToOrFrom(Direction.LEFT);
|
tile.preventDiggingToOrFrom(Direction.LEFT);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,16 @@
|
||||||
package ch.fritteli.maze.generator.model;
|
package ch.fritteli.maze.generator.model;
|
||||||
|
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.Value;
|
|
||||||
import lombok.With;
|
import lombok.With;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@Value
|
|
||||||
@With
|
@With
|
||||||
public class Position {
|
public record Position(int x, int y) {
|
||||||
int x;
|
public Position move(@NotNull final Direction direction) {
|
||||||
int y;
|
return switch (direction) {
|
||||||
|
case BOTTOM -> this.withY(this.y + 1);
|
||||||
public Position move(@NonNull final Direction direction) {
|
case LEFT -> this.withX(this.x - 1);
|
||||||
switch (direction) {
|
case RIGHT -> this.withX(this.x + 1);
|
||||||
case BOTTOM:
|
case TOP -> this.withY(this.y - 1);
|
||||||
return this.withY(this.y + 1);
|
};
|
||||||
case LEFT:
|
|
||||||
return this.withX(this.x - 1);
|
|
||||||
case RIGHT:
|
|
||||||
return this.withX(this.x + 1);
|
|
||||||
case TOP:
|
|
||||||
return this.withY(this.y - 1);
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Programming error: Not all Direction enum values covered in Position#move(Direction)!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,9 @@ import io.vavr.control.Option;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import lombok.experimental.FieldDefaults;
|
import lombok.experimental.FieldDefaults;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
@ -32,24 +32,24 @@ public class Tile {
|
||||||
* @param walls
|
* @param walls
|
||||||
* @param solution
|
* @param solution
|
||||||
*/
|
*/
|
||||||
private Tile(@NonNull final EnumSet<Direction> walls, final boolean solution) {
|
private Tile(@NotNull final EnumSet<Direction> walls, final boolean solution) {
|
||||||
for (final Direction direction : walls) {
|
for (final Direction direction : walls) {
|
||||||
this.walls.set(direction);
|
this.walls.set(direction);
|
||||||
this.walls.harden(direction);
|
this.walls.seal(direction);
|
||||||
}
|
}
|
||||||
this.visited = true;
|
this.visited = true;
|
||||||
this.solution = solution;
|
this.solution = solution;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void preventDiggingToOrFrom(@NonNull final Direction direction) {
|
public void preventDiggingToOrFrom(@NotNull final Direction direction) {
|
||||||
this.walls.harden(direction);
|
this.walls.seal(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enableDiggingToOrFrom(@NonNull final Direction direction) {
|
public void enableDiggingToOrFrom(@NotNull final Direction direction) {
|
||||||
this.walls.unharden(direction);
|
this.walls.unseal(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean digFrom(@NonNull final Direction direction) {
|
public boolean digFrom(@NotNull final Direction direction) {
|
||||||
if (this.visited) {
|
if (this.visited) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -57,16 +57,16 @@ public class Tile {
|
||||||
return this.walls.clear(direction);
|
return this.walls.clear(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean digTo(@NonNull final Direction direction) {
|
public boolean digTo(@NotNull final Direction direction) {
|
||||||
return this.walls.clear(direction);
|
return this.walls.clear(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void undigTo(@NonNull final Direction direction) {
|
public void undigTo(@NotNull final Direction direction) {
|
||||||
this.walls.set(direction);
|
this.walls.set(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Option<Direction> getRandomAvailableDirection(@NonNull final Random random) {
|
public Option<Direction> getRandomAvailableDirection(@NotNull final Random random) {
|
||||||
final Stream<Direction> availableDirections = this.walls.getUnhardenedSet();
|
final Stream<Direction> availableDirections = this.walls.getUnsealedSet();
|
||||||
if (availableDirections.isEmpty()) {
|
if (availableDirections.isEmpty()) {
|
||||||
return Option.none();
|
return Option.none();
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ public class Tile {
|
||||||
return Option.of(availableDirections.get(index));
|
return Option.of(availableDirections.get(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasWallAt(@NonNull final Direction direction) {
|
public boolean hasWallAt(@NotNull final Direction direction) {
|
||||||
return this.walls.isSet(direction);
|
return this.walls.isSet(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ package ch.fritteli.maze.generator.model;
|
||||||
|
|
||||||
import io.vavr.collection.Stream;
|
import io.vavr.collection.Stream;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -15,9 +15,9 @@ import java.util.TreeSet;
|
||||||
@ToString
|
@ToString
|
||||||
public class Walls {
|
public class Walls {
|
||||||
private final SortedSet<Direction> directions = new TreeSet<>();
|
private final SortedSet<Direction> directions = new TreeSet<>();
|
||||||
private final Set<Direction> hardened = new HashSet<>();
|
private final Set<Direction> sealed = new HashSet<>();
|
||||||
|
|
||||||
public void set(@NonNull final Direction direction) {
|
public void set(@NotNull final Direction direction) {
|
||||||
this.directions.add(direction);
|
this.directions.add(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,30 +25,30 @@ public class Walls {
|
||||||
this.directions.addAll(EnumSet.allOf(Direction.class));
|
this.directions.addAll(EnumSet.allOf(Direction.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean clear(@NonNull final Direction direction) {
|
public boolean clear(@NotNull final Direction direction) {
|
||||||
if (this.hardened.contains(direction)) {
|
if (this.sealed.contains(direction)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return this.directions.remove(direction);
|
return this.directions.remove(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSet(@NonNull final Direction direction) {
|
public boolean isSet(@NotNull final Direction direction) {
|
||||||
return this.directions.contains(direction);
|
return this.directions.contains(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream<Direction> getUnhardenedSet() {
|
public Stream<Direction> getUnsealedSet() {
|
||||||
return Stream.ofAll(this.directions)
|
return Stream.ofAll(this.directions)
|
||||||
.removeAll(this.hardened);
|
.removeAll(this.sealed);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void harden(@NonNull final Direction direction) {
|
public void seal(@NotNull final Direction direction) {
|
||||||
if (!this.directions.contains(direction)) {
|
if (!this.directions.contains(direction)) {
|
||||||
throw new IllegalStateException("Trying to harden cleared Direction: " + direction);
|
throw new IllegalStateException("Trying to seal cleared Direction: " + direction);
|
||||||
}
|
}
|
||||||
this.hardened.add(direction);
|
this.sealed.add(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unharden(@NonNull final Direction direction) {
|
public void unseal(@NotNull final Direction direction) {
|
||||||
this.hardened.remove(direction);
|
this.sealed.remove(direction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package ch.fritteli.maze.generator.renderer;
|
package ch.fritteli.maze.generator.renderer;
|
||||||
|
|
||||||
import ch.fritteli.maze.generator.model.Maze;
|
import ch.fritteli.maze.generator.model.Maze;
|
||||||
import lombok.NonNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public interface Renderer<T> {
|
public interface Renderer<T> {
|
||||||
@NonNull
|
@NotNull
|
||||||
T render(@NonNull final Maze maze);
|
T render(@NotNull final Maze maze);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ import ch.fritteli.maze.generator.model.Tile;
|
||||||
import io.vavr.collection.HashSet;
|
import io.vavr.collection.HashSet;
|
||||||
import io.vavr.collection.Set;
|
import io.vavr.collection.Set;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||||
class Generator {
|
class Generator {
|
||||||
|
@ -31,7 +31,7 @@ class Generator {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> getClasses(@NonNull final Tile tile) {
|
private Set<String> getClasses(@NotNull final Tile tile) {
|
||||||
Set<String> result = HashSet.empty();
|
Set<String> result = HashSet.empty();
|
||||||
if (tile.hasWallAt(Direction.TOP)) {
|
if (tile.hasWallAt(Direction.TOP)) {
|
||||||
result = result.add("top");
|
result = result.add("top");
|
||||||
|
|
|
@ -2,7 +2,7 @@ package ch.fritteli.maze.generator.renderer.html;
|
||||||
|
|
||||||
import ch.fritteli.maze.generator.model.Maze;
|
import ch.fritteli.maze.generator.model.Maze;
|
||||||
import ch.fritteli.maze.generator.renderer.Renderer;
|
import ch.fritteli.maze.generator.renderer.Renderer;
|
||||||
import lombok.NonNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class HTMLRenderer implements Renderer<String> {
|
public class HTMLRenderer implements Renderer<String> {
|
||||||
|
|
||||||
|
@ -112,14 +112,14 @@ public class HTMLRenderer implements Renderer<String> {
|
||||||
private HTMLRenderer() {
|
private HTMLRenderer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public static HTMLRenderer newInstance() {
|
public static HTMLRenderer newInstance() {
|
||||||
return new HTMLRenderer();
|
return new HTMLRenderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public String render(@NonNull final Maze maze) {
|
public String render(@NotNull final Maze maze) {
|
||||||
if (maze.getWidth() == 0 || maze.getHeight() == 0) {
|
if (maze.getWidth() == 0 || maze.getHeight() == 0) {
|
||||||
return this.getPreamble(maze) + POSTAMBLE;
|
return this.getPreamble(maze) + POSTAMBLE;
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,7 @@ public class HTMLRenderer implements Renderer<String> {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPreamble(@NonNull final Maze maze) {
|
private String getPreamble(@NotNull final Maze maze) {
|
||||||
return "<!DOCTYPE html><html lang=\"en\">" +
|
return "<!DOCTYPE html><html lang=\"en\">" +
|
||||||
"<head>" +
|
"<head>" +
|
||||||
"<title>Maze " + maze.getWidth() + "x" + maze.getHeight() + ", ID " + maze.getRandomSeed() + "</title>" +
|
"<title>Maze " + maze.getWidth() + "x" + maze.getHeight() + ", ID " + maze.getRandomSeed() + "</title>" +
|
||||||
|
|
|
@ -5,8 +5,8 @@ import ch.fritteli.maze.generator.renderer.Renderer;
|
||||||
import ch.fritteli.maze.generator.renderer.html.HTMLRenderer;
|
import ch.fritteli.maze.generator.renderer.html.HTMLRenderer;
|
||||||
import io.vavr.control.Option;
|
import io.vavr.control.Option;
|
||||||
import io.vavr.control.Try;
|
import io.vavr.control.Try;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -17,9 +17,9 @@ import java.util.NoSuchElementException;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class HTMLFileRenderer implements Renderer<Path> {
|
public class HTMLFileRenderer implements Renderer<Path> {
|
||||||
@NonNull
|
@NotNull
|
||||||
private static final HTMLRenderer HTML_RENDERER = HTMLRenderer.newInstance();
|
private static final HTMLRenderer HTML_RENDERER = HTMLRenderer.newInstance();
|
||||||
@NonNull
|
@NotNull
|
||||||
private Option<Path> targetFile;
|
private Option<Path> targetFile;
|
||||||
|
|
||||||
private HTMLFileRenderer() {
|
private HTMLFileRenderer() {
|
||||||
|
@ -29,7 +29,7 @@ public class HTMLFileRenderer implements Renderer<Path> {
|
||||||
.toOption();
|
.toOption();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public static HTMLFileRenderer newInstance() {
|
public static HTMLFileRenderer newInstance() {
|
||||||
return new HTMLFileRenderer();
|
return new HTMLFileRenderer();
|
||||||
}
|
}
|
||||||
|
@ -40,15 +40,15 @@ public class HTMLFileRenderer implements Renderer<Path> {
|
||||||
.exists(File::canWrite);
|
.exists(File::canWrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public HTMLFileRenderer setTargetFile(@NonNull final Path targetFile) {
|
public HTMLFileRenderer setTargetFile(@NotNull final Path targetFile) {
|
||||||
this.targetFile = Option.of(targetFile);
|
this.targetFile = Option.of(targetFile);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Path render(@NonNull final Maze maze) {
|
public Path render(@NotNull final Maze maze) {
|
||||||
if (!this.isTargetFileDefinedAndWritable()) {
|
if (!this.isTargetFileDefinedAndWritable()) {
|
||||||
try {
|
try {
|
||||||
Files.createFile(this.targetFile.get());
|
Files.createFile(this.targetFile.get());
|
||||||
|
|
|
@ -7,8 +7,8 @@ import ch.fritteli.maze.generator.model.Direction;
|
||||||
import ch.fritteli.maze.generator.model.Maze;
|
import ch.fritteli.maze.generator.model.Maze;
|
||||||
import ch.fritteli.maze.generator.model.Tile;
|
import ch.fritteli.maze.generator.model.Tile;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -16,10 +16,10 @@ import java.util.List;
|
||||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||||
class Generator {
|
class Generator {
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private final Maze maze;
|
private final Maze maze;
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
JsonMaze generate() {
|
JsonMaze generate() {
|
||||||
final JsonMaze result = new JsonMaze();
|
final JsonMaze result = new JsonMaze();
|
||||||
result.setId(String.valueOf(this.maze.getRandomSeed()));
|
result.setId(String.valueOf(this.maze.getRandomSeed()));
|
||||||
|
@ -30,7 +30,7 @@ class Generator {
|
||||||
final ArrayList<JsonCell> row = new ArrayList<>();
|
final ArrayList<JsonCell> row = new ArrayList<>();
|
||||||
for (int x = 0; x < this.maze.getWidth(); x++) {
|
for (int x = 0; x < this.maze.getWidth(); x++) {
|
||||||
// x and y are not effectively final and can therefore not be accessed from within the lambda. Hence, create the string beforehand.
|
// x and y are not effectively final and can therefore not be accessed from within the lambda. Hence, create the string beforehand.
|
||||||
final String exceptionString = "Failed to obtain tile at %dx%d, although maze has dimensoins %dx%d"
|
final String exceptionString = "Failed to obtain tile at %dx%d, although maze has dimensions %dx%d"
|
||||||
.formatted(x, y, this.maze.getWidth(), this.maze.getHeight());
|
.formatted(x, y, this.maze.getWidth(), this.maze.getHeight());
|
||||||
final Tile tile = this.maze.getTileAt(x, y)
|
final Tile tile = this.maze.getTileAt(x, y)
|
||||||
.getOrElseThrow(() -> new IllegalStateException(exceptionString));
|
.getOrElseThrow(() -> new IllegalStateException(exceptionString));
|
||||||
|
@ -46,12 +46,12 @@ class Generator {
|
||||||
}
|
}
|
||||||
result.setGrid(rows);
|
result.setGrid(rows);
|
||||||
final JsonCoordinates start = new JsonCoordinates();
|
final JsonCoordinates start = new JsonCoordinates();
|
||||||
start.setX(this.maze.getStart().getX());
|
start.setX(this.maze.getStart().x());
|
||||||
start.setY(this.maze.getStart().getY());
|
start.setY(this.maze.getStart().y());
|
||||||
result.setStart(start);
|
result.setStart(start);
|
||||||
final JsonCoordinates end = new JsonCoordinates();
|
final JsonCoordinates end = new JsonCoordinates();
|
||||||
end.setX(this.maze.getEnd().getX());
|
end.setX(this.maze.getEnd().x());
|
||||||
end.setY(this.maze.getEnd().getY());
|
end.setY(this.maze.getEnd().y());
|
||||||
result.setEnd(end);
|
result.setEnd(end);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,14 @@ import ch.fritteli.maze.generator.renderer.Renderer;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
import lombok.NonNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class JsonRenderer implements Renderer<String> {
|
public class JsonRenderer implements Renderer<String> {
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
private JsonRenderer() {
|
private JsonRenderer() {
|
||||||
|
@ -22,12 +22,12 @@ public class JsonRenderer implements Renderer<String> {
|
||||||
.enable(SerializationFeature.INDENT_OUTPUT);
|
.enable(SerializationFeature.INDENT_OUTPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public static JsonRenderer newInstance() {
|
public static JsonRenderer newInstance() {
|
||||||
return new JsonRenderer();
|
return new JsonRenderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private static JsonMaze createSingleCellMaze() {
|
private static JsonMaze createSingleCellMaze() {
|
||||||
// This is the only cell.
|
// This is the only cell.
|
||||||
final JsonCell cell = new JsonCell();
|
final JsonCell cell = new JsonCell();
|
||||||
|
@ -37,7 +37,7 @@ public class JsonRenderer implements Renderer<String> {
|
||||||
// Wrap that in a nested list.
|
// Wrap that in a nested list.
|
||||||
final List<List<JsonCell>> rows = new ArrayList<>();
|
final List<List<JsonCell>> rows = new ArrayList<>();
|
||||||
rows.add(new ArrayList<>());
|
rows.add(new ArrayList<>());
|
||||||
rows.get(0).add(cell);
|
rows.getFirst().add(cell);
|
||||||
// Wrap it all in an instance of JsonMaze.
|
// Wrap it all in an instance of JsonMaze.
|
||||||
final JsonMaze jsonMaze = new JsonMaze();
|
final JsonMaze jsonMaze = new JsonMaze();
|
||||||
jsonMaze.setId("0");
|
jsonMaze.setId("0");
|
||||||
|
@ -45,8 +45,8 @@ public class JsonRenderer implements Renderer<String> {
|
||||||
return jsonMaze;
|
return jsonMaze;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private String toString(@NonNull final JsonMaze jsonMaze) {
|
private String toString(@NotNull final JsonMaze jsonMaze) {
|
||||||
try {
|
try {
|
||||||
return this.objectMapper.writeValueAsString(jsonMaze);
|
return this.objectMapper.writeValueAsString(jsonMaze);
|
||||||
} catch (final JsonProcessingException e) {
|
} catch (final JsonProcessingException e) {
|
||||||
|
@ -54,9 +54,9 @@ public class JsonRenderer implements Renderer<String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public String render(@NonNull final Maze maze) {
|
public String render(@NotNull final Maze maze) {
|
||||||
final JsonMaze jsonMaze;
|
final JsonMaze jsonMaze;
|
||||||
if (maze.getWidth() == 0 || maze.getHeight() == 0) {
|
if (maze.getWidth() == 0 || maze.getHeight() == 0) {
|
||||||
jsonMaze = createSingleCellMaze();
|
jsonMaze = createSingleCellMaze();
|
||||||
|
|
|
@ -5,21 +5,22 @@ import ch.fritteli.maze.generator.renderer.Renderer;
|
||||||
import ch.fritteli.maze.generator.renderer.json.JsonRenderer;
|
import ch.fritteli.maze.generator.renderer.json.JsonRenderer;
|
||||||
import io.vavr.control.Option;
|
import io.vavr.control.Option;
|
||||||
import io.vavr.control.Try;
|
import io.vavr.control.Try;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class JsonFileRenderer implements Renderer<Path> {
|
public class JsonFileRenderer implements Renderer<Path> {
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private static final JsonRenderer JSON_RENDERER = JsonRenderer.newInstance();
|
private static final JsonRenderer JSON_RENDERER = JsonRenderer.newInstance();
|
||||||
@NonNull
|
@NotNull
|
||||||
private Option<Path> targetFile;
|
private Option<Path> targetFile;
|
||||||
|
|
||||||
private JsonFileRenderer() {
|
private JsonFileRenderer() {
|
||||||
|
@ -29,7 +30,7 @@ public class JsonFileRenderer implements Renderer<Path> {
|
||||||
.toOption();
|
.toOption();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public static JsonFileRenderer newInstance() {
|
public static JsonFileRenderer newInstance() {
|
||||||
return new JsonFileRenderer();
|
return new JsonFileRenderer();
|
||||||
}
|
}
|
||||||
|
@ -40,15 +41,15 @@ public class JsonFileRenderer implements Renderer<Path> {
|
||||||
.exists(File::canWrite);
|
.exists(File::canWrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public JsonFileRenderer setTargetFile(@NonNull final Path targetFile) {
|
public JsonFileRenderer setTargetFile(@NotNull final Path targetFile) {
|
||||||
this.targetFile = Option.of(targetFile);
|
this.targetFile = Option.of(targetFile);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Path render(@NonNull final Maze maze) {
|
public Path render(@NotNull final Maze maze) {
|
||||||
if (!this.isTargetFileDefinedAndWritable()) {
|
if (!this.isTargetFileDefinedAndWritable()) {
|
||||||
try {
|
try {
|
||||||
Files.createFile(this.targetFile.get());
|
Files.createFile(this.targetFile.get());
|
||||||
|
|
|
@ -5,11 +5,6 @@ import ch.fritteli.maze.generator.model.Maze;
|
||||||
import ch.fritteli.maze.generator.model.Position;
|
import ch.fritteli.maze.generator.model.Position;
|
||||||
import ch.fritteli.maze.generator.model.Tile;
|
import ch.fritteli.maze.generator.model.Tile;
|
||||||
import io.vavr.control.Option;
|
import io.vavr.control.Option;
|
||||||
import java.awt.BasicStroke;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
@ -18,16 +13,21 @@ import org.apache.pdfbox.pdmodel.PDDocumentInformation;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Slf4j
|
@Slf4j
|
||||||
class Generator {
|
class Generator {
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private final Maze maze;
|
private final Maze maze;
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public ByteArrayOutputStream generate() {
|
public ByteArrayOutputStream generate() {
|
||||||
final float pageWidth = this.maze.getWidth() * PDFRenderer.SCALE + 2 * PDFRenderer.MARGIN;
|
final float pageWidth = this.maze.getWidth() * PDFRenderer.SCALE + 2 * PDFRenderer.MARGIN;
|
||||||
final float pageHeight = this.maze.getHeight() * PDFRenderer.SCALE + 2 * PDFRenderer.MARGIN;
|
final float pageHeight = this.maze.getHeight() * PDFRenderer.SCALE + 2 * PDFRenderer.MARGIN;
|
||||||
|
@ -60,7 +60,7 @@ class Generator {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUpPageContentStream(@NonNull final PDPageContentStream pageContentStream) throws IOException {
|
private void setUpPageContentStream(@NotNull final PDPageContentStream pageContentStream) throws IOException {
|
||||||
pageContentStream.setLineCapStyle(BasicStroke.CAP_ROUND);
|
pageContentStream.setLineCapStyle(BasicStroke.CAP_ROUND);
|
||||||
pageContentStream.setLineJoinStyle(BasicStroke.JOIN_ROUND);
|
pageContentStream.setLineJoinStyle(BasicStroke.JOIN_ROUND);
|
||||||
pageContentStream.setLineWidth(1.0f);
|
pageContentStream.setLineWidth(1.0f);
|
||||||
|
@ -68,7 +68,7 @@ class Generator {
|
||||||
pageContentStream.setNonStrokingColor(Color.BLACK);
|
pageContentStream.setNonStrokingColor(Color.BLACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawHorizontalLines(@NonNull final PDPageContentStream... contentStreams) throws IOException {
|
private void drawHorizontalLines(@NotNull final PDPageContentStream... contentStreams) throws IOException {
|
||||||
// PDF has the origin in the lower left corner. We want it in the upper left corner, hence some magic is required.
|
// PDF has the origin in the lower left corner. We want it in the upper left corner, hence some magic is required.
|
||||||
Coordinate coordinate = new Coordinate(0f, 0f);
|
Coordinate coordinate = new Coordinate(0f, 0f);
|
||||||
// Draw the TOP borders of all tiles.
|
// Draw the TOP borders of all tiles.
|
||||||
|
@ -136,7 +136,7 @@ class Generator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawVerticalLines(@NonNull final PDPageContentStream... contentStreams) throws IOException {
|
private void drawVerticalLines(@NotNull final PDPageContentStream... contentStreams) throws IOException {
|
||||||
// PDF has the origin in the lower left corner. We want it in the upper left corner, hence some magic is required.
|
// PDF has the origin in the lower left corner. We want it in the upper left corner, hence some magic is required.
|
||||||
Coordinate coordinate = new Coordinate(0f, 0f);
|
Coordinate coordinate = new Coordinate(0f, 0f);
|
||||||
// Draw the LEFT borders of all tiles.
|
// Draw the LEFT borders of all tiles.
|
||||||
|
@ -204,27 +204,27 @@ class Generator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawSolution(@NonNull final PDPageContentStream pageContentStream) throws IOException {
|
private void drawSolution(@NotNull final PDPageContentStream pageContentStream) throws IOException {
|
||||||
// Draw the solution in red
|
// Draw the solution in red
|
||||||
pageContentStream.setStrokingColor(Color.RED);
|
pageContentStream.setStrokingColor(Color.RED);
|
||||||
// PDF has the origin in the lower left corner. We want it in the upper left corner, hence some magic is required.
|
// PDF has the origin in the lower left corner. We want it in the upper left corner, hence some magic is required.
|
||||||
final Position end = this.maze.getEnd();
|
final Position end = this.maze.getEnd();
|
||||||
Position currentPosition = this.maze.getStart();
|
Position currentPosition = this.maze.getStart();
|
||||||
Position previousPosition = null;
|
Position previousPosition = null;
|
||||||
SolutionCoordinate coordinate = new SolutionCoordinate(currentPosition.getX(), currentPosition.getY());
|
SolutionCoordinate coordinate = new SolutionCoordinate(currentPosition.x(), currentPosition.y());
|
||||||
pageContentStream.moveTo(coordinate.getX(), coordinate.getY());
|
pageContentStream.moveTo(coordinate.getX(), coordinate.getY());
|
||||||
do {
|
do {
|
||||||
Position newCurrent = this.findNextSolutionPosition(previousPosition, currentPosition);
|
Position newCurrent = this.findNextSolutionPosition(previousPosition, currentPosition);
|
||||||
previousPosition = currentPosition;
|
previousPosition = currentPosition;
|
||||||
currentPosition = newCurrent;
|
currentPosition = newCurrent;
|
||||||
coordinate = new SolutionCoordinate(currentPosition.getX(), currentPosition.getY());
|
coordinate = new SolutionCoordinate(currentPosition.x(), currentPosition.y());
|
||||||
pageContentStream.lineTo(coordinate.getX(), coordinate.getY());
|
pageContentStream.lineTo(coordinate.getX(), coordinate.getY());
|
||||||
} while (!currentPosition.equals(end));
|
} while (!currentPosition.equals(end));
|
||||||
pageContentStream.stroke();
|
pageContentStream.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private Position findNextSolutionPosition(@Nullable final Position previousPosition, @NonNull final Position currentPosition) {
|
private Position findNextSolutionPosition(@Nullable final Position previousPosition, @NotNull final Position currentPosition) {
|
||||||
final Tile currentTile = this.maze.getTileAt(currentPosition).get();
|
final Tile currentTile = this.maze.getTileAt(currentPosition).get();
|
||||||
for (final Direction direction : Direction.values()) {
|
for (final Direction direction : Direction.values()) {
|
||||||
if (!currentTile.hasWallAt(direction)) {
|
if (!currentTile.hasWallAt(direction)) {
|
||||||
|
|
|
@ -2,9 +2,9 @@ package ch.fritteli.maze.generator.renderer.pdf;
|
||||||
|
|
||||||
import ch.fritteli.maze.generator.model.Maze;
|
import ch.fritteli.maze.generator.model.Maze;
|
||||||
import ch.fritteli.maze.generator.renderer.Renderer;
|
import ch.fritteli.maze.generator.renderer.Renderer;
|
||||||
import java.io.ByteArrayOutputStream;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import lombok.NonNull;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
|
||||||
public class PDFRenderer implements Renderer<ByteArrayOutputStream> {
|
public class PDFRenderer implements Renderer<ByteArrayOutputStream> {
|
||||||
static final float MARGIN = 10;
|
static final float MARGIN = 10;
|
||||||
|
@ -13,14 +13,14 @@ public class PDFRenderer implements Renderer<ByteArrayOutputStream> {
|
||||||
private PDFRenderer() {
|
private PDFRenderer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public static PDFRenderer newInstance() {
|
public static PDFRenderer newInstance() {
|
||||||
return new PDFRenderer();
|
return new PDFRenderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public ByteArrayOutputStream render(@NonNull final Maze maze) {
|
public ByteArrayOutputStream render(@NotNull final Maze maze) {
|
||||||
final Generator generator = new Generator(maze);
|
final Generator generator = new Generator(maze);
|
||||||
return generator.generate();
|
return generator.generate();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,21 +5,22 @@ import ch.fritteli.maze.generator.renderer.Renderer;
|
||||||
import ch.fritteli.maze.generator.renderer.pdf.PDFRenderer;
|
import ch.fritteli.maze.generator.renderer.pdf.PDFRenderer;
|
||||||
import io.vavr.control.Option;
|
import io.vavr.control.Option;
|
||||||
import io.vavr.control.Try;
|
import io.vavr.control.Try;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class PDFFileRenderer implements Renderer<Path> {
|
public class PDFFileRenderer implements Renderer<Path> {
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private static final PDFRenderer PDF_RENDERER = PDFRenderer.newInstance();
|
private static final PDFRenderer PDF_RENDERER = PDFRenderer.newInstance();
|
||||||
@NonNull
|
@NotNull
|
||||||
private Option<Path> targetFile;
|
private Option<Path> targetFile;
|
||||||
|
|
||||||
private PDFFileRenderer() {
|
private PDFFileRenderer() {
|
||||||
|
@ -29,7 +30,7 @@ public class PDFFileRenderer implements Renderer<Path> {
|
||||||
.toOption();
|
.toOption();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public static PDFFileRenderer newInstance() {
|
public static PDFFileRenderer newInstance() {
|
||||||
return new PDFFileRenderer();
|
return new PDFFileRenderer();
|
||||||
}
|
}
|
||||||
|
@ -40,15 +41,15 @@ public class PDFFileRenderer implements Renderer<Path> {
|
||||||
.exists(File::canWrite);
|
.exists(File::canWrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public PDFFileRenderer setTargetFile(@NonNull final Path targetFile) {
|
public PDFFileRenderer setTargetFile(@NotNull final Path targetFile) {
|
||||||
this.targetFile = Option.of(targetFile);
|
this.targetFile = Option.of(targetFile);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Path render(@NonNull final Maze maze) {
|
public Path render(@NotNull final Maze maze) {
|
||||||
if (!this.isTargetFileDefinedAndWritable()) {
|
if (!this.isTargetFileDefinedAndWritable()) {
|
||||||
try {
|
try {
|
||||||
Files.createFile(this.targetFile.get());
|
Files.createFile(this.targetFile.get());
|
||||||
|
|
|
@ -5,12 +5,12 @@ import ch.fritteli.maze.generator.model.Maze;
|
||||||
import ch.fritteli.maze.generator.model.Tile;
|
import ch.fritteli.maze.generator.model.Tile;
|
||||||
import io.vavr.control.Option;
|
import io.vavr.control.Option;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||||
class Generator {
|
class Generator {
|
||||||
@NonNull
|
@NotNull
|
||||||
private final Maze maze;
|
private final Maze maze;
|
||||||
private final boolean renderSolution;
|
private final boolean renderSolution;
|
||||||
private int x = 0;
|
private int x = 0;
|
||||||
|
@ -34,21 +34,12 @@ class Generator {
|
||||||
final Option<Tile> rightTile = this.maze.getTileAt(this.x + 1, this.y);
|
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> bottomTile = this.maze.getTileAt(this.x, this.y + 1);
|
||||||
final Option<Tile> leftTile = this.maze.getTileAt(this.x - 1, this.y);
|
final Option<Tile> leftTile = this.maze.getTileAt(this.x - 1, this.y);
|
||||||
final String s;
|
final String s = switch (this.line) {
|
||||||
switch (this.line) {
|
case 0 -> this.renderTopLine(currentTile, leftTile, topTile);
|
||||||
case 0:
|
case 1 -> this.renderCenterLine(currentTile, topTile, rightTile, bottomTile, leftTile);
|
||||||
s = this.renderTopLine(currentTile, leftTile, topTile);
|
case 2 -> this.renderBottomLine(currentTile, leftTile);
|
||||||
break;
|
default -> "";
|
||||||
case 1:
|
};
|
||||||
s = this.renderCenterLine(currentTile, topTile, rightTile, bottomTile, leftTile);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
s = this.renderBottomLine(currentTile, leftTile);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
s = "";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this.prepareNextStep();
|
this.prepareNextStep();
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@ -76,7 +67,7 @@ class Generator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String renderTopLine(@NonNull final Tile currentTile, @NonNull final Option<Tile> leftTile, @NonNull final Option<Tile> topTile) {
|
private String renderTopLine(@NotNull final Tile currentTile, @NotNull final Option<Tile> leftTile, @NotNull final Option<Tile> topTile) {
|
||||||
final CharDefinition charDef1 = new CharDefinition();
|
final CharDefinition charDef1 = new CharDefinition();
|
||||||
final CharDefinition charDef2 = new CharDefinition();
|
final CharDefinition charDef2 = new CharDefinition();
|
||||||
final CharDefinition charDef3 = new CharDefinition();
|
final CharDefinition charDef3 = new CharDefinition();
|
||||||
|
@ -112,11 +103,11 @@ class Generator {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String renderCenterLine(@NonNull final Tile currentTile,
|
private String renderCenterLine(@NotNull final Tile currentTile,
|
||||||
@NonNull final Option<Tile> topTile,
|
@NotNull final Option<Tile> topTile,
|
||||||
@NonNull final Option<Tile> rightTile,
|
@NotNull final Option<Tile> rightTile,
|
||||||
@NonNull final Option<Tile> bottomTile,
|
@NotNull final Option<Tile> bottomTile,
|
||||||
@NonNull final Option<Tile> leftTile) {
|
@NotNull final Option<Tile> leftTile) {
|
||||||
final CharDefinition charDef1 = new CharDefinition();
|
final CharDefinition charDef1 = new CharDefinition();
|
||||||
final CharDefinition charDef2 = new CharDefinition();
|
final CharDefinition charDef2 = new CharDefinition();
|
||||||
final CharDefinition charDef3 = new CharDefinition();
|
final CharDefinition charDef3 = new CharDefinition();
|
||||||
|
@ -160,7 +151,7 @@ class Generator {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String renderBottomLine(@NonNull final Tile currentTile, @NonNull final Option<Tile> leftTile) {
|
private String renderBottomLine(@NotNull final Tile currentTile, @NotNull final Option<Tile> leftTile) {
|
||||||
String result;
|
String result;
|
||||||
final CharDefinition charDef1 = new CharDefinition();
|
final CharDefinition charDef1 = new CharDefinition();
|
||||||
final CharDefinition charDef2 = new CharDefinition();
|
final CharDefinition charDef2 = new CharDefinition();
|
||||||
|
@ -193,15 +184,15 @@ class Generator {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasWallAt(@NonNull final Option<Tile> tile, @NonNull final Direction direction) {
|
private boolean hasWallAt(@NotNull final Option<Tile> tile, @NotNull final Direction direction) {
|
||||||
return tile.map(t -> t.hasWallAt(direction)).getOrElse(false);
|
return tile.map(t -> t.hasWallAt(direction)).getOrElse(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSolution(@NonNull final Tile tile) {
|
private boolean isSolution(@NotNull final Tile tile) {
|
||||||
return this.renderSolution && tile != null && tile.isSolution();
|
return this.renderSolution && tile.isSolution();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSolution(@NonNull final Option<Tile> tile) {
|
private boolean isSolution(@NotNull final Option<Tile> tile) {
|
||||||
return this.renderSolution && tile.map(Tile::isSolution).getOrElse(false);
|
return this.renderSolution && tile.map(Tile::isSolution).getOrElse(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package ch.fritteli.maze.generator.renderer.text;
|
||||||
|
|
||||||
import ch.fritteli.maze.generator.model.Maze;
|
import ch.fritteli.maze.generator.model.Maze;
|
||||||
import ch.fritteli.maze.generator.renderer.Renderer;
|
import ch.fritteli.maze.generator.renderer.Renderer;
|
||||||
import lombok.NonNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class TextRenderer implements Renderer<String> {
|
public class TextRenderer implements Renderer<String> {
|
||||||
private boolean renderSolution;
|
private boolean renderSolution;
|
||||||
|
@ -11,20 +11,20 @@ public class TextRenderer implements Renderer<String> {
|
||||||
this.renderSolution = false;
|
this.renderSolution = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public static TextRenderer newInstance() {
|
public static TextRenderer newInstance() {
|
||||||
return new TextRenderer();
|
return new TextRenderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public TextRenderer setRenderSolution(final boolean renderSolution) {
|
public TextRenderer setRenderSolution(final boolean renderSolution) {
|
||||||
this.renderSolution = renderSolution;
|
this.renderSolution = renderSolution;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public String render(@NonNull final Maze maze) {
|
public String render(@NotNull final Maze maze) {
|
||||||
if (maze.getWidth() == 0 || maze.getHeight() == 0) {
|
if (maze.getWidth() == 0 || maze.getHeight() == 0) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,23 +6,22 @@ import ch.fritteli.maze.generator.renderer.text.TextRenderer;
|
||||||
import io.vavr.collection.List;
|
import io.vavr.collection.List;
|
||||||
import io.vavr.control.Option;
|
import io.vavr.control.Option;
|
||||||
import io.vavr.control.Try;
|
import io.vavr.control.Try;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class TextFileRenderer implements Renderer<List<Path>> {
|
public class TextFileRenderer implements Renderer<List<Path>> {
|
||||||
@NonNull
|
@NotNull
|
||||||
private static final TextRenderer TEXT_RENDERER = TextRenderer.newInstance();
|
private static final TextRenderer TEXT_RENDERER = TextRenderer.newInstance();
|
||||||
@NonNull
|
@NotNull
|
||||||
private Option<Path> targetMazeFile;
|
private Option<Path> targetMazeFile;
|
||||||
@NonNull
|
@NotNull
|
||||||
private Option<Path> targetSolutionFile;
|
private Option<Path> targetSolutionFile;
|
||||||
|
|
||||||
private TextFileRenderer() {
|
private TextFileRenderer() {
|
||||||
|
@ -42,7 +41,7 @@ public class TextFileRenderer implements Renderer<List<Path>> {
|
||||||
.toOption();
|
.toOption();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public static TextFileRenderer newInstance() {
|
public static TextFileRenderer newInstance() {
|
||||||
return new TextFileRenderer();
|
return new TextFileRenderer();
|
||||||
}
|
}
|
||||||
|
@ -59,21 +58,21 @@ public class TextFileRenderer implements Renderer<List<Path>> {
|
||||||
.exists(File::canWrite);
|
.exists(File::canWrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public TextFileRenderer setTargetMazeFile(@NonNull final Path targetMazeFile) {
|
public TextFileRenderer setTargetMazeFile(@NotNull final Path targetMazeFile) {
|
||||||
this.targetMazeFile = Option.of(targetMazeFile);
|
this.targetMazeFile = Option.of(targetMazeFile);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public TextFileRenderer setTargetSolutionFile(@NonNull final Path targetSolutionFile) {
|
public TextFileRenderer setTargetSolutionFile(@NotNull final Path targetSolutionFile) {
|
||||||
this.targetSolutionFile = Option.of(targetSolutionFile);
|
this.targetSolutionFile = Option.of(targetSolutionFile);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public List<Path> render(@NonNull final Maze maze) {
|
public List<Path> render(@NotNull final Maze maze) {
|
||||||
if (!this.isTargetMazeFileDefinedAndWritable()) {
|
if (!this.isTargetMazeFileDefinedAndWritable()) {
|
||||||
try {
|
try {
|
||||||
Files.createFile(this.targetMazeFile.get());
|
Files.createFile(this.targetMazeFile.get());
|
||||||
|
@ -98,12 +97,12 @@ public class TextFileRenderer implements Renderer<List<Path>> {
|
||||||
final Path targetMazeFile = this.targetMazeFile.get();
|
final Path targetMazeFile = this.targetMazeFile.get();
|
||||||
final Path targetSolutionFile = this.targetSolutionFile.get();
|
final Path targetSolutionFile = this.targetSolutionFile.get();
|
||||||
try {
|
try {
|
||||||
Files.write(targetMazeFile, text.getBytes(StandardCharsets.UTF_8));
|
Files.writeString(targetMazeFile, text);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Failed writing to file " + targetMazeFile.normalize(), e);
|
log.error("Failed writing to file " + targetMazeFile.normalize(), e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Files.write(targetSolutionFile, solution.getBytes(StandardCharsets.UTF_8));
|
Files.writeString(targetSolutionFile, solution);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Failed writing to file " + targetSolutionFile.normalize());
|
log.error("Failed writing to file " + targetSolutionFile.normalize());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
package ch.fritteli.maze.generator.serialization;
|
package ch.fritteli.maze.generator.serialization;
|
||||||
|
|
||||||
import ch.fritteli.maze.generator.model.Maze;
|
import ch.fritteli.maze.generator.model.Maze;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import lombok.NonNull;
|
|
||||||
|
|
||||||
public abstract class AbstractMazeInputStream extends ByteArrayInputStream {
|
public abstract class AbstractMazeInputStream extends ByteArrayInputStream {
|
||||||
|
|
||||||
public AbstractMazeInputStream(@NonNull final byte[] buf) {
|
public AbstractMazeInputStream(@NotNull final byte[] buf) {
|
||||||
super(buf);
|
super(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void checkHeader();
|
public abstract void checkHeader();
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public abstract Maze readMazeData();
|
public abstract Maze readMazeData();
|
||||||
|
|
||||||
public byte readByte() {
|
public byte readByte() {
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
package ch.fritteli.maze.generator.serialization;
|
package ch.fritteli.maze.generator.serialization;
|
||||||
|
|
||||||
import ch.fritteli.maze.generator.model.Maze;
|
import ch.fritteli.maze.generator.model.Maze;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import lombok.NonNull;
|
|
||||||
|
|
||||||
public abstract class AbstractMazeOutputStream extends ByteArrayOutputStream {
|
public abstract class AbstractMazeOutputStream extends ByteArrayOutputStream {
|
||||||
|
|
||||||
public abstract void writeHeader();
|
public abstract void writeHeader();
|
||||||
|
|
||||||
public abstract void writeMazeData(@NonNull final Maze maze);
|
public abstract void writeMazeData(@NotNull final Maze maze);
|
||||||
|
|
||||||
public void writeByte(final byte value) {
|
public void writeByte(final byte value) {
|
||||||
this.write(value);
|
this.write(value);
|
||||||
|
|
|
@ -3,11 +3,11 @@ package ch.fritteli.maze.generator.serialization.v1;
|
||||||
import ch.fritteli.maze.generator.model.Maze;
|
import ch.fritteli.maze.generator.model.Maze;
|
||||||
import ch.fritteli.maze.generator.model.Tile;
|
import ch.fritteli.maze.generator.model.Tile;
|
||||||
import ch.fritteli.maze.generator.serialization.AbstractMazeInputStream;
|
import ch.fritteli.maze.generator.serialization.AbstractMazeInputStream;
|
||||||
import lombok.NonNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class MazeInputStreamV1 extends AbstractMazeInputStream {
|
public class MazeInputStreamV1 extends AbstractMazeInputStream {
|
||||||
|
|
||||||
public MazeInputStreamV1(@NonNull final byte[] buf) {
|
public MazeInputStreamV1(@NotNull final byte[] buf) {
|
||||||
super(buf);
|
super(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ public class MazeInputStreamV1 extends AbstractMazeInputStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Maze readMazeData() {
|
public Maze readMazeData() {
|
||||||
final long randomSeed = this.readLong();
|
final long randomSeed = this.readLong();
|
||||||
|
|
|
@ -3,7 +3,7 @@ package ch.fritteli.maze.generator.serialization.v1;
|
||||||
import ch.fritteli.maze.generator.model.Maze;
|
import ch.fritteli.maze.generator.model.Maze;
|
||||||
import ch.fritteli.maze.generator.model.Tile;
|
import ch.fritteli.maze.generator.model.Tile;
|
||||||
import ch.fritteli.maze.generator.serialization.AbstractMazeOutputStream;
|
import ch.fritteli.maze.generator.serialization.AbstractMazeOutputStream;
|
||||||
import lombok.NonNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class MazeOutputStreamV1 extends AbstractMazeOutputStream {
|
public class MazeOutputStreamV1 extends AbstractMazeOutputStream {
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ public class MazeOutputStreamV1 extends AbstractMazeOutputStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeMazeData(@NonNull final Maze maze) {
|
public void writeMazeData(@NotNull final Maze maze) {
|
||||||
final long randomSeed = maze.getRandomSeed();
|
final long randomSeed = maze.getRandomSeed();
|
||||||
final int width = maze.getWidth();
|
final int width = maze.getWidth();
|
||||||
final int height = maze.getHeight();
|
final int height = maze.getHeight();
|
||||||
|
|
|
@ -3,8 +3,8 @@ package ch.fritteli.maze.generator.serialization.v1;
|
||||||
import ch.fritteli.maze.generator.model.Direction;
|
import ch.fritteli.maze.generator.model.Direction;
|
||||||
import ch.fritteli.maze.generator.model.Maze;
|
import ch.fritteli.maze.generator.model.Maze;
|
||||||
import ch.fritteli.maze.generator.model.Tile;
|
import ch.fritteli.maze.generator.model.Tile;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
@ -62,8 +62,8 @@ public class SerializerDeserializerV1 {
|
||||||
* @param maze The {@link Maze} to be serialized.
|
* @param maze The {@link Maze} to be serialized.
|
||||||
* @return The resulting byte array.
|
* @return The resulting byte array.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NotNull
|
||||||
public byte[] serialize(@NonNull final Maze maze) {
|
public byte[] serialize(@NotNull final Maze maze) {
|
||||||
final MazeOutputStreamV1 stream = new MazeOutputStreamV1();
|
final MazeOutputStreamV1 stream = new MazeOutputStreamV1();
|
||||||
stream.writeHeader();
|
stream.writeHeader();
|
||||||
stream.writeMazeData(maze);
|
stream.writeMazeData(maze);
|
||||||
|
@ -76,38 +76,38 @@ public class SerializerDeserializerV1 {
|
||||||
* @param bytes The byte array to be deserialized.
|
* @param bytes The byte array to be deserialized.
|
||||||
* @return An instance of {@link Maze}.
|
* @return An instance of {@link Maze}.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NotNull
|
||||||
public Maze deserialize(@NonNull final byte[] bytes) {
|
public Maze deserialize(@NotNull final byte[] bytes) {
|
||||||
final MazeInputStreamV1 stream = new MazeInputStreamV1(bytes);
|
final MazeInputStreamV1 stream = new MazeInputStreamV1(bytes);
|
||||||
stream.checkHeader();
|
stream.checkHeader();
|
||||||
return stream.readMazeData();
|
return stream.readMazeData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
Maze createMaze(@NonNull final Tile[][] field, final int width, final int height, final long randomSeed) {
|
Maze createMaze(@NotNull final Tile[][] field, final int width, final int height, final long randomSeed) {
|
||||||
try {
|
try {
|
||||||
final Constructor<Maze> constructor = Maze.class.getDeclaredConstructor(Tile[][].class, Integer.TYPE, Integer.TYPE, Long.TYPE);
|
final Constructor<Maze> constructor = Maze.class.getDeclaredConstructor(Tile[][].class, Integer.TYPE, Integer.TYPE, Long.TYPE);
|
||||||
constructor.setAccessible(true);
|
constructor.setAccessible(true);
|
||||||
return constructor.newInstance(field, width, height, randomSeed);
|
return constructor.newInstance(field, width, height, randomSeed);
|
||||||
} catch (@NonNull final NoSuchMethodException | IllegalAccessException | InstantiationException |
|
} catch (@NotNull final NoSuchMethodException | IllegalAccessException | InstantiationException |
|
||||||
InvocationTargetException e) {
|
InvocationTargetException e) {
|
||||||
throw new RuntimeException("Can not deserialize Maze from maze data.", e);
|
throw new RuntimeException("Can not deserialize Maze from maze data.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private Tile createTile(@NonNull final EnumSet<Direction> walls, boolean solution) {
|
private Tile createTile(@NotNull final EnumSet<Direction> walls, boolean solution) {
|
||||||
try {
|
try {
|
||||||
final Constructor<Tile> constructor = Tile.class.getDeclaredConstructor(EnumSet.class, Boolean.TYPE);
|
final Constructor<Tile> constructor = Tile.class.getDeclaredConstructor(EnumSet.class, Boolean.TYPE);
|
||||||
constructor.setAccessible(true);
|
constructor.setAccessible(true);
|
||||||
return constructor.newInstance(walls, solution);
|
return constructor.newInstance(walls, solution);
|
||||||
} catch (@NonNull final NoSuchMethodException | InstantiationException | IllegalAccessException |
|
} catch (@NotNull final NoSuchMethodException | InstantiationException | IllegalAccessException |
|
||||||
InvocationTargetException e) {
|
InvocationTargetException e) {
|
||||||
throw new RuntimeException("Can not deserialize Tile from maze data.", e);
|
throw new RuntimeException("Can not deserialize Tile from maze data.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
byte getBitmaskForTile(@NonNull final Tile tile) {
|
byte getBitmaskForTile(@NotNull final Tile tile) {
|
||||||
byte bitmask = 0;
|
byte bitmask = 0;
|
||||||
if (tile.hasWallAt(Direction.TOP)) {
|
if (tile.hasWallAt(Direction.TOP)) {
|
||||||
bitmask |= TOP_BIT;
|
bitmask |= TOP_BIT;
|
||||||
|
@ -127,7 +127,7 @@ public class SerializerDeserializerV1 {
|
||||||
return bitmask;
|
return bitmask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
Tile getTileForBitmask(final byte bitmask) {
|
Tile getTileForBitmask(final byte bitmask) {
|
||||||
final EnumSet<Direction> walls = EnumSet.noneOf(Direction.class);
|
final EnumSet<Direction> walls = EnumSet.noneOf(Direction.class);
|
||||||
if ((bitmask & TOP_BIT) == TOP_BIT) {
|
if ((bitmask & TOP_BIT) == TOP_BIT) {
|
||||||
|
|
|
@ -4,11 +4,11 @@ import ch.fritteli.maze.generator.model.Maze;
|
||||||
import ch.fritteli.maze.generator.model.Position;
|
import ch.fritteli.maze.generator.model.Position;
|
||||||
import ch.fritteli.maze.generator.model.Tile;
|
import ch.fritteli.maze.generator.model.Tile;
|
||||||
import ch.fritteli.maze.generator.serialization.AbstractMazeInputStream;
|
import ch.fritteli.maze.generator.serialization.AbstractMazeInputStream;
|
||||||
import lombok.NonNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class MazeInputStreamV2 extends AbstractMazeInputStream {
|
public class MazeInputStreamV2 extends AbstractMazeInputStream {
|
||||||
|
|
||||||
public MazeInputStreamV2(@NonNull final byte[] buf) {
|
public MazeInputStreamV2(@NotNull final byte[] buf) {
|
||||||
super(buf);
|
super(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ public class MazeInputStreamV2 extends AbstractMazeInputStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Maze readMazeData() {
|
public Maze readMazeData() {
|
||||||
// 03..06 width (int)
|
// 03..06 width (int)
|
||||||
|
|
|
@ -4,7 +4,7 @@ import ch.fritteli.maze.generator.model.Maze;
|
||||||
import ch.fritteli.maze.generator.model.Position;
|
import ch.fritteli.maze.generator.model.Position;
|
||||||
import ch.fritteli.maze.generator.model.Tile;
|
import ch.fritteli.maze.generator.model.Tile;
|
||||||
import ch.fritteli.maze.generator.serialization.AbstractMazeOutputStream;
|
import ch.fritteli.maze.generator.serialization.AbstractMazeOutputStream;
|
||||||
import lombok.NonNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class MazeOutputStreamV2 extends AbstractMazeOutputStream {
|
public class MazeOutputStreamV2 extends AbstractMazeOutputStream {
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ public class MazeOutputStreamV2 extends AbstractMazeOutputStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeMazeData(@NonNull final Maze maze) {
|
public void writeMazeData(@NotNull final Maze maze) {
|
||||||
// 03..06 width (int)
|
// 03..06 width (int)
|
||||||
// 07..10 height (int)
|
// 07..10 height (int)
|
||||||
// 11..14 start-x (int)
|
// 11..14 start-x (int)
|
||||||
|
@ -35,10 +35,10 @@ public class MazeOutputStreamV2 extends AbstractMazeOutputStream {
|
||||||
final Position end = maze.getEnd();
|
final Position end = maze.getEnd();
|
||||||
this.writeInt(width);
|
this.writeInt(width);
|
||||||
this.writeInt(height);
|
this.writeInt(height);
|
||||||
this.writeInt(start.getX());
|
this.writeInt(start.x());
|
||||||
this.writeInt(start.getY());
|
this.writeInt(start.y());
|
||||||
this.writeInt(end.getX());
|
this.writeInt(end.x());
|
||||||
this.writeInt(end.getY());
|
this.writeInt(end.y());
|
||||||
this.writeLong(randomSeed);
|
this.writeLong(randomSeed);
|
||||||
|
|
||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
|
|
|
@ -4,8 +4,8 @@ import ch.fritteli.maze.generator.model.Direction;
|
||||||
import ch.fritteli.maze.generator.model.Maze;
|
import ch.fritteli.maze.generator.model.Maze;
|
||||||
import ch.fritteli.maze.generator.model.Position;
|
import ch.fritteli.maze.generator.model.Position;
|
||||||
import ch.fritteli.maze.generator.model.Tile;
|
import ch.fritteli.maze.generator.model.Tile;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
@ -67,8 +67,8 @@ public class SerializerDeserializerV2 {
|
||||||
* @param maze The {@link Maze} to be serialized.
|
* @param maze The {@link Maze} to be serialized.
|
||||||
* @return The resulting byte array.
|
* @return The resulting byte array.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NotNull
|
||||||
public byte[] serialize(@NonNull final Maze maze) {
|
public byte[] serialize(@NotNull final Maze maze) {
|
||||||
final MazeOutputStreamV2 stream = new MazeOutputStreamV2();
|
final MazeOutputStreamV2 stream = new MazeOutputStreamV2();
|
||||||
stream.writeHeader();
|
stream.writeHeader();
|
||||||
stream.writeMazeData(maze);
|
stream.writeMazeData(maze);
|
||||||
|
@ -81,38 +81,38 @@ public class SerializerDeserializerV2 {
|
||||||
* @param bytes The byte array to be deserialized.
|
* @param bytes The byte array to be deserialized.
|
||||||
* @return An instance of {@link Maze}.
|
* @return An instance of {@link Maze}.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NotNull
|
||||||
public Maze deserialize(@NonNull final byte[] bytes) {
|
public Maze deserialize(@NotNull final byte[] bytes) {
|
||||||
final MazeInputStreamV2 stream = new MazeInputStreamV2(bytes);
|
final MazeInputStreamV2 stream = new MazeInputStreamV2(bytes);
|
||||||
stream.checkHeader();
|
stream.checkHeader();
|
||||||
return stream.readMazeData();
|
return stream.readMazeData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
Maze createMaze(@NonNull final Tile[][] field, final int width, final int height, @NonNull final Position start, @NonNull final Position end, final long randomSeed) {
|
Maze createMaze(@NotNull final Tile[][] field, final int width, final int height, @NotNull final Position start, @NotNull final Position end, final long randomSeed) {
|
||||||
try {
|
try {
|
||||||
final Constructor<Maze> constructor = Maze.class.getDeclaredConstructor(Tile[][].class, Integer.TYPE, Integer.TYPE, Position.class, Position.class, Long.TYPE);
|
final Constructor<Maze> constructor = Maze.class.getDeclaredConstructor(Tile[][].class, Integer.TYPE, Integer.TYPE, Position.class, Position.class, Long.TYPE);
|
||||||
constructor.setAccessible(true);
|
constructor.setAccessible(true);
|
||||||
return constructor.newInstance(field, width, height, start, end, randomSeed);
|
return constructor.newInstance(field, width, height, start, end, randomSeed);
|
||||||
} catch (@NonNull final NoSuchMethodException | IllegalAccessException | InstantiationException |
|
} catch (@NotNull final NoSuchMethodException | IllegalAccessException | InstantiationException |
|
||||||
InvocationTargetException e) {
|
InvocationTargetException e) {
|
||||||
throw new RuntimeException("Can not deserialize Maze from maze data.", e);
|
throw new RuntimeException("Can not deserialize Maze from maze data.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private Tile createTile(@NonNull final EnumSet<Direction> walls, boolean solution) {
|
private Tile createTile(@NotNull final EnumSet<Direction> walls, boolean solution) {
|
||||||
try {
|
try {
|
||||||
final Constructor<Tile> constructor = Tile.class.getDeclaredConstructor(EnumSet.class, Boolean.TYPE);
|
final Constructor<Tile> constructor = Tile.class.getDeclaredConstructor(EnumSet.class, Boolean.TYPE);
|
||||||
constructor.setAccessible(true);
|
constructor.setAccessible(true);
|
||||||
return constructor.newInstance(walls, solution);
|
return constructor.newInstance(walls, solution);
|
||||||
} catch (@NonNull final NoSuchMethodException | InstantiationException | IllegalAccessException |
|
} catch (@NotNull final NoSuchMethodException | InstantiationException | IllegalAccessException |
|
||||||
InvocationTargetException e) {
|
InvocationTargetException e) {
|
||||||
throw new RuntimeException("Can not deserialize Tile from maze data.", e);
|
throw new RuntimeException("Can not deserialize Tile from maze data.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
byte getBitmaskForTile(@NonNull final Tile tile) {
|
byte getBitmaskForTile(@NotNull final Tile tile) {
|
||||||
byte bitmask = 0;
|
byte bitmask = 0;
|
||||||
if (tile.hasWallAt(Direction.TOP)) {
|
if (tile.hasWallAt(Direction.TOP)) {
|
||||||
bitmask |= TOP_BIT;
|
bitmask |= TOP_BIT;
|
||||||
|
@ -132,7 +132,7 @@ public class SerializerDeserializerV2 {
|
||||||
return bitmask;
|
return bitmask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
Tile getTileForBitmask(final byte bitmask) {
|
Tile getTileForBitmask(final byte bitmask) {
|
||||||
final EnumSet<Direction> walls = EnumSet.noneOf(Direction.class);
|
final EnumSet<Direction> walls = EnumSet.noneOf(Direction.class);
|
||||||
if ((bitmask & TOP_BIT) == TOP_BIT) {
|
if ((bitmask & TOP_BIT) == TOP_BIT) {
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
package ch.fritteli.maze.generator.model;
|
package ch.fritteli.maze.generator.model;
|
||||||
|
|
||||||
import ch.fritteli.maze.generator.model.Direction;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class DirectionTest {
|
class DirectionTest {
|
||||||
@Test
|
@Test
|
||||||
void invert() {
|
void shouldInvertTheDirection() {
|
||||||
assertThat(Direction.TOP.invert()).isEqualTo(Direction.BOTTOM);
|
assertThat(Direction.TOP.invert()).isEqualTo(Direction.BOTTOM);
|
||||||
assertThat(Direction.RIGHT.invert()).isEqualTo(Direction.LEFT);
|
assertThat(Direction.RIGHT.invert()).isEqualTo(Direction.LEFT);
|
||||||
assertThat(Direction.BOTTOM.invert()).isEqualTo(Direction.TOP);
|
assertThat(Direction.BOTTOM.invert()).isEqualTo(Direction.TOP);
|
||||||
|
|
|
@ -7,14 +7,24 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
|
||||||
class MazeTest {
|
class MazeTest {
|
||||||
@Test
|
@Test
|
||||||
void testConstruct() {
|
void shouldNotAccept0AsWidthOrHeight() {
|
||||||
// act / assert on simple cases
|
// act / assert
|
||||||
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new Maze(0, 0))
|
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new Maze(0, 0))
|
||||||
.withMessage("width and height must be >1");
|
.withMessage("width and height must be >1");
|
||||||
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new Maze(0, 0, 0))
|
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new Maze(0, 0, 0))
|
||||||
.withMessage("width and height must be >1");
|
.withMessage("width and height must be >1");
|
||||||
|
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new Maze(0, 5))
|
||||||
|
.withMessage("width and height must be >1");
|
||||||
|
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new Maze(0, 5, 0))
|
||||||
|
.withMessage("width and height must be >1");
|
||||||
|
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new Maze(5, 0))
|
||||||
|
.withMessage("width and height must be >1");
|
||||||
|
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new Maze(5, 0, 0))
|
||||||
|
.withMessage("width and height must be >1");
|
||||||
|
}
|
||||||
|
|
||||||
// now for the real work:
|
@Test
|
||||||
|
void testConstruct() {
|
||||||
// arrange
|
// arrange
|
||||||
final Maze sut = new Maze(2, 3, 5);
|
final Maze sut = new Maze(2, 3, 5);
|
||||||
|
|
||||||
|
@ -23,7 +33,9 @@ class MazeTest {
|
||||||
.returns(2, Maze::getWidth)
|
.returns(2, Maze::getWidth)
|
||||||
.returns(3, Maze::getHeight)
|
.returns(3, Maze::getHeight)
|
||||||
.returns(5L, Maze::getRandomSeed)
|
.returns(5L, Maze::getRandomSeed)
|
||||||
.returns(new Position(0, 0), Maze::getStart)
|
.satisfies(
|
||||||
.returns(new Position(1, 2), Maze::getEnd);
|
maze -> assertThat(maze.getStart()).isEqualTo(new Position(0, 0)),
|
||||||
|
maze -> assertThat(maze.getEnd()).isEqualTo(new Position(1, 2))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
package ch.fritteli.maze.generator.model;
|
package ch.fritteli.maze.generator.model;
|
||||||
|
|
||||||
import ch.fritteli.maze.generator.model.Direction;
|
|
||||||
import ch.fritteli.maze.generator.model.Position;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class PositionTest {
|
class PositionTest {
|
||||||
@Test
|
@Test
|
||||||
void move() {
|
void shouldMoveToCorrectNeighborPosition() {
|
||||||
// arrange
|
// arrange
|
||||||
final Position sut = new Position(0, 0);
|
final Position sut = new Position(0, 0);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package ch.fritteli.maze.generator.model;
|
package ch.fritteli.maze.generator.model;
|
||||||
|
|
||||||
import ch.fritteli.maze.generator.model.Direction;
|
|
||||||
import ch.fritteli.maze.generator.model.Tile;
|
|
||||||
import io.vavr.control.Option;
|
import io.vavr.control.Option;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -11,21 +9,20 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class TileTest {
|
class TileTest {
|
||||||
@Test
|
@Test
|
||||||
void testConstruct() {
|
void shouldConstructATileWithDefaultValues() {
|
||||||
// arrange / act
|
// arrange / act
|
||||||
final Tile sut = new Tile();
|
final Tile sut = new Tile();
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
assertThat(sut)
|
assertThat(sut.hasWallAt(Direction.TOP)).isTrue();
|
||||||
.returns(true, v -> v.hasWallAt(Direction.TOP))
|
assertThat(sut.hasWallAt(Direction.RIGHT)).isTrue();
|
||||||
.returns(true, v -> v.hasWallAt(Direction.RIGHT))
|
assertThat(sut.hasWallAt(Direction.BOTTOM)).isTrue();
|
||||||
.returns(true, v -> v.hasWallAt(Direction.BOTTOM))
|
assertThat(sut.hasWallAt(Direction.LEFT)).isTrue();
|
||||||
.returns(true, v -> v.hasWallAt(Direction.LEFT))
|
assertThat(sut.isSolution()).isFalse();
|
||||||
.returns(false, Tile::isSolution);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testDigFrom() {
|
void tileCanBeDugIntoOnlyOnce() {
|
||||||
// arrange
|
// arrange
|
||||||
final Tile sut = new Tile();
|
final Tile sut = new Tile();
|
||||||
|
|
||||||
|
@ -45,29 +42,27 @@ class TileTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testDigTo() {
|
void canDigToDirectionOnlyOnce() {
|
||||||
// arrange
|
// arrange
|
||||||
final Tile sut = new Tile();
|
final Tile sut = new Tile();
|
||||||
|
|
||||||
// act / assert
|
// act / assert
|
||||||
assertThat(sut)
|
assertThat(sut.digTo(Direction.TOP)).isTrue();
|
||||||
.returns(true, v -> v.digTo(Direction.TOP))
|
assertThat(sut.digTo(Direction.RIGHT)).isTrue();
|
||||||
.returns(true, v -> v.digTo(Direction.RIGHT))
|
assertThat(sut.digTo(Direction.BOTTOM)).isTrue();
|
||||||
.returns(true, v -> v.digTo(Direction.BOTTOM))
|
assertThat(sut.digTo(Direction.LEFT)).isTrue();
|
||||||
.returns(true, v -> v.digTo(Direction.LEFT))
|
|
||||||
// digging a second time does not succeed
|
// digging a second time does not succeed
|
||||||
.returns(false, v -> v.digTo(Direction.LEFT));
|
assertThat(sut.digTo(Direction.LEFT)).isFalse();
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
assertThat(sut)
|
assertThat(sut.hasWallAt(Direction.TOP)).isFalse();
|
||||||
.returns(false, v -> v.hasWallAt(Direction.TOP))
|
assertThat(sut.hasWallAt(Direction.RIGHT)).isFalse();
|
||||||
.returns(false, v -> v.hasWallAt(Direction.RIGHT))
|
assertThat(sut.hasWallAt(Direction.BOTTOM)).isFalse();
|
||||||
.returns(false, v -> v.hasWallAt(Direction.BOTTOM))
|
assertThat(sut.hasWallAt(Direction.LEFT)).isFalse();
|
||||||
.returns(false, v -> v.hasWallAt(Direction.LEFT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testPreventDiggingToOrFrom() {
|
void canNotDigToOrFromDirectionWhenPrevented() {
|
||||||
// arrange
|
// arrange
|
||||||
final Tile sut = new Tile();
|
final Tile sut = new Tile();
|
||||||
|
|
||||||
|
@ -75,10 +70,11 @@ class TileTest {
|
||||||
sut.preventDiggingToOrFrom(Direction.LEFT);
|
sut.preventDiggingToOrFrom(Direction.LEFT);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
assertThat(sut)
|
assertThat(sut.digTo(Direction.LEFT)).isFalse();
|
||||||
.returns(false, v -> v.digTo(Direction.LEFT))
|
assertThat(sut.digFrom(Direction.LEFT)).isFalse();
|
||||||
.returns(false, v -> v.digFrom(Direction.LEFT))
|
assertThat(sut.hasWallAt(Direction.LEFT)).isTrue();
|
||||||
.returns(true, v -> v.hasWallAt(Direction.LEFT));
|
// Digging in another direction is still permitted
|
||||||
|
assertThat(sut.digTo(Direction.RIGHT)).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package ch.fritteli.maze.generator.model;
|
package ch.fritteli.maze.generator.model;
|
||||||
|
|
||||||
import ch.fritteli.maze.generator.model.Direction;
|
|
||||||
import ch.fritteli.maze.generator.model.Walls;
|
|
||||||
import io.vavr.collection.Stream;
|
import io.vavr.collection.Stream;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -65,14 +63,14 @@ class WallsTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testHarden() {
|
void testSeal() {
|
||||||
// arrange
|
// arrange
|
||||||
final Walls sut = new Walls();
|
final Walls sut = new Walls();
|
||||||
sut.set(Direction.TOP);
|
sut.set(Direction.TOP);
|
||||||
sut.set(Direction.RIGHT);
|
sut.set(Direction.RIGHT);
|
||||||
|
|
||||||
// act
|
// act
|
||||||
sut.harden(Direction.TOP);
|
sut.seal(Direction.TOP);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
assertThat(sut)
|
assertThat(sut)
|
||||||
|
@ -88,34 +86,34 @@ class WallsTest {
|
||||||
assertThat(result).isFalse();
|
assertThat(result).isFalse();
|
||||||
assertThat(sut.isSet(Direction.TOP)).isTrue();
|
assertThat(sut.isSet(Direction.TOP)).isTrue();
|
||||||
|
|
||||||
// act / assert: try to harden un-set wall
|
// act / assert: try to seal un-set wall
|
||||||
assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> sut.harden(Direction.LEFT));
|
assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> sut.seal(Direction.LEFT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUnharden() {
|
void testUnseal() {
|
||||||
// arrange
|
// arrange
|
||||||
final Walls sut = new Walls();
|
final Walls sut = new Walls();
|
||||||
sut.setAll();
|
sut.setAll();
|
||||||
sut.harden(Direction.TOP);
|
sut.seal(Direction.TOP);
|
||||||
|
|
||||||
// pre-assert: TOP can't be cleared while hardened
|
// pre-assert: TOP can't be cleared while hardened
|
||||||
assertThat(sut.clear(Direction.TOP)).isFalse();
|
assertThat(sut.clear(Direction.TOP)).isFalse();
|
||||||
|
|
||||||
// act
|
// act
|
||||||
sut.unharden(Direction.TOP);
|
sut.unseal(Direction.TOP);
|
||||||
|
|
||||||
// assert: TOP can be cleared
|
// assert: TOP can be cleared
|
||||||
assertThat(sut.clear(Direction.TOP)).isTrue();
|
assertThat(sut.clear(Direction.TOP)).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testGetUnhardenedSet() {
|
void testGetUnsealedSet() {
|
||||||
// arrange
|
// arrange
|
||||||
final Walls sut = new Walls();
|
final Walls sut = new Walls();
|
||||||
|
|
||||||
// act
|
// act
|
||||||
Stream<Direction> result = sut.getUnhardenedSet();
|
Stream<Direction> result = sut.getUnsealedSet();
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
assertThat(result).isEmpty();
|
assertThat(result).isEmpty();
|
||||||
|
@ -125,16 +123,16 @@ class WallsTest {
|
||||||
sut.set(Direction.LEFT);
|
sut.set(Direction.LEFT);
|
||||||
|
|
||||||
// act
|
// act
|
||||||
result = sut.getUnhardenedSet();
|
result = sut.getUnsealedSet();
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
assertThat(result).containsExactly(Direction.TOP, Direction.LEFT);
|
assertThat(result).containsExactly(Direction.TOP, Direction.LEFT);
|
||||||
|
|
||||||
// arrange: harden a direction
|
// arrange: seal a direction
|
||||||
sut.harden(Direction.TOP);
|
sut.seal(Direction.TOP);
|
||||||
|
|
||||||
// act
|
// act
|
||||||
result = sut.getUnhardenedSet();
|
result = sut.getUnsealedSet();
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
assertThat(result).containsExactly(Direction.LEFT);
|
assertThat(result).containsExactly(Direction.LEFT);
|
||||||
|
|
Loading…
Reference in a new issue