diff --git a/pom.xml b/pom.xml index 980deec..10fd9be 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ ch.fritteli.a-maze-r maze-generator - 0.3.1-SNAPSHOT + 0.2.1 A-Maze-R, The Maze Generator. It is a library for generating mazes in various output formats. @@ -43,7 +43,7 @@ scm:git:https://gittr.ch/java/maze-generator.git scm:git:ssh://git@gittr.ch/java/maze-generator.git https://gittr.ch/java/maze-generator - HEAD + v0.2.1 @@ -74,11 +74,6 @@ io.vavr vavr - - com.google.guava - guava - 33.2.1-jre - org.apache.pdfbox pdfbox diff --git a/src/main/java/ch/fritteli/maze/generator/algorithm/AbstractMazeGeneratorAlgorithm.java b/src/main/java/ch/fritteli/maze/generator/algorithm/AbstractMazeGeneratorAlgorithm.java deleted file mode 100644 index 2037f76..0000000 --- a/src/main/java/ch/fritteli/maze/generator/algorithm/AbstractMazeGeneratorAlgorithm.java +++ /dev/null @@ -1,20 +0,0 @@ -package ch.fritteli.maze.generator.algorithm; - -import ch.fritteli.maze.generator.model.Maze; -import org.jetbrains.annotations.NotNull; - -import java.util.Random; - -public abstract class AbstractMazeGeneratorAlgorithm implements MazeGeneratorAlgorithm { - @NotNull - protected final Maze maze; - @NotNull - protected final Random random; - - - protected AbstractMazeGeneratorAlgorithm(@NotNull final Maze maze, @NotNull final String algorithmName) { - this.maze = maze; - this.random = new Random(maze.getRandomSeed()); - this.maze.setAlgorithm(algorithmName); - } -} diff --git a/src/main/java/ch/fritteli/maze/generator/algorithm/MazeGeneratorAlgorithm.java b/src/main/java/ch/fritteli/maze/generator/algorithm/MazeGeneratorAlgorithm.java deleted file mode 100644 index 0cc091a..0000000 --- a/src/main/java/ch/fritteli/maze/generator/algorithm/MazeGeneratorAlgorithm.java +++ /dev/null @@ -1,5 +0,0 @@ -package ch.fritteli.maze.generator.algorithm; - -public interface MazeGeneratorAlgorithm { - void run(); -} diff --git a/src/main/java/ch/fritteli/maze/generator/algorithm/RandomDepthFirst.java b/src/main/java/ch/fritteli/maze/generator/algorithm/RandomDepthFirst.java index eab53a9..2c1b3d2 100644 --- a/src/main/java/ch/fritteli/maze/generator/algorithm/RandomDepthFirst.java +++ b/src/main/java/ch/fritteli/maze/generator/algorithm/RandomDepthFirst.java @@ -6,17 +6,23 @@ import ch.fritteli.maze.generator.model.Position; import ch.fritteli.maze.generator.model.Tile; import io.vavr.control.Option; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.Deque; import java.util.LinkedList; +import java.util.Random; -public class RandomDepthFirst extends AbstractMazeGeneratorAlgorithm { +public class RandomDepthFirst { + + @NotNull + private final Maze maze; + @NotNull + private final Random random; @NotNull private final Deque positions = new LinkedList<>(); public RandomDepthFirst(@NotNull final Maze maze) { - super(maze, "Random Depth First"); + this.maze = maze; + this.random = new Random(maze.getRandomSeed()); } public void run() { @@ -27,11 +33,20 @@ public class RandomDepthFirst extends AbstractMazeGeneratorAlgorithm { private void preDig() { final Position end = this.maze.getEnd(); - final Direction direction = this.determineDirectionForDigging(end); + final Tile endTile = this.maze.getEndTile(); - if (direction != null) { - final Tile endTile = this.maze.getEndTile(); - this.digFrom(endTile, direction); + if (end.y() == 0) { + endTile.enableDiggingToOrFrom(Direction.TOP); + endTile.digFrom(Direction.TOP); + } else if (end.x() == 0) { + endTile.enableDiggingToOrFrom(Direction.LEFT); + endTile.digFrom(Direction.LEFT); + } else if (end.y() == this.maze.getHeight() - 1) { + endTile.enableDiggingToOrFrom(Direction.BOTTOM); + endTile.digFrom(Direction.BOTTOM); + } else if (end.x() == this.maze.getWidth() - 1) { + endTile.enableDiggingToOrFrom(Direction.RIGHT); + endTile.digFrom(Direction.RIGHT); } this.positions.push(end); @@ -70,38 +85,20 @@ public class RandomDepthFirst extends AbstractMazeGeneratorAlgorithm { private void postDig() { final Position start = this.maze.getStart(); + final Tile startTile = this.maze.getStartTile(); - final Direction direction = this.determineDirectionForDigging(start); - if (direction != null) { - final Tile startTile = this.maze.getStartTile(); - this.digTo(startTile, direction); + if (start.y() == 0) { + startTile.enableDiggingToOrFrom(Direction.TOP); + startTile.digTo(Direction.TOP); + } else if (start.x() == 0) { + startTile.enableDiggingToOrFrom(Direction.LEFT); + startTile.digTo(Direction.LEFT); + } else if (start.y() == this.maze.getHeight() - 1) { + startTile.enableDiggingToOrFrom(Direction.BOTTOM); + startTile.digTo(Direction.BOTTOM); + } else if (start.x() == this.maze.getWidth() - 1) { + startTile.enableDiggingToOrFrom(Direction.RIGHT); + startTile.digTo(Direction.RIGHT); } } - - @Nullable - private Direction determineDirectionForDigging(@NotNull final Position position) { - if (position.y() == 0) { - return Direction.TOP; - } - if (position.x() == 0) { - return Direction.LEFT; - } - if (position.y() == this.maze.getHeight() - 1) { - return Direction.BOTTOM; - } - if (position.x() == this.maze.getWidth() - 1) { - return Direction.RIGHT; - } - return null; - } - - private void digFrom(@NotNull final Tile tile, @NotNull final Direction direction) { - tile.enableDiggingToOrFrom(direction); - tile.digFrom(direction); - } - - private void digTo(@NotNull final Tile tile, @NotNull final Direction direction) { - tile.enableDiggingToOrFrom(direction); - tile.digTo(direction); - } } diff --git a/src/main/java/ch/fritteli/maze/generator/algorithm/wilson/MazeSolver.java b/src/main/java/ch/fritteli/maze/generator/algorithm/wilson/MazeSolver.java deleted file mode 100644 index f84a20c..0000000 --- a/src/main/java/ch/fritteli/maze/generator/algorithm/wilson/MazeSolver.java +++ /dev/null @@ -1,62 +0,0 @@ -package ch.fritteli.maze.generator.algorithm.wilson; - -import ch.fritteli.maze.generator.model.Direction; -import ch.fritteli.maze.generator.model.Maze; -import ch.fritteli.maze.generator.model.Position; -import org.jetbrains.annotations.NotNull; - -import java.util.EnumSet; -import java.util.Iterator; -import java.util.List; -import java.util.Stack; -import java.util.stream.Collectors; - -public class MazeSolver { - @NotNull - private final Maze maze; - - MazeSolver(@NotNull final Maze maze) { - this.maze = maze; - } - - void solve() { - final Direction directionToOuterWall = Wilson.getDirectionToOuterWall( - this.maze.getStart(), - this.maze.getWidth(), - this.maze.getHeight() - ); - - final List solution = this.getSolution(this.maze.getStart(), directionToOuterWall); - for (Position position : solution) { - this.maze.getTileAt(position).get().setSolution(); - } - } - - private List getSolution(@NotNull Position position, - @NotNull Direction forbidden) { - record PathElement(@NotNull Position position, - @NotNull EnumSet possibleDirections) { - } - final Stack solution = new Stack<>(); - final EnumSet directions = this.maze.getTileAt(position).get().getOpenDirections(); - directions.remove(forbidden); - PathElement head = new PathElement(position, directions); - solution.push(head); - while (!head.position.equals(this.maze.getEnd())) { - if (head.possibleDirections.isEmpty()) { - solution.pop(); - head = solution.peek(); - } else { - final Iterator iterator = head.possibleDirections.iterator(); - final Direction direction = iterator.next(); - iterator.remove(); - final Position next = head.position.move(direction); - final EnumSet openDirections = this.maze.getTileAt(next).get().getOpenDirections(); - openDirections.remove(direction.invert()); - head = new PathElement(next, openDirections); - solution.push(head); - } - } - return solution.stream().map(PathElement::position).collect(Collectors.toList()); - } -} diff --git a/src/main/java/ch/fritteli/maze/generator/algorithm/wilson/Path.java b/src/main/java/ch/fritteli/maze/generator/algorithm/wilson/Path.java deleted file mode 100644 index ab18ec2..0000000 --- a/src/main/java/ch/fritteli/maze/generator/algorithm/wilson/Path.java +++ /dev/null @@ -1,91 +0,0 @@ -package ch.fritteli.maze.generator.algorithm.wilson; - -import ch.fritteli.maze.generator.model.Direction; -import ch.fritteli.maze.generator.model.Position; -import io.vavr.collection.List; -import io.vavr.collection.Stream; -import io.vavr.collection.Traversable; -import io.vavr.control.Option; -import org.jetbrains.annotations.NotNull; - -import java.util.Random; - -class Path { - private final int width; - private final int height; - @NotNull - private List positions; - - Path(@NotNull final Position start, int width, int height) { - this.positions = List.of(start); - this.width = width; - this.height = height; - } - - @NotNull - Position growRandom(@NotNull final Random random) { - final Position position = this.nextRandomPosition(random); - if (this.contains(position)) { - this.removeLoopUpTo(position); - return this.growRandom(random); - } - this.positions = this.positions.prepend(position); - return position; - } - - @NotNull - List getPositions() { - return this.positions; - } - - @NotNull - Position getStart() { - return this.positions.last(); - } - - @NotNull - Traversable getMovesFromStart() { - return this.positions.reverse().sliding(2) - .flatMap(positions1 -> Option.when( - positions1.size() == 2, - // DEV-NOTE: .get() is safe here, because in the context of a path, there MUST be a direction - // from one position to the next. - () -> positions1.head().getDirectionTo(positions1.last()).get() - )); - } - - @NotNull - private Position nextRandomPosition(@NotNull final Random random) { - final Direction randomDirection = this.getRandomDirection(random); - final Position nextPosition = this.positions.head().move(randomDirection); - if (this.isWithinBounds(nextPosition) && !nextPosition.equals(this.positions.head())) { - return nextPosition; - } - return this.nextRandomPosition(random); - } - - private boolean isWithinBounds(@NotNull final Position position) { - return position.x() >= 0 && position.x() < this.width && position.y() >= 0 && position.y() < this.height; - } - - private boolean contains(@NotNull final Position position) { - return this.positions.contains(position); - } - - private void removeLoopUpTo(@NotNull final Position position) { - this.positions = this.positions.dropUntil(position::equals); - } - - @NotNull - private Direction getRandomDirection(@NotNull final Random random) { - final Direction[] array = Direction.values(); - return array[random.nextInt(array.length)]; - } - - @Override - public String toString() { - return Stream.ofAll(this.positions) - .map(position -> "(%s,%s)".formatted(position.x(), position.y())) - .mkString("Path[", "->", "]"); - } -} diff --git a/src/main/java/ch/fritteli/maze/generator/algorithm/wilson/PathsBuilder.java b/src/main/java/ch/fritteli/maze/generator/algorithm/wilson/PathsBuilder.java deleted file mode 100644 index 098ed81..0000000 --- a/src/main/java/ch/fritteli/maze/generator/algorithm/wilson/PathsBuilder.java +++ /dev/null @@ -1,131 +0,0 @@ -package ch.fritteli.maze.generator.algorithm.wilson; - -import ch.fritteli.maze.generator.model.Maze; -import ch.fritteli.maze.generator.model.Position; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; -import io.vavr.Tuple; -import io.vavr.collection.Stream; -import io.vavr.collection.Traversable; -import io.vavr.control.Option; -import org.jetbrains.annotations.NotNull; - -import java.util.Random; - -/** - * This class will build paths such that in the end all fields of the maze are covered by exactly one path. - */ -class PathsBuilder { - private final int width; - private final int height; - @NotNull - private final Random random; - @NotNull - private final Multimap availablePositions; - - PathsBuilder(@NotNull final Maze maze, - @NotNull final Random random) { - this.width = maze.getWidth(); - this.height = maze.getHeight(); - this.random = random; - this.availablePositions = HashMultimap.create(this.width, this.height); - - // Initialize the available positions. - for (int x = 0; x < this.width; x++) { - for (int y = 0; y < this.height; y++) { - this.availablePositions.put(x, y); - } - } - } - - /** - * Create all the paths such that the maze will be completely filled and every field of it will be covered by - * exactly one path. - * - * @return A {@link Traversable} of generated {@link Path Paths}. - */ - @NotNull - Traversable buildPaths() { - this.initializeWithRandomStartingPosition(); - - return Stream.unfoldLeft( - this, - builder -> builder.buildPath() - .map(path -> { - builder.setPartOfMaze(path); - return Tuple.of(builder, path); - }) - ); - } - - private void initializeWithRandomStartingPosition() { - this.popRandomPosition(); - } - - /** - * Creates one new path, if possible. If the maze is already filled, {@link io.vavr.control.Option.None} is - * returned. - * - * @return An {@link Option} of a new {@link Path} instance. - */ - @NotNull - private Option buildPath() { - return this.initializeNewPath() - .map(this::growPath); - } - - @NotNull - private Option initializeNewPath() { - return this.popRandomPosition() - .map(position -> new Path(position, this.width, this.height)); - } - - /** - * Randomly grow the {@code path} until it reaches a field that is part of the maze and return it. The resulting - * path will contain no loops. - * - * @param path The {@link Path} to grow. - * @return The final {@link Path} that reaches the maze. - */ - @NotNull - private Path growPath(@NotNull final Path path) { - Position lastPosition; - do { - lastPosition = path.growRandom(this.random); - } while (this.isNotPartOfMaze(lastPosition)); - return path; - } - - private boolean isNotPartOfMaze(@NotNull final Position position) { - return this.availablePositions.containsEntry(position.x(), position.y()); - } - - private void setPartOfMaze(@NotNull final Position position) { - this.availablePositions.remove(position.x(), position.y()); - } - - private void setPartOfMaze(@NotNull final Path path) { - path.getPositions().forEach(this::setPartOfMaze); - } - - /** - * Finds a random {@link Position}, that is not yet part of the maze, marks it as being part of the maze and returns - * it. If no position is available, {@link io.vavr.control.Option.None} is returned. - * - * @return An available position or {@link io.vavr.control.Option.None}. - */ - @NotNull - private Option popRandomPosition() { - if (this.availablePositions.isEmpty()) { - return Option.none(); - } - - final Integer[] keys = this.availablePositions.keySet().toArray(Integer[]::new); - final int key = keys[this.random.nextInt(keys.length)]; - final Integer[] values = this.availablePositions.get(key).toArray(Integer[]::new); - final int value = values[this.random.nextInt(values.length)]; - - this.availablePositions.remove(key, value); - return Option.some(new Position(key, value)); - } -} diff --git a/src/main/java/ch/fritteli/maze/generator/algorithm/wilson/Wilson.java b/src/main/java/ch/fritteli/maze/generator/algorithm/wilson/Wilson.java deleted file mode 100644 index 1587e6e..0000000 --- a/src/main/java/ch/fritteli/maze/generator/algorithm/wilson/Wilson.java +++ /dev/null @@ -1,96 +0,0 @@ -package ch.fritteli.maze.generator.algorithm.wilson; - -import ch.fritteli.maze.generator.algorithm.AbstractMazeGeneratorAlgorithm; -import ch.fritteli.maze.generator.model.Direction; -import ch.fritteli.maze.generator.model.Maze; -import ch.fritteli.maze.generator.model.Position; -import ch.fritteli.maze.generator.model.Tile; -import io.vavr.collection.Traversable; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * An implementation of Wilson's Algorithm. - * In short: - *
    - *
  1. Pick random location, add to maze
  2. - *
  3. While locations that are not part of the maze exist, loop: - *
      - *
    1. Pick random location that's not part of the maze
    2. - *
    3. Randomly walk from this location, until ... - *
        - *
      • ... either you hit the current path, forming a loop. Then remove the entire loop and continue - * walking.
      • - *
      • ... or you hit a position that is part of the maze. Then add the path to the maze and start the next - * walk.
      • - *
    4. - *
  4. - *
- */ -public class Wilson extends AbstractMazeGeneratorAlgorithm { - - public Wilson(@NotNull final Maze maze) { - super(maze, "Wilson"); - } - - @Nullable - static Direction getDirectionToOuterWall(@NotNull final Position position, - final int width, - final int height) { - if (position.y() == 0) { - return Direction.TOP; - } - if (position.y() == height - 1) { - return Direction.BOTTOM; - } - if (position.x() == 0) { - return Direction.LEFT; - } - if (position.x() == width - 1) { - return Direction.RIGHT; - } - return null; - } - - @Override - public void run() { - final Traversable paths = new PathsBuilder(this.maze, this.random) - .buildPaths(); - - this.applyPathsToMaze(paths); - } - - private void applyPathsToMaze(@NotNull final Traversable paths) { - this.openStartAndEndWalls(); - paths.forEach(path -> path.getMovesFromStart() - .foldLeft( - path.getStart(), - (position, direction) -> { - this.maze.getTileAt(position).get() - .digTo(direction); - final Position next = position.move(direction); - this.maze.getTileAt(next).get() - .digTo(direction.invert()); - return next; - })); - - final MazeSolver solver = new MazeSolver(this.maze); - solver.solve(); - } - - private void openStartAndEndWalls() { - this.openWall(this.maze.getStart(), this.maze.getStartTile()); - this.openWall(this.maze.getEnd(), this.maze.getEndTile()); - } - - private void openWall(@NotNull final Position position, @NotNull final Tile tile) { - final Direction direction = this.getDirectionToOuterWall(position); - tile.enableDiggingToOrFrom(direction); - tile.digTo(direction); - } - - @Nullable - private Direction getDirectionToOuterWall(@NotNull final Position position) { - return getDirectionToOuterWall(position, this.maze.getWidth(), this.maze.getHeight()); - } -} diff --git a/src/main/java/ch/fritteli/maze/generator/model/Direction.java b/src/main/java/ch/fritteli/maze/generator/model/Direction.java index 75ee1c4..e50ba6c 100644 --- a/src/main/java/ch/fritteli/maze/generator/model/Direction.java +++ b/src/main/java/ch/fritteli/maze/generator/model/Direction.java @@ -1,14 +1,11 @@ package ch.fritteli.maze.generator.model; -import org.jetbrains.annotations.NotNull; - public enum Direction { TOP, RIGHT, BOTTOM, LEFT; - @NotNull public Direction invert() { return switch (this) { case TOP -> BOTTOM; diff --git a/src/main/java/ch/fritteli/maze/generator/model/Maze.java b/src/main/java/ch/fritteli/maze/generator/model/Maze.java index 633e1b3..14d7af6 100644 --- a/src/main/java/ch/fritteli/maze/generator/model/Maze.java +++ b/src/main/java/ch/fritteli/maze/generator/model/Maze.java @@ -3,7 +3,6 @@ package ch.fritteli.maze.generator.model; import io.vavr.control.Option; import lombok.EqualsAndHashCode; import lombok.Getter; -import lombok.Setter; import lombok.ToString; import org.jetbrains.annotations.NotNull; @@ -22,9 +21,6 @@ public class Maze { private final Position start; @Getter private final Position end; - @Getter - @Setter - private String algorithm; public Maze(final int width, final int height) { this(width, height, System.nanoTime()); @@ -38,11 +34,7 @@ public class Maze { 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, - @NotNull final Position start, - @NotNull 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) { throw new IllegalArgumentException("width and height must be >1"); } @@ -79,12 +71,7 @@ public class Maze { /** * INTERNAL API. Exists only for deserialization. Not to be called from user code. */ - private Maze(@NotNull final Tile[][] field, - final int width, - final int height, - @NotNull final Position start, - @NotNull 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.width = width; this.height = height; @@ -93,25 +80,6 @@ public class Maze { this.end = end; } - /** - * INTERNAL API. Exists only for deserialization. Not to be called from user code. - */ - private Maze(@NotNull final Tile[][] field, - final int width, - final int height, - @NotNull final Position start, - @NotNull final Position end, - final long randomSeed, - @NotNull final String algorithm) { - this.field = field; - this.width = width; - this.height = height; - this.randomSeed = randomSeed; - this.algorithm = algorithm; - this.start = start; - this.end = end; - } - @NotNull public Option getTileAt(@NotNull final Position position) { return this.getTileAt(position.x(), position.y()); diff --git a/src/main/java/ch/fritteli/maze/generator/model/Position.java b/src/main/java/ch/fritteli/maze/generator/model/Position.java index a6fa788..4b91543 100644 --- a/src/main/java/ch/fritteli/maze/generator/model/Position.java +++ b/src/main/java/ch/fritteli/maze/generator/model/Position.java @@ -1,12 +1,10 @@ package ch.fritteli.maze.generator.model; -import io.vavr.control.Option; import lombok.With; import org.jetbrains.annotations.NotNull; @With public record Position(int x, int y) { - @NotNull public Position move(@NotNull final Direction direction) { return switch (direction) { case BOTTOM -> this.withY(this.y + 1); @@ -15,20 +13,4 @@ public record Position(int x, int y) { case TOP -> this.withY(this.y - 1); }; } - - @NotNull - public Option getDirectionTo(@NotNull final Position position) { - final int xDiff = position.x - this.x; - final int yDiff = position.y - this.y; - return switch (xDiff) { - case -1 -> Option.when(yDiff == 0, Direction.LEFT); - case 0 -> switch (yDiff) { - case -1 -> Option.some(Direction.TOP); - case 1 -> Option.some(Direction.BOTTOM); - default -> Option.none(); - }; - case 1 -> Option.when(yDiff == 0, Direction.RIGHT); - default -> Option.none(); - }; - } } diff --git a/src/main/java/ch/fritteli/maze/generator/model/Tile.java b/src/main/java/ch/fritteli/maze/generator/model/Tile.java index b4428f1..c6f9367 100644 --- a/src/main/java/ch/fritteli/maze/generator/model/Tile.java +++ b/src/main/java/ch/fritteli/maze/generator/model/Tile.java @@ -1,6 +1,6 @@ package ch.fritteli.maze.generator.model; -import io.vavr.collection.Vector; +import io.vavr.collection.Stream; import io.vavr.control.Option; import lombok.AccessLevel; import lombok.EqualsAndHashCode; @@ -17,7 +17,6 @@ import java.util.Random; @ToString public class Tile { final Walls walls = new Walls(); - @EqualsAndHashCode.Exclude boolean visited = false; @Getter boolean solution = false; @@ -36,18 +35,18 @@ public class Tile { private Tile(@NotNull final EnumSet walls, final boolean solution) { for (final Direction direction : walls) { this.walls.set(direction); - this.walls.seal(direction); + this.walls.harden(direction); } this.visited = true; this.solution = solution; } public void preventDiggingToOrFrom(@NotNull final Direction direction) { - this.walls.seal(direction); + this.walls.harden(direction); } public void enableDiggingToOrFrom(@NotNull final Direction direction) { - this.walls.unseal(direction); + this.walls.unharden(direction); } public boolean digFrom(@NotNull final Direction direction) { @@ -66,23 +65,13 @@ public class Tile { this.walls.set(direction); } - @NotNull - public EnumSet getOpenDirections() { - return this.walls.getOpen(); - } - - @NotNull public Option getRandomAvailableDirection(@NotNull final Random random) { - final EnumSet availableDirections = this.walls.getUnsealedSet(); + final Stream availableDirections = this.walls.getUnhardenedSet(); if (availableDirections.isEmpty()) { return Option.none(); } - if (availableDirections.size() == 1) { - return Option.some(availableDirections.iterator().next()); - } - final Vector directions = Vector.ofAll(availableDirections); - final int index = random.nextInt(directions.size()); - return Option.of(directions.get(index)); + final int index = random.nextInt(availableDirections.length()); + return Option.of(availableDirections.get(index)); } public boolean hasWallAt(@NotNull final Direction direction) { diff --git a/src/main/java/ch/fritteli/maze/generator/model/Walls.java b/src/main/java/ch/fritteli/maze/generator/model/Walls.java index 4a899c7..f77ceaa 100644 --- a/src/main/java/ch/fritteli/maze/generator/model/Walls.java +++ b/src/main/java/ch/fritteli/maze/generator/model/Walls.java @@ -1,17 +1,21 @@ package ch.fritteli.maze.generator.model; +import io.vavr.collection.Stream; import lombok.EqualsAndHashCode; import lombok.ToString; import org.jetbrains.annotations.NotNull; import java.util.EnumSet; +import java.util.HashSet; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; @EqualsAndHashCode @ToString public class Walls { - private final EnumSet directions = EnumSet.noneOf(Direction.class); - @EqualsAndHashCode.Exclude - private final EnumSet sealed = EnumSet.noneOf(Direction.class); + private final SortedSet directions = new TreeSet<>(); + private final Set hardened = new HashSet<>(); public void set(@NotNull final Direction direction) { this.directions.add(direction); @@ -22,7 +26,7 @@ public class Walls { } public boolean clear(@NotNull final Direction direction) { - if (this.sealed.contains(direction)) { + if (this.hardened.contains(direction)) { return false; } return this.directions.remove(direction); @@ -32,26 +36,19 @@ public class Walls { return this.directions.contains(direction); } - @NotNull - public EnumSet getUnsealedSet() { - final EnumSet result = EnumSet.copyOf(this.directions); - result.removeAll(this.sealed); - return result; + public Stream getUnhardenedSet() { + return Stream.ofAll(this.directions) + .removeAll(this.hardened); } - public void seal(@NotNull final Direction direction) { + public void harden(@NotNull final Direction direction) { if (!this.directions.contains(direction)) { - throw new IllegalStateException("Trying to seal cleared Direction: " + direction); + throw new IllegalStateException("Trying to harden cleared Direction: " + direction); } - this.sealed.add(direction); + this.hardened.add(direction); } - public void unseal(@NotNull final Direction direction) { - this.sealed.remove(direction); - } - - @NotNull - public EnumSet getOpen() { - return EnumSet.complementOf(this.directions); + public void unharden(@NotNull final Direction direction) { + this.hardened.remove(direction); } } diff --git a/src/main/java/ch/fritteli/maze/generator/renderer/html/HTMLRenderer.java b/src/main/java/ch/fritteli/maze/generator/renderer/html/HTMLRenderer.java index a2de552..1242ac3 100644 --- a/src/main/java/ch/fritteli/maze/generator/renderer/html/HTMLRenderer.java +++ b/src/main/java/ch/fritteli/maze/generator/renderer/html/HTMLRenderer.java @@ -6,109 +6,108 @@ import org.jetbrains.annotations.NotNull; public class HTMLRenderer implements Renderer { - private static final String POSTAMBLE = """ - """; + private static final String POSTAMBLE = ""; private HTMLRenderer() { } @@ -136,42 +135,33 @@ public class HTMLRenderer implements Renderer { } private String getPreamble(@NotNull final Maze maze) { - return """ - - - - Maze %dx%d, ID %d, Algorithm %s - - - - - - - """ - .formatted( - maze.getWidth(), - maze.getHeight(), - maze.getRandomSeed(), - maze.getAlgorithm() - ); + return "" + + "" + + "Maze " + maze.getWidth() + "x" + maze.getHeight() + ", ID " + maze.getRandomSeed() + "" + + "" + + "" + + "" + + "" + + "" + + ""; } } diff --git a/src/main/java/ch/fritteli/maze/generator/renderer/json/Generator.java b/src/main/java/ch/fritteli/maze/generator/renderer/json/Generator.java index 3970732..c08ed1b 100644 --- a/src/main/java/ch/fritteli/maze/generator/renderer/json/Generator.java +++ b/src/main/java/ch/fritteli/maze/generator/renderer/json/Generator.java @@ -23,7 +23,6 @@ class Generator { JsonMaze generate() { final JsonMaze result = new JsonMaze(); result.setId(String.valueOf(this.maze.getRandomSeed())); - result.setAlgorithm(this.maze.getAlgorithm()); result.setWidth(this.maze.getWidth()); result.setHeight(this.maze.getHeight()); final List> rows = new ArrayList<>(); diff --git a/src/main/java/ch/fritteli/maze/generator/renderer/pdf/Generator.java b/src/main/java/ch/fritteli/maze/generator/renderer/pdf/Generator.java index a4a103d..0de27a9 100644 --- a/src/main/java/ch/fritteli/maze/generator/renderer/pdf/Generator.java +++ b/src/main/java/ch/fritteli/maze/generator/renderer/pdf/Generator.java @@ -34,7 +34,7 @@ class Generator { final PDDocument pdDocument = new PDDocument(); final PDDocumentInformation info = new PDDocumentInformation(); - info.setTitle("Maze %sx%s, ID %s (%s)".formatted(this.maze.getWidth(), this.maze.getHeight(), this.maze.getRandomSeed(), this.maze.getAlgorithm())); + info.setTitle("Maze %sx%s, ID %s".formatted(this.maze.getWidth(), this.maze.getHeight(), this.maze.getRandomSeed())); pdDocument.setDocumentInformation(info); final PDPage puzzlePage = new PDPage(new PDRectangle(pageWidth, pageHeight)); final PDPage solutionPage = new PDPage(new PDRectangle(pageWidth, pageHeight)); @@ -233,7 +233,7 @@ class Generator { if (position.equals(previousPosition)) { continue; } - if (tileAtPosition.exists(Tile::isSolution)) { + if (tileAtPosition.map(Tile::isSolution).getOrElse(false)) { return position; } } diff --git a/src/main/java/ch/fritteli/maze/generator/serialization/AbstractMazeInputStream.java b/src/main/java/ch/fritteli/maze/generator/serialization/AbstractMazeInputStream.java index 7e4ba88..3803ef1 100644 --- a/src/main/java/ch/fritteli/maze/generator/serialization/AbstractMazeInputStream.java +++ b/src/main/java/ch/fritteli/maze/generator/serialization/AbstractMazeInputStream.java @@ -4,7 +4,6 @@ import ch.fritteli.maze.generator.model.Maze; import org.jetbrains.annotations.NotNull; import java.io.ByteArrayInputStream; -import java.io.IOException; public abstract class AbstractMazeInputStream extends ByteArrayInputStream { @@ -15,7 +14,7 @@ public abstract class AbstractMazeInputStream extends ByteArrayInputStream { public abstract void checkHeader(); @NotNull - public abstract Maze readMazeData() throws IOException; + public abstract Maze readMazeData(); public byte readByte() { final int read = this.read(); @@ -26,10 +25,6 @@ public abstract class AbstractMazeInputStream extends ByteArrayInputStream { return (byte) read; } - public int readByteAsInt() { - return 0xff & this.readByte(); - } - public int readInt() { int result = 0; result |= (0xff & this.readByte()) << 24; diff --git a/src/main/java/ch/fritteli/maze/generator/serialization/CommonTileHandler.java b/src/main/java/ch/fritteli/maze/generator/serialization/CommonTileHandler.java deleted file mode 100644 index b4051c8..0000000 --- a/src/main/java/ch/fritteli/maze/generator/serialization/CommonTileHandler.java +++ /dev/null @@ -1,100 +0,0 @@ -package ch.fritteli.maze.generator.serialization; - -import ch.fritteli.maze.generator.model.Direction; -import ch.fritteli.maze.generator.model.Tile; -import lombok.experimental.UtilityClass; -import org.jetbrains.annotations.NotNull; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.EnumSet; - - -/** - * Binary format description of a {@link Tile}.
- * A tile is stored in one byte: - *
    - *
  • bits 0..2: always 0
  • - *
  • bit 3: 1=solution, 0=not solution
  • - *
  • bits 4..7: encode walls
  • - *
- * The values for bits 4..7 are as follows: - *
- * decimal hex bin  border
- *       0   0 0000 no border
- *       1   1 0001 top
- *       2   2 0010 right
- *       3   3 0011 top+right
- *       4   4 0100 bottom
- *       5   5 0101 top+bottom
- *       6   6 0110 right+bottom
- *       7   7 0111 top+right+bottom
- *       8   8 1000 left
- *       9   9 1001 top+left
- *      10   a 1010 right+left
- *      11   b 1011 top+right+left
- *      12   c 1100 bottom+left
- *      13   d 1101 top+bottom+left
- *      14   e 1110 right+bottom+left
- *      15   f 1111 top+right+bottom+left
- * 
- */ -@UtilityClass -public class CommonTileHandler { - private final byte TOP_BIT = 0b0000_0001; - private final byte RIGHT_BIT = 0b0000_0010; - private final byte BOTTOM_BIT = 0b0000_0100; - private final byte LEFT_BIT = 0b0000_1000; - private final byte SOLUTION_BIT = 0b0001_0000; - - public byte getBitmaskForTile(@NotNull final Tile tile) { - byte bitmask = 0; - if (tile.hasWallAt(Direction.TOP)) { - bitmask |= TOP_BIT; - } - if (tile.hasWallAt(Direction.RIGHT)) { - bitmask |= RIGHT_BIT; - } - if (tile.hasWallAt(Direction.BOTTOM)) { - bitmask |= BOTTOM_BIT; - } - if (tile.hasWallAt((Direction.LEFT))) { - bitmask |= LEFT_BIT; - } - if (tile.isSolution()) { - bitmask |= SOLUTION_BIT; - } - return bitmask; - } - - @NotNull - public Tile getTileForBitmask(final byte bitmask) { - final EnumSet walls = EnumSet.noneOf(Direction.class); - if ((bitmask & TOP_BIT) == TOP_BIT) { - walls.add(Direction.TOP); - } - if ((bitmask & RIGHT_BIT) == RIGHT_BIT) { - walls.add(Direction.RIGHT); - } - if ((bitmask & BOTTOM_BIT) == BOTTOM_BIT) { - walls.add(Direction.BOTTOM); - } - if ((bitmask & LEFT_BIT) == LEFT_BIT) { - walls.add(Direction.LEFT); - } - final boolean solution = (bitmask & SOLUTION_BIT) == SOLUTION_BIT; - return createTile(walls, solution); - } - - @NotNull - private Tile createTile(@NotNull final EnumSet walls, boolean solution) { - try { - final Constructor constructor = Tile.class.getDeclaredConstructor(EnumSet.class, Boolean.TYPE); - constructor.setAccessible(true); - return constructor.newInstance(walls, solution); - } catch (@NotNull final NoSuchMethodException | InstantiationException | IllegalAccessException | - InvocationTargetException e) { - throw new RuntimeException("Can not deserialize Tile from maze data.", e); - } - } -} diff --git a/src/main/java/ch/fritteli/maze/generator/serialization/MazeConstants.java b/src/main/java/ch/fritteli/maze/generator/serialization/MazeConstants.java deleted file mode 100644 index 4b9c200..0000000 --- a/src/main/java/ch/fritteli/maze/generator/serialization/MazeConstants.java +++ /dev/null @@ -1,9 +0,0 @@ -package ch.fritteli.maze.generator.serialization; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class MazeConstants { - public final byte MAGIC_BYTE_1 = 0x1a; - public final byte MAGIC_BYTE_2 = (byte) 0xb1; -} diff --git a/src/main/java/ch/fritteli/maze/generator/serialization/v1/MazeInputStreamV1.java b/src/main/java/ch/fritteli/maze/generator/serialization/v1/MazeInputStreamV1.java index 2532787..3495e5b 100644 --- a/src/main/java/ch/fritteli/maze/generator/serialization/v1/MazeInputStreamV1.java +++ b/src/main/java/ch/fritteli/maze/generator/serialization/v1/MazeInputStreamV1.java @@ -3,8 +3,6 @@ package ch.fritteli.maze.generator.serialization.v1; import ch.fritteli.maze.generator.model.Maze; import ch.fritteli.maze.generator.model.Tile; import ch.fritteli.maze.generator.serialization.AbstractMazeInputStream; -import ch.fritteli.maze.generator.serialization.CommonTileHandler; -import ch.fritteli.maze.generator.serialization.MazeConstants; import org.jetbrains.annotations.NotNull; public class MazeInputStreamV1 extends AbstractMazeInputStream { @@ -15,15 +13,12 @@ public class MazeInputStreamV1 extends AbstractMazeInputStream { @Override public void checkHeader() { - // 00 0x1a magic - // 01 0xb1 magic - // 02 0x01 version final byte magic1 = this.readByte(); - if (magic1 != MazeConstants.MAGIC_BYTE_1) { + if (magic1 != SerializerDeserializerV1.MAGIC_BYTE_1) { throw new IllegalArgumentException("Invalid maze data."); } final byte magic2 = this.readByte(); - if (magic2 != MazeConstants.MAGIC_BYTE_2) { + if (magic2 != SerializerDeserializerV1.MAGIC_BYTE_2) { throw new IllegalArgumentException("Invalid maze data."); } final int version = this.readByte(); @@ -35,10 +30,6 @@ public class MazeInputStreamV1 extends AbstractMazeInputStream { @NotNull @Override public Maze readMazeData() { - // 03..10 random seed number (long) - // 11..14 width (int) - // 15..18 height (int) - // 19.. tiles final long randomSeed = this.readLong(); final int width = this.readInt(); final int height = this.readInt(); @@ -51,7 +42,7 @@ public class MazeInputStreamV1 extends AbstractMazeInputStream { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { final byte bitmask = this.readByte(); - tiles[x][y] = CommonTileHandler.getTileForBitmask(bitmask); + tiles[x][y] = SerializerDeserializerV1.getTileForBitmask(bitmask); } } diff --git a/src/main/java/ch/fritteli/maze/generator/serialization/v1/MazeOutputStreamV1.java b/src/main/java/ch/fritteli/maze/generator/serialization/v1/MazeOutputStreamV1.java index 7085b04..62ecf70 100644 --- a/src/main/java/ch/fritteli/maze/generator/serialization/v1/MazeOutputStreamV1.java +++ b/src/main/java/ch/fritteli/maze/generator/serialization/v1/MazeOutputStreamV1.java @@ -3,28 +3,19 @@ package ch.fritteli.maze.generator.serialization.v1; import ch.fritteli.maze.generator.model.Maze; import ch.fritteli.maze.generator.model.Tile; import ch.fritteli.maze.generator.serialization.AbstractMazeOutputStream; -import ch.fritteli.maze.generator.serialization.CommonTileHandler; -import ch.fritteli.maze.generator.serialization.MazeConstants; import org.jetbrains.annotations.NotNull; public class MazeOutputStreamV1 extends AbstractMazeOutputStream { @Override public void writeHeader() { - // 00 0x1a magic - // 01 0xb1 magic - // 02 0x02 version - this.writeByte(MazeConstants.MAGIC_BYTE_1); - this.writeByte(MazeConstants.MAGIC_BYTE_2); + this.writeByte(SerializerDeserializerV1.MAGIC_BYTE_1); + this.writeByte(SerializerDeserializerV1.MAGIC_BYTE_2); this.writeByte(SerializerDeserializerV1.VERSION_BYTE); } @Override public void writeMazeData(@NotNull final Maze maze) { - // 03..10 random seed number (long) - // 11..14 width (int) - // 15..18 height (int) - // 19.. tiles final long randomSeed = maze.getRandomSeed(); final int width = maze.getWidth(); final int height = maze.getHeight(); @@ -36,7 +27,7 @@ public class MazeOutputStreamV1 extends AbstractMazeOutputStream { for (int x = 0; x < width; x++) { // We .get() it, because we want to crash hard if it is not available. final Tile tile = maze.getTileAt(x, y).get(); - final byte bitmask = CommonTileHandler.getBitmaskForTile(tile); + final byte bitmask = SerializerDeserializerV1.getBitmaskForTile(tile); this.writeByte(bitmask); } } diff --git a/src/main/java/ch/fritteli/maze/generator/serialization/v1/SerializerDeserializerV1.java b/src/main/java/ch/fritteli/maze/generator/serialization/v1/SerializerDeserializerV1.java index 9f4a561..24f7358 100644 --- a/src/main/java/ch/fritteli/maze/generator/serialization/v1/SerializerDeserializerV1.java +++ b/src/main/java/ch/fritteli/maze/generator/serialization/v1/SerializerDeserializerV1.java @@ -1,5 +1,6 @@ package ch.fritteli.maze.generator.serialization.v1; +import ch.fritteli.maze.generator.model.Direction; import ch.fritteli.maze.generator.model.Maze; import ch.fritteli.maze.generator.model.Tile; import lombok.experimental.UtilityClass; @@ -7,24 +8,53 @@ import org.jetbrains.annotations.NotNull; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.util.EnumSet; /** - * Header bytes are: + *
+ * decimal hex bin  border
+ *       0   0 0000 no border
+ *       1   1 0001 top
+ *       2   2 0010 right
+ *       3   3 0011 top+right
+ *       4   4 0100 bottom
+ *       5   5 0101 top+bottom
+ *       6   6 0110 right+bottom
+ *       7   7 0111 top+right+bottom
+ *       8   8 1000 left
+ *       9   9 1001 top+left
+ *      10   a 1010 right+left
+ *      11   b 1011 top+right+left
+ *      12   c 1100 bottom+left
+ *      13   d 1101 top+bottom+left
+ *      14   e 1110 right+bottom+left
+ *      15   f 1111 top+right+bottom+left
+ * 
+ * ==> bits 0..2: always 0; bit 3: 1=solution, 0=not solution; bits 4..7: encode walls + * ==> first bytes are: *
  *   byte  hex meaning
  *     00 0x1a magic
  *     01 0xb1 magic
  *     02 0x01 version (0x00 -> dev, 0x01 -> stable)
- * 03..10      random seed number (long)
- * 11..14      width (int)
- * 15..18      height (int)
+ * 03..06      width (int)
+ * 07..10      height (int)
+ * 11..18      random seed number (long)
  * 19..        tiles
  * 
* Extraneous space (poss. last nibble) is ignored. */ @UtilityClass public class SerializerDeserializerV1 { - public final byte VERSION_BYTE = 0x01; + final byte MAGIC_BYTE_1 = 0x1a; + final byte MAGIC_BYTE_2 = (byte) 0xb1; + final byte VERSION_BYTE = 0x01; + + private final byte TOP_BIT = 0b0000_0001; + private final byte RIGHT_BIT = 0b0000_0010; + private final byte BOTTOM_BIT = 0b0000_0100; + private final byte LEFT_BIT = 0b0000_1000; + private final byte SOLUTION_BIT = 0b0001_0000; /** * Serializes the {@code maze} into a byte array. @@ -64,4 +94,55 @@ public class SerializerDeserializerV1 { throw new RuntimeException("Can not deserialize Maze from maze data.", e); } } + + @NotNull + private Tile createTile(@NotNull final EnumSet walls, boolean solution) { + try { + final Constructor constructor = Tile.class.getDeclaredConstructor(EnumSet.class, Boolean.TYPE); + constructor.setAccessible(true); + return constructor.newInstance(walls, solution); + } catch (@NotNull final NoSuchMethodException | InstantiationException | IllegalAccessException | + InvocationTargetException e) { + throw new RuntimeException("Can not deserialize Tile from maze data.", e); + } + } + + byte getBitmaskForTile(@NotNull final Tile tile) { + byte bitmask = 0; + if (tile.hasWallAt(Direction.TOP)) { + bitmask |= TOP_BIT; + } + if (tile.hasWallAt(Direction.RIGHT)) { + bitmask |= RIGHT_BIT; + } + if (tile.hasWallAt(Direction.BOTTOM)) { + bitmask |= BOTTOM_BIT; + } + if (tile.hasWallAt((Direction.LEFT))) { + bitmask |= LEFT_BIT; + } + if (tile.isSolution()) { + bitmask |= SOLUTION_BIT; + } + return bitmask; + } + + @NotNull + Tile getTileForBitmask(final byte bitmask) { + final EnumSet walls = EnumSet.noneOf(Direction.class); + if ((bitmask & TOP_BIT) == TOP_BIT) { + walls.add(Direction.TOP); + } + if ((bitmask & RIGHT_BIT) == RIGHT_BIT) { + walls.add(Direction.RIGHT); + } + if ((bitmask & BOTTOM_BIT) == BOTTOM_BIT) { + walls.add(Direction.BOTTOM); + } + if ((bitmask & LEFT_BIT) == LEFT_BIT) { + walls.add(Direction.LEFT); + } + final boolean solution = (bitmask & SOLUTION_BIT) == SOLUTION_BIT; + return createTile(walls, solution); + } } diff --git a/src/main/java/ch/fritteli/maze/generator/serialization/v2/MazeInputStreamV2.java b/src/main/java/ch/fritteli/maze/generator/serialization/v2/MazeInputStreamV2.java index 036d636..de982a6 100644 --- a/src/main/java/ch/fritteli/maze/generator/serialization/v2/MazeInputStreamV2.java +++ b/src/main/java/ch/fritteli/maze/generator/serialization/v2/MazeInputStreamV2.java @@ -4,8 +4,6 @@ import ch.fritteli.maze.generator.model.Maze; import ch.fritteli.maze.generator.model.Position; import ch.fritteli.maze.generator.model.Tile; import ch.fritteli.maze.generator.serialization.AbstractMazeInputStream; -import ch.fritteli.maze.generator.serialization.CommonTileHandler; -import ch.fritteli.maze.generator.serialization.MazeConstants; import org.jetbrains.annotations.NotNull; public class MazeInputStreamV2 extends AbstractMazeInputStream { @@ -20,11 +18,11 @@ public class MazeInputStreamV2 extends AbstractMazeInputStream { // 01 0xb1 magic // 02 0x02 version final byte magic1 = this.readByte(); - if (magic1 != MazeConstants.MAGIC_BYTE_1) { + if (magic1 != SerializerDeserializerV2.MAGIC_BYTE_1) { throw new IllegalArgumentException("Invalid maze data."); } final byte magic2 = this.readByte(); - if (magic2 != MazeConstants.MAGIC_BYTE_2) { + if (magic2 != SerializerDeserializerV2.MAGIC_BYTE_2) { throw new IllegalArgumentException("Invalid maze data."); } final int version = this.readByte(); @@ -60,7 +58,7 @@ public class MazeInputStreamV2 extends AbstractMazeInputStream { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { final byte bitmask = this.readByte(); - tiles[x][y] = CommonTileHandler.getTileForBitmask(bitmask); + tiles[x][y] = SerializerDeserializerV2.getTileForBitmask(bitmask); } } diff --git a/src/main/java/ch/fritteli/maze/generator/serialization/v2/MazeOutputStreamV2.java b/src/main/java/ch/fritteli/maze/generator/serialization/v2/MazeOutputStreamV2.java index 1cb8f78..b2305fb 100644 --- a/src/main/java/ch/fritteli/maze/generator/serialization/v2/MazeOutputStreamV2.java +++ b/src/main/java/ch/fritteli/maze/generator/serialization/v2/MazeOutputStreamV2.java @@ -4,8 +4,6 @@ import ch.fritteli.maze.generator.model.Maze; import ch.fritteli.maze.generator.model.Position; import ch.fritteli.maze.generator.model.Tile; import ch.fritteli.maze.generator.serialization.AbstractMazeOutputStream; -import ch.fritteli.maze.generator.serialization.CommonTileHandler; -import ch.fritteli.maze.generator.serialization.MazeConstants; import org.jetbrains.annotations.NotNull; public class MazeOutputStreamV2 extends AbstractMazeOutputStream { @@ -15,8 +13,8 @@ public class MazeOutputStreamV2 extends AbstractMazeOutputStream { // 00 0x1a magic // 01 0xb1 magic // 02 0x02 version - this.writeByte(MazeConstants.MAGIC_BYTE_1); - this.writeByte(MazeConstants.MAGIC_BYTE_2); + this.writeByte(SerializerDeserializerV2.MAGIC_BYTE_1); + this.writeByte(SerializerDeserializerV2.MAGIC_BYTE_2); this.writeByte(SerializerDeserializerV2.VERSION_BYTE); } @@ -47,7 +45,7 @@ public class MazeOutputStreamV2 extends AbstractMazeOutputStream { for (int x = 0; x < width; x++) { // We .get() it, because we want to crash hard if it is not available. final Tile tile = maze.getTileAt(x, y).get(); - final byte bitmask = CommonTileHandler.getBitmaskForTile(tile); + final byte bitmask = SerializerDeserializerV2.getBitmaskForTile(tile); this.writeByte(bitmask); } } diff --git a/src/main/java/ch/fritteli/maze/generator/serialization/v2/SerializerDeserializerV2.java b/src/main/java/ch/fritteli/maze/generator/serialization/v2/SerializerDeserializerV2.java index 99f8e22..6e7b199 100644 --- a/src/main/java/ch/fritteli/maze/generator/serialization/v2/SerializerDeserializerV2.java +++ b/src/main/java/ch/fritteli/maze/generator/serialization/v2/SerializerDeserializerV2.java @@ -1,5 +1,6 @@ package ch.fritteli.maze.generator.serialization.v2; +import ch.fritteli.maze.generator.model.Direction; import ch.fritteli.maze.generator.model.Maze; import ch.fritteli.maze.generator.model.Position; import ch.fritteli.maze.generator.model.Tile; @@ -8,9 +9,29 @@ import org.jetbrains.annotations.NotNull; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.util.EnumSet; /** - * Header bytes are: + *
+ * decimal hex bin  border
+ *       0   0 0000 no border
+ *       1   1 0001 top
+ *       2   2 0010 right
+ *       3   3 0011 top+right
+ *       4   4 0100 bottom
+ *       5   5 0101 top+bottom
+ *       6   6 0110 right+bottom
+ *       7   7 0111 top+right+bottom
+ *       8   8 1000 left
+ *       9   9 1001 top+left
+ *      10   a 1010 right+left
+ *      11   b 1011 top+right+left
+ *      12   c 1100 bottom+left
+ *      13   d 1101 top+bottom+left
+ *      14   e 1110 right+bottom+left
+ *      15   f 1111 top+right+bottom+left
+ * 
+ * ==> bits 0..2: always 0; bit 3: 1=solution, 0=not solution; bits 4..7: encode walls ==> first bytes are: *
  *   byte  hex meaning
  *     00 0x1a magic
@@ -29,7 +50,16 @@ import java.lang.reflect.InvocationTargetException;
  */
 @UtilityClass
 public class SerializerDeserializerV2 {
-    public final byte VERSION_BYTE = 0x02;
+
+    final byte MAGIC_BYTE_1 = 0x1a;
+    final byte MAGIC_BYTE_2 = (byte) 0xb1;
+    final byte VERSION_BYTE = 0x02;
+
+    private final byte TOP_BIT = 0b0000_0001;
+    private final byte RIGHT_BIT = 0b0000_0010;
+    private final byte BOTTOM_BIT = 0b0000_0100;
+    private final byte LEFT_BIT = 0b0000_1000;
+    private final byte SOLUTION_BIT = 0b0001_0000;
 
     /**
      * Serializes the {@code maze} into a byte array.
@@ -69,4 +99,55 @@ public class SerializerDeserializerV2 {
             throw new RuntimeException("Can not deserialize Maze from maze data.", e);
         }
     }
+
+    @NotNull
+    private Tile createTile(@NotNull final EnumSet walls, boolean solution) {
+        try {
+            final Constructor constructor = Tile.class.getDeclaredConstructor(EnumSet.class, Boolean.TYPE);
+            constructor.setAccessible(true);
+            return constructor.newInstance(walls, solution);
+        } catch (@NotNull final NoSuchMethodException | InstantiationException | IllegalAccessException |
+                                InvocationTargetException e) {
+            throw new RuntimeException("Can not deserialize Tile from maze data.", e);
+        }
+    }
+
+    byte getBitmaskForTile(@NotNull final Tile tile) {
+        byte bitmask = 0;
+        if (tile.hasWallAt(Direction.TOP)) {
+            bitmask |= TOP_BIT;
+        }
+        if (tile.hasWallAt(Direction.RIGHT)) {
+            bitmask |= RIGHT_BIT;
+        }
+        if (tile.hasWallAt(Direction.BOTTOM)) {
+            bitmask |= BOTTOM_BIT;
+        }
+        if (tile.hasWallAt((Direction.LEFT))) {
+            bitmask |= LEFT_BIT;
+        }
+        if (tile.isSolution()) {
+            bitmask |= SOLUTION_BIT;
+        }
+        return bitmask;
+    }
+
+    @NotNull
+    Tile getTileForBitmask(final byte bitmask) {
+        final EnumSet walls = EnumSet.noneOf(Direction.class);
+        if ((bitmask & TOP_BIT) == TOP_BIT) {
+            walls.add(Direction.TOP);
+        }
+        if ((bitmask & RIGHT_BIT) == RIGHT_BIT) {
+            walls.add(Direction.RIGHT);
+        }
+        if ((bitmask & BOTTOM_BIT) == BOTTOM_BIT) {
+            walls.add(Direction.BOTTOM);
+        }
+        if ((bitmask & LEFT_BIT) == LEFT_BIT) {
+            walls.add(Direction.LEFT);
+        }
+        final boolean solution = (bitmask & SOLUTION_BIT) == SOLUTION_BIT;
+        return createTile(walls, solution);
+    }
 }
diff --git a/src/main/java/ch/fritteli/maze/generator/serialization/v3/MazeInputStreamV3.java b/src/main/java/ch/fritteli/maze/generator/serialization/v3/MazeInputStreamV3.java
deleted file mode 100644
index 8a0dc26..0000000
--- a/src/main/java/ch/fritteli/maze/generator/serialization/v3/MazeInputStreamV3.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package ch.fritteli.maze.generator.serialization.v3;
-
-import ch.fritteli.maze.generator.model.Maze;
-import ch.fritteli.maze.generator.model.Position;
-import ch.fritteli.maze.generator.model.Tile;
-import ch.fritteli.maze.generator.serialization.AbstractMazeInputStream;
-import ch.fritteli.maze.generator.serialization.CommonTileHandler;
-import ch.fritteli.maze.generator.serialization.MazeConstants;
-import org.jetbrains.annotations.NotNull;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-public class MazeInputStreamV3 extends AbstractMazeInputStream {
-
-    public MazeInputStreamV3(@NotNull final byte[] buf) {
-        super(buf);
-    }
-
-    @Override
-    public void checkHeader() {
-        // 00 0x1a magic
-        // 01 0xb1 magic
-        // 02 0x03 version
-        final byte magic1 = this.readByte();
-        if (magic1 != MazeConstants.MAGIC_BYTE_1) {
-            throw new IllegalArgumentException("Invalid maze data.");
-        }
-        final byte magic2 = this.readByte();
-        if (magic2 != MazeConstants.MAGIC_BYTE_2) {
-            throw new IllegalArgumentException("Invalid maze data.");
-        }
-        final int version = this.readByte();
-        if (version != SerializerDeserializerV3.VERSION_BYTE) {
-            throw new IllegalArgumentException("Unknown maze data version: " + version);
-        }
-    }
-
-    @NotNull
-    @Override
-    public Maze readMazeData() throws IOException {
-        // 03..06   width (int)
-        // 07..10   height (int)
-        // 11..14   start-x (int)
-        // 15..18   start-y (int)
-        // 19..22   end-x (int)
-        // 23..26   end-y (int)
-        // 27..34   random seed number (long)
-        // 35       length of the algorithm's name (unsigned byte)
-        // 36..+len name (bytes of String)
-        // +len+1.. tiles
-        final int width = this.readInt();
-        final int height = this.readInt();
-        final int startX = this.readInt();
-        final int startY = this.readInt();
-        final int endX = this.readInt();
-        final int endY = this.readInt();
-        final long randomSeed = this.readLong();
-        final int algorithmLength = this.readByteAsInt();
-
-        final String algorithm = new String(this.readNBytes(algorithmLength), StandardCharsets.UTF_8);
-
-        final Tile[][] tiles = new Tile[width][height];
-        for (int x = 0; x < width; x++) {
-            tiles[x] = new Tile[height];
-        }
-
-        for (int y = 0; y < height; y++) {
-            for (int x = 0; x < width; x++) {
-                final byte bitmask = this.readByte();
-                tiles[x][y] = CommonTileHandler.getTileForBitmask(bitmask);
-            }
-        }
-
-        final Position start = new Position(startX, startY);
-        final Position end = new Position(endX, endY);
-        return SerializerDeserializerV3.createMaze(tiles, width, height, start, end, randomSeed, algorithm);
-    }
-}
diff --git a/src/main/java/ch/fritteli/maze/generator/serialization/v3/MazeOutputStreamV3.java b/src/main/java/ch/fritteli/maze/generator/serialization/v3/MazeOutputStreamV3.java
deleted file mode 100644
index c9da80e..0000000
--- a/src/main/java/ch/fritteli/maze/generator/serialization/v3/MazeOutputStreamV3.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package ch.fritteli.maze.generator.serialization.v3;
-
-import ch.fritteli.maze.generator.model.Maze;
-import ch.fritteli.maze.generator.model.Position;
-import ch.fritteli.maze.generator.model.Tile;
-import ch.fritteli.maze.generator.serialization.AbstractMazeOutputStream;
-import ch.fritteli.maze.generator.serialization.CommonTileHandler;
-import ch.fritteli.maze.generator.serialization.MazeConstants;
-import org.jetbrains.annotations.NotNull;
-
-import java.nio.charset.StandardCharsets;
-
-public class MazeOutputStreamV3 extends AbstractMazeOutputStream {
-
-    @Override
-    public void writeHeader() {
-        // 00 0x1a magic
-        // 01 0xb1 magic
-        // 02 0x03 version
-        this.writeByte(MazeConstants.MAGIC_BYTE_1);
-        this.writeByte(MazeConstants.MAGIC_BYTE_2);
-        this.writeByte(SerializerDeserializerV3.VERSION_BYTE);
-    }
-
-    @Override
-    public void writeMazeData(@NotNull final Maze maze) {
-        // 03..06   width (int)
-        // 07..10   height (int)
-        // 11..14   start-x (int)
-        // 15..18   start-y (int)
-        // 19..22   end-x (int)
-        // 23..26   end-y (int)
-        // 27..34   random seed number (long)
-        // 35       length of the algorithm's name (unsigned byte)
-        // 36..+len name (bytes of String)
-        // +len+1.. tiles
-        final long randomSeed = maze.getRandomSeed();
-        final AlgorithmWrapper algorithm = this.getAlgorithmWrapper(maze.getAlgorithm());
-        final int width = maze.getWidth();
-        final int height = maze.getHeight();
-        final Position start = maze.getStart();
-        final Position end = maze.getEnd();
-        this.writeInt(width);
-        this.writeInt(height);
-        this.writeInt(start.x());
-        this.writeInt(start.y());
-        this.writeInt(end.x());
-        this.writeInt(end.y());
-        this.writeLong(randomSeed);
-        this.writeByte(algorithm.length());
-        this.writeBytes(algorithm.name());
-
-        for (int y = 0; y < height; y++) {
-            for (int x = 0; x < width; x++) {
-                // We .get() it, because we want to crash hard if it is not available.
-                final Tile tile = maze.getTileAt(x, y).get();
-                final byte bitmask = CommonTileHandler.getBitmaskForTile(tile);
-                this.writeByte(bitmask);
-            }
-        }
-    }
-
-    @NotNull
-    private AlgorithmWrapper getAlgorithmWrapper(@NotNull final String algorithm) {
-        final byte[] bytes = algorithm.getBytes(StandardCharsets.UTF_8);
-        if (bytes.length < 256) {
-            // Phew, that's the easy case!
-            return new AlgorithmWrapper(bytes, (byte) bytes.length);
-        }
-
-        // Let's use a very primitive, brute-force approach
-        int strLen = Math.min(255, algorithm.length());
-        int len;
-        byte[] name;
-        do {
-            name = algorithm.substring(0, strLen).getBytes(StandardCharsets.UTF_8);
-            len = name.length;
-            strLen--;
-        } while (len > 255);
-
-        return new AlgorithmWrapper(name, (byte) len);
-    }
-
-    private record AlgorithmWrapper(@NotNull byte[] name, byte length) {
-    }
-}
diff --git a/src/main/java/ch/fritteli/maze/generator/serialization/v3/SerializerDeserializerV3.java b/src/main/java/ch/fritteli/maze/generator/serialization/v3/SerializerDeserializerV3.java
deleted file mode 100644
index 1da19cc..0000000
--- a/src/main/java/ch/fritteli/maze/generator/serialization/v3/SerializerDeserializerV3.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package ch.fritteli.maze.generator.serialization.v3;
-
-import ch.fritteli.maze.generator.model.Maze;
-import ch.fritteli.maze.generator.model.Position;
-import ch.fritteli.maze.generator.model.Tile;
-import lombok.experimental.UtilityClass;
-import org.jetbrains.annotations.NotNull;
-
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-
-/**
- * Header bytes are:
- * 
- *   byte     hex    meaning
- *     00     0x1a   magic
- *     01     0xb1   magic
- *     02     0x03   version (0x00 -> dev, 0x01, 0x02 -> deprecated, 0x03 -> stable)
- *     03..06        width (int)
- *     07..10        height (int)
- *     11..14        start-x (int)
- *     15..18        start-y (int)
- *     19..22        end-x (int)
- *     23..26        end-y (int)
- *     27..34        random seed number (long)
- *     35            length of the algorithm's name (number of bytes of the Java String) (unsigned byte)
- *     36..(36+len)  algorithm's name (bytes of the Java String) (byte...)
- *     36+len+1..    tiles
- * 
- * Extraneous space (poss. last nibble) is ignored. - */ -@UtilityClass -public class SerializerDeserializerV3 { - public final byte VERSION_BYTE = 0x03; - - /** - * Serializes the {@code maze} into a byte array. - * - * @param maze The {@link Maze} to be serialized. - * @return The resulting byte array. - */ - @NotNull - public byte[] serialize(@NotNull final Maze maze) { - final MazeOutputStreamV3 stream = new MazeOutputStreamV3(); - stream.writeHeader(); - stream.writeMazeData(maze); - return stream.toByteArray(); - } - - /** - * Deserializes the byte array into an instance of {@link Maze}. - * - * @param bytes The byte array to be deserialized. - * @return An instance of {@link Maze}. - */ - @NotNull - public Maze deserialize(@NotNull final byte[] bytes) throws IOException { - final MazeInputStreamV3 stream = new MazeInputStreamV3(bytes); - stream.checkHeader(); - return stream.readMazeData(); - } - - @NotNull - Maze createMaze(@NotNull final Tile[][] field, - final int width, - final int height, - @NotNull final Position start, - @NotNull final Position end, - final long randomSeed, - @NotNull final String algorithm) { - try { - final Constructor constructor = Maze.class.getDeclaredConstructor( - Tile[][].class, - Integer.TYPE, - Integer.TYPE, - Position.class, - Position.class, - Long.TYPE, - String.class - ); - constructor.setAccessible(true); - return constructor.newInstance(field, width, height, start, end, randomSeed, algorithm); - } catch (@NotNull final NoSuchMethodException | IllegalAccessException | InstantiationException | - InvocationTargetException e) { - throw new RuntimeException("Can not deserialize Maze from maze data.", e); - } - } -} diff --git a/src/main/resources/maze.schema.json b/src/main/resources/maze.schema.json index 8fbbb05..4b7a57e 100644 --- a/src/main/resources/maze.schema.json +++ b/src/main/resources/maze.schema.json @@ -6,7 +6,6 @@ "additionalProperties": false, "required": [ "id", - "algorithm", "width", "height", "start", @@ -18,10 +17,6 @@ "type": "string", "description": "64 bit precision signed integer value. Transmitted as string, because ECMAScript (browsers) don't normally handle 64 bit integers well, as the ECMAScript 'number' type is a 64 bit signed double value, leaving only 53 bits for the integer part, thus losing precision." }, - "algorithm": { - "type": "string", - "description": "The name of the algorithm used to generate the maze." - }, "width": { "type": "integer", "minimum": 1 diff --git a/src/test/java/ch/fritteli/maze/generator/algorithm/wilson/WilsonTest.java b/src/test/java/ch/fritteli/maze/generator/algorithm/wilson/WilsonTest.java deleted file mode 100644 index 4457c42..0000000 --- a/src/test/java/ch/fritteli/maze/generator/algorithm/wilson/WilsonTest.java +++ /dev/null @@ -1,100 +0,0 @@ -package ch.fritteli.maze.generator.algorithm.wilson; - -import ch.fritteli.maze.generator.model.Maze; -import ch.fritteli.maze.generator.renderer.text.TextRenderer; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -class WilsonTest { - @Test - void testTinyGeneration() { - // arrange - final Maze maze = new Maze(3, 3, 0); - final Wilson wilson = new Wilson(maze); - - // act - wilson.run(); - - // assert - final String textRepresentation = TextRenderer.newInstance().render(maze); - assertThat(textRepresentation).isEqualTo(""" - ╷ ╶───┐ - │ │ - │ ┌─┐ │ - │ │ │ │ - │ ╵ │ │ - │ │ │ - └───┘ ╵"""); - } - - @Test - void testSimpleGeneration() { - // arrange - final Maze maze = new Maze(10, 10, 0); - final Wilson wilson = new Wilson(maze); - - // act - wilson.run(); - - // assert - final String textRepresentation = TextRenderer.newInstance().render(maze); - assertThat(textRepresentation).isEqualTo(""" - ╷ ╶─────┬─────┬─────┐ - │ │ │ │ - ├─╴ ╷ ╶─┴─┬─╴ ├─┐ ╶─┤ - │ │ │ │ │ │ - │ ╷ └─┐ ╶─┘ ┌─┘ ╵ ╷ │ - │ │ │ │ │ │ - │ │ ╶─┤ ╶─┐ ╵ ╷ ╶─┤ │ - │ │ │ │ │ │ │ - │ ├─┐ ├─╴ └─┐ │ ╶─┼─┤ - │ │ │ │ │ │ │ │ - ├─┘ └─┘ ╶─┐ └─┤ ╶─┤ │ - │ │ │ │ │ - ├───┐ ╷ ╶─┴───┴─┐ │ │ - │ │ │ │ │ │ - ├─╴ └─┼─╴ ╷ ╷ ┌─┴─┘ │ - │ │ │ │ │ │ - │ ╷ ╶─┴─┬─┤ ├─┘ ╶─┐ │ - │ │ │ │ │ │ │ - │ ├─╴ ╷ ╵ ╵ ╵ ┌─╴ │ │ - │ │ │ │ │ │ - └─┴───┴───────┴───┘ ╵"""); - } - - @Test - void testSimpleGenerationWithSolution() { - // arrange - final Maze maze = new Maze(10, 10, 0); - final Wilson wilson = new Wilson(maze); - - // act - wilson.run(); - - // assert - final String textRepresentation = TextRenderer.newInstance().setRenderSolution(true).render(maze); - assertThat(textRepresentation).isEqualTo(""" - ╷│╶─────┬─────┬─────┐ - │╰───╮ │ │ │ - ├─╴ ╷│╶─┴─┬─╴ ├─┐ ╶─┤ - │ │╰─╮ │ │ │ │ - │ ╷ └─┐│╶─┘ ┌─┘ ╵ ╷ │ - │ │ ││ │ │ │ - │ │ ╶─┤│╶─┐ ╵ ╷ ╶─┤ │ - │ │ │╰─╮│ │ │ │ - │ ├─┐ ├─╴│└─┐ │ ╶─┼─┤ - │ │ │ │╭─╯ │ │ │ │ - ├─┘ └─┘│╶─┐ └─┤ ╶─┤ │ - │ │ │ │ │ │ - ├───┐ ╷│╶─┴───┴─┐ │ │ - │ │ │╰───╮ │ │ │ - ├─╴ └─┼─╴ ╷│╷ ┌─┴─┘ │ - │ │ │││ │╭───╮│ - │ ╷ ╶─┴─┬─┤│├─┘│╶─┐││ - │ │ │ │││╭─╯ │││ - │ ├─╴ ╷ ╵ ╵│╵│┌─╴ │││ - │ │ │ ╰─╯│ │││ - └─┴───┴───────┴───┘│╵"""); - } -} diff --git a/src/test/java/ch/fritteli/maze/generator/model/WallsTest.java b/src/test/java/ch/fritteli/maze/generator/model/WallsTest.java index d0f861f..1b4f2ce 100644 --- a/src/test/java/ch/fritteli/maze/generator/model/WallsTest.java +++ b/src/test/java/ch/fritteli/maze/generator/model/WallsTest.java @@ -1,10 +1,10 @@ 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 org.junit.jupiter.api.Test; -import java.util.EnumSet; - import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -65,14 +65,14 @@ class WallsTest { } @Test - void testSeal() { + void testHarden() { // arrange final Walls sut = new Walls(); sut.set(Direction.TOP); sut.set(Direction.RIGHT); // act - sut.seal(Direction.TOP); + sut.harden(Direction.TOP); // assert assertThat(sut) @@ -88,34 +88,34 @@ class WallsTest { assertThat(result).isFalse(); assertThat(sut.isSet(Direction.TOP)).isTrue(); - // act / assert: try to seal un-set wall - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> sut.seal(Direction.LEFT)); + // act / assert: try to harden un-set wall + assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> sut.harden(Direction.LEFT)); } @Test - void testUnseal() { + void testUnharden() { // arrange final Walls sut = new Walls(); sut.setAll(); - sut.seal(Direction.TOP); + sut.harden(Direction.TOP); // pre-assert: TOP can't be cleared while hardened assertThat(sut.clear(Direction.TOP)).isFalse(); // act - sut.unseal(Direction.TOP); + sut.unharden(Direction.TOP); // assert: TOP can be cleared assertThat(sut.clear(Direction.TOP)).isTrue(); } @Test - void testGetUnsealedSet() { + void testGetUnhardenedSet() { // arrange final Walls sut = new Walls(); // act - EnumSet result = sut.getUnsealedSet(); + Stream result = sut.getUnhardenedSet(); // assert assertThat(result).isEmpty(); @@ -125,16 +125,16 @@ class WallsTest { sut.set(Direction.LEFT); // act - result = sut.getUnsealedSet(); + result = sut.getUnhardenedSet(); // assert assertThat(result).containsExactly(Direction.TOP, Direction.LEFT); - // arrange: seal a direction - sut.seal(Direction.TOP); + // arrange: harden a direction + sut.harden(Direction.TOP); // act - result = sut.getUnsealedSet(); + result = sut.getUnhardenedSet(); // assert assertThat(result).containsExactly(Direction.LEFT); diff --git a/src/test/java/ch/fritteli/maze/generator/serialization/v1/SerializerDeserializerV1Test.java b/src/test/java/ch/fritteli/maze/generator/serialization/v1/SerializerDeserializerV1Test.java index 4d8a392..2fd819b 100644 --- a/src/test/java/ch/fritteli/maze/generator/serialization/v1/SerializerDeserializerV1Test.java +++ b/src/test/java/ch/fritteli/maze/generator/serialization/v1/SerializerDeserializerV1Test.java @@ -13,8 +13,6 @@ class SerializerDeserializerV1Test { new RandomDepthFirst(expected).run(); final byte[] bytes = SerializerDeserializerV1.serialize(expected); final Maze result = SerializerDeserializerV1.deserialize(bytes); - assertThat(result.getAlgorithm()).isNull(); - expected.setAlgorithm(null); assertThat(result).isEqualTo(expected); } @@ -24,8 +22,6 @@ class SerializerDeserializerV1Test { new RandomDepthFirst(expected).run(); final byte[] bytes = SerializerDeserializerV1.serialize(expected); final Maze result = SerializerDeserializerV1.deserialize(bytes); - assertThat(result.getAlgorithm()).isNull(); - expected.setAlgorithm(null); assertThat(result).isEqualTo(expected); } @@ -35,8 +31,6 @@ class SerializerDeserializerV1Test { new RandomDepthFirst(expected).run(); final byte[] bytes = SerializerDeserializerV1.serialize(expected); final Maze result = SerializerDeserializerV1.deserialize(bytes); - assertThat(result.getAlgorithm()).isNull(); - expected.setAlgorithm(null); assertThat(result).isEqualTo(expected); } } diff --git a/src/test/java/ch/fritteli/maze/generator/serialization/v2/SerializerDeserializerV2Test.java b/src/test/java/ch/fritteli/maze/generator/serialization/v2/SerializerDeserializerV2Test.java deleted file mode 100644 index a89e947..0000000 --- a/src/test/java/ch/fritteli/maze/generator/serialization/v2/SerializerDeserializerV2Test.java +++ /dev/null @@ -1,46 +0,0 @@ -package ch.fritteli.maze.generator.serialization.v2; - -import ch.fritteli.maze.generator.algorithm.RandomDepthFirst; -import ch.fritteli.maze.generator.algorithm.wilson.Wilson; -import ch.fritteli.maze.generator.model.Maze; -import ch.fritteli.maze.generator.model.Position; -import org.junit.jupiter.api.Test; - -import java.io.IOException; - -import static org.assertj.core.api.Assertions.assertThat; - -class SerializerDeserializerV2Test { - @Test - void testSerializeDeserializeTiny() throws IOException { - final Maze expected = new Maze(2, 2, 255, new Position(0, 0), new Position(1, 1)); - new RandomDepthFirst(expected).run(); - final byte[] bytes = SerializerDeserializerV2.serialize(expected); - final Maze result = SerializerDeserializerV2.deserialize(bytes); - assertThat(result.getAlgorithm()).isNull(); - expected.setAlgorithm(null); - assertThat(result).isEqualTo(expected); - } - - @Test - void testSerializeDeserializeMedium() throws IOException { - final Maze expected = new Maze(20, 20, -271828182846L); - new Wilson(expected).run(); - final byte[] bytes = SerializerDeserializerV2.serialize(expected); - final Maze result = SerializerDeserializerV2.deserialize(bytes); - assertThat(result.getAlgorithm()).isNull(); - expected.setAlgorithm(null); - assertThat(result).isEqualTo(expected); - } - - @Test - void testSerializeDeserializeLarge() throws IOException { - final Maze expected = new Maze(200, 320, 3141592653589793238L); - new Wilson(expected).run(); - final byte[] bytes = SerializerDeserializerV2.serialize(expected); - final Maze result = SerializerDeserializerV2.deserialize(bytes); - assertThat(result.getAlgorithm()).isNull(); - expected.setAlgorithm(null); - assertThat(result).isEqualTo(expected); - } -} diff --git a/src/test/java/ch/fritteli/maze/generator/serialization/v3/SerializerDeserializerV3Test.java b/src/test/java/ch/fritteli/maze/generator/serialization/v3/SerializerDeserializerV3Test.java deleted file mode 100644 index 801c677..0000000 --- a/src/test/java/ch/fritteli/maze/generator/serialization/v3/SerializerDeserializerV3Test.java +++ /dev/null @@ -1,49 +0,0 @@ -package ch.fritteli.maze.generator.serialization.v3; - -import ch.fritteli.maze.generator.algorithm.RandomDepthFirst; -import ch.fritteli.maze.generator.algorithm.wilson.Wilson; -import ch.fritteli.maze.generator.model.Maze; -import ch.fritteli.maze.generator.model.Position; -import org.junit.jupiter.api.Test; - -import java.io.IOException; - -import static org.assertj.core.api.Assertions.assertThat; - -class SerializerDeserializerV3Test { - @Test - void testSerializeDeserializeTiny() throws IOException { - final Maze expected = new Maze(2, 2, 255, new Position(0, 0), new Position(1, 1)); - new RandomDepthFirst(expected).run(); - final byte[] bytes = SerializerDeserializerV3.serialize(expected); - final Maze result = SerializerDeserializerV3.deserialize(bytes); - assertThat(result).isEqualTo(expected); - } - - @Test - void testSerializeDeserializeMedium() throws IOException { - final Maze expected = new Maze(20, 20, -271828182846L); - new Wilson(expected).run(); - final byte[] bytes = SerializerDeserializerV3.serialize(expected); - final Maze result = SerializerDeserializerV3.deserialize(bytes); - assertThat(result).isEqualTo(expected); - } - - @Test - void testSerializeDeserializeLarge() throws IOException { - final Maze expected = new Maze(200, 320, 3141592653589793238L); - new Wilson(expected).run(); - final byte[] bytes = SerializerDeserializerV3.serialize(expected); - final Maze result = SerializerDeserializerV3.deserialize(bytes); - assertThat(result).isEqualTo(expected); - } - - @Test - void testSerializeDeserializeLargeRandom() throws IOException { - final Maze expected = new Maze(200, 320, 3141592653589793238L); - new RandomDepthFirst(expected).run(); - final byte[] bytes = SerializerDeserializerV3.serialize(expected); - final Maze result = SerializerDeserializerV3.deserialize(bytes); - assertThat(result).isEqualTo(expected); - } -}