Compare commits

...

13 commits

Author SHA1 Message Date
b18e7fba9e
Reword some stuff.
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-18 02:18:06 +02:00
49725fe7df
[maven-release-plugin] prepare for next development iteration
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-18 01:24:06 +02:00
4db011fd92
[maven-release-plugin] prepare release v0.2.1
Some checks failed
continuous-integration/drone/push Build is failing
2024-05-18 01:24:03 +02:00
7eb8d09ae8
Update parent.
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-18 01:21:22 +02:00
1e190150ef
Tokenized login. 2024-05-18 01:21:09 +02:00
724f0f1a53
[maven-release-plugin] prepare for next development iteration
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-18 00:38:03 +02:00
65084659c2
[maven-release-plugin] prepare release v0.2.0
Some checks failed
continuous-integration/drone/push Build is failing
2024-05-18 00:38:00 +02:00
79f5620aaa
[maven-release-plugin] rollback the release of v0.2.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-18 00:36:13 +02:00
684ecdbe53
[maven-release-plugin] prepare for next development iteration
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-18 00:23:00 +02:00
2ab7f9dc5f
[maven-release-plugin] prepare release v0.2.0
Some checks failed
continuous-integration/drone/push Build is failing
2024-05-18 00:22:57 +02:00
e3bc8d8452 Merge pull request 'Chore: Java 21.' (#9) from feature/java21 into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #9
2024-05-18 00:20:54 +02:00
4dd91b0277
Chore: Java 21.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2024-05-17 23:44:11 +02:00
aa27d5f138
[maven-release-plugin] prepare for next development iteration
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-16 02:36:43 +02:00
36 changed files with 300 additions and 322 deletions

View file

@ -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

View file

@ -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
View file

@ -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>

View file

@ -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();
} }
} }

View file

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

View file

@ -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()!");
}
} }
} }

View file

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

View file

@ -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)!");
}
} }
} }

View file

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

View file

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

View file

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

View file

@ -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");

View file

@ -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>" +

View file

@ -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());

View file

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

View file

@ -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();

View file

@ -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());

View file

@ -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;
@ -41,7 +41,7 @@ class Generator {
pdDocument.addPage(puzzlePage); pdDocument.addPage(puzzlePage);
pdDocument.addPage(solutionPage); pdDocument.addPage(solutionPage);
try (final PDPageContentStream puzzlePageContentStream = new PDPageContentStream(pdDocument, puzzlePage); try (final PDPageContentStream puzzlePageContentStream = new PDPageContentStream(pdDocument, puzzlePage);
final PDPageContentStream solutionPageContentStream = new PDPageContentStream(pdDocument, solutionPage)) { final PDPageContentStream solutionPageContentStream = new PDPageContentStream(pdDocument, solutionPage)) {
setUpPageContentStream(puzzlePageContentStream); setUpPageContentStream(puzzlePageContentStream);
setUpPageContentStream(solutionPageContentStream); setUpPageContentStream(solutionPageContentStream);
this.drawHorizontalLines(puzzlePageContentStream, solutionPageContentStream); this.drawHorizontalLines(puzzlePageContentStream, solutionPageContentStream);
@ -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)) {

View file

@ -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();
} }

View file

@ -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());

View file

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

View file

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

View file

@ -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());
} }

View file

@ -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() {

View file

@ -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);

View file

@ -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();

View file

@ -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();

View file

@ -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) {

View file

@ -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)

View file

@ -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++) {

View file

@ -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) {

View file

@ -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);

View file

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

View file

@ -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);

View file

@ -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 assertThat(sut.digTo(Direction.LEFT)).isFalse();
.returns(false, v -> v.digTo(Direction.LEFT));
// 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

View file

@ -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);