Update versions, refactor, cleanup.
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
4291b422bf
commit
874d4208ef
19 changed files with 478 additions and 429 deletions
|
@ -15,8 +15,8 @@ import java.nio.file.Paths;
|
|||
@Slf4j
|
||||
public class Main {
|
||||
public static void main(@NonNull final String[] args) {
|
||||
int width = 100;
|
||||
int height = 100;
|
||||
final int width = 20;
|
||||
final int height = 30;
|
||||
final Labyrinth labyrinth = new Labyrinth(width, height/*, 0*/);
|
||||
final TextRenderer textRenderer = TextRenderer.newInstance();
|
||||
final HTMLRenderer htmlRenderer = HTMLRenderer.newInstance();
|
||||
|
|
|
@ -2,11 +2,7 @@ package ch.fritteli.labyrinth.generator.model;
|
|||
|
||||
import io.vavr.collection.Stream;
|
||||
import io.vavr.control.Option;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.*;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
|
|
@ -5,11 +5,7 @@ import lombok.EqualsAndHashCode;
|
|||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.*;
|
||||
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
|
|
|
@ -33,7 +33,8 @@ class Generator {
|
|||
|
||||
final PDDocument pdDocument = new PDDocument();
|
||||
final PDDocumentInformation info = new PDDocumentInformation();
|
||||
info.setTitle("Labyrinth " + this.labyrinth.getWidth() + "x" + this.labyrinth.getHeight() + ", ID " + this.labyrinth.getRandomSeed());
|
||||
info.setTitle("Labyrinth " + this.labyrinth.getWidth() + "x" + this.labyrinth.getHeight() + ", ID " +
|
||||
this.labyrinth.getRandomSeed());
|
||||
pdDocument.setDocumentInformation(info);
|
||||
final PDPage puzzlePage = new PDPage(new PDRectangle(pageWidth, pageHeight));
|
||||
final PDPage solutionPage = new PDPage(new PDRectangle(pageWidth, pageHeight));
|
||||
|
@ -68,7 +69,8 @@ class Generator {
|
|||
}
|
||||
|
||||
private void drawHorizontalLines(@NonNull 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);
|
||||
// Draw the TOP borders of all tiles.
|
||||
for (int y = 0; y < this.labyrinth.getHeight(); y++) {
|
||||
|
@ -136,7 +138,8 @@ class Generator {
|
|||
}
|
||||
|
||||
private void drawVerticalLines(@NonNull 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);
|
||||
// Draw the LEFT borders of all tiles.
|
||||
for (int x = 0; x < this.labyrinth.getWidth(); x++) {
|
||||
|
@ -206,7 +209,8 @@ class Generator {
|
|||
private void drawSolution(@NonNull final PDPageContentStream pageContentStream) throws IOException {
|
||||
// Draw the solution in 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.labyrinth.getEnd();
|
||||
Position currentPosition = this.labyrinth.getStart();
|
||||
Position previousPosition = null;
|
||||
|
@ -223,7 +227,8 @@ class Generator {
|
|||
}
|
||||
|
||||
@NonNull
|
||||
private Position findNextSolutionPosition(@Nullable final Position previousPosition, @NonNull final Position currentPosition) {
|
||||
private Position findNextSolutionPosition(@Nullable final Position previousPosition,
|
||||
@NonNull final Position currentPosition) {
|
||||
final Tile currentTile = this.labyrinth.getTileAt(currentPosition).get();
|
||||
for (final Direction direction : Direction.values()) {
|
||||
if (!currentTile.hasWallAt(direction)) {
|
||||
|
@ -250,21 +255,21 @@ class Generator {
|
|||
this.y = y;
|
||||
}
|
||||
|
||||
private float calcX(final int x) {
|
||||
return x * PDFRenderer.SCALE + PDFRenderer.MARGIN;
|
||||
}
|
||||
|
||||
private float calcY(final int y) {
|
||||
return (Generator.this.labyrinth.getHeight() - y) * PDFRenderer.SCALE + PDFRenderer.MARGIN;
|
||||
}
|
||||
|
||||
public Coordinate withX(final int x) {
|
||||
return new Coordinate(calcX(x), this.y);
|
||||
}
|
||||
|
||||
private float calcX(final int x) {
|
||||
return x * PDFRenderer.SCALE + PDFRenderer.MARGIN;
|
||||
}
|
||||
|
||||
public Coordinate withY(final int y) {
|
||||
return new Coordinate(this.x, calcY(y));
|
||||
}
|
||||
|
||||
private float calcY(final int y) {
|
||||
return (Generator.this.labyrinth.getHeight() - y) * PDFRenderer.SCALE + PDFRenderer.MARGIN;
|
||||
}
|
||||
}
|
||||
|
||||
@Value
|
||||
|
@ -282,7 +287,8 @@ class Generator {
|
|||
}
|
||||
|
||||
private float calcY(final int y) {
|
||||
return (Generator.this.labyrinth.getHeight() - y) * PDFRenderer.SCALE - PDFRenderer.SCALE / 2 + PDFRenderer.MARGIN;
|
||||
return (Generator.this.labyrinth.getHeight() - y) * PDFRenderer.SCALE - PDFRenderer.SCALE / 2 +
|
||||
PDFRenderer.MARGIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
package ch.fritteli.labyrinth.generator.serialization;
|
||||
|
||||
import ch.fritteli.labyrinth.generator.model.Labyrinth;
|
||||
import ch.fritteli.labyrinth.generator.model.Tile;
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import static ch.fritteli.labyrinth.generator.serialization.SerializerDeserializer.*;
|
||||
|
||||
public class LabyrinthInputStream extends ByteArrayInputStream {
|
||||
public LabyrinthInputStream(@NonNull final byte[] buf) {
|
||||
super(buf);
|
||||
}
|
||||
|
||||
public byte readByte() {
|
||||
final int read = this.read();
|
||||
if (read == -1) {
|
||||
// end of stream reached
|
||||
throw new ArrayIndexOutOfBoundsException("End of stream reached. Cannot read more bytes.");
|
||||
}
|
||||
return (byte) read;
|
||||
public long readLong() {
|
||||
long result = 0;
|
||||
result |= ((long) this.readInt()) << 32;
|
||||
result |= 0xffffffffL & this.readInt();
|
||||
return result;
|
||||
}
|
||||
|
||||
public int readInt() {
|
||||
|
@ -27,10 +29,48 @@ public class LabyrinthInputStream extends ByteArrayInputStream {
|
|||
return result;
|
||||
}
|
||||
|
||||
public long readLong() {
|
||||
long result = 0;
|
||||
result |= ((long) this.readInt()) << 32;
|
||||
result |= 0xffffffffL & this.readInt();
|
||||
return result;
|
||||
public void checkHeader() {
|
||||
final byte magic1 = this.readByte();
|
||||
if (magic1 != MAGIC_BYTE_1) {
|
||||
throw new IllegalArgumentException("Invalid labyrinth data.");
|
||||
}
|
||||
final byte magic2 = this.readByte();
|
||||
if (magic2 != MAGIC_BYTE_2) {
|
||||
throw new IllegalArgumentException("Invalid labyrinth data.");
|
||||
}
|
||||
final int version = this.readByte();
|
||||
if (version != VERSION_BYTE) {
|
||||
throw new IllegalArgumentException("Unknown Labyrinth data version: " + version);
|
||||
}
|
||||
}
|
||||
|
||||
public byte readByte() {
|
||||
final int read = this.read();
|
||||
if (read == -1) {
|
||||
// end of stream reached
|
||||
throw new ArrayIndexOutOfBoundsException("End of stream reached. Cannot read more bytes.");
|
||||
}
|
||||
return (byte) read;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Labyrinth readLabyrinthData() {
|
||||
final long randomSeed = this.readLong();
|
||||
final int width = this.readInt();
|
||||
final int height = this.readInt();
|
||||
|
||||
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] = SerializerDeserializer.getTileForBitmask(bitmask);
|
||||
}
|
||||
}
|
||||
|
||||
return SerializerDeserializer.createLabyrinth(tiles, width, height, randomSeed);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,51 @@
|
|||
package ch.fritteli.labyrinth.generator.serialization;
|
||||
|
||||
import ch.fritteli.labyrinth.generator.model.Labyrinth;
|
||||
import ch.fritteli.labyrinth.generator.model.Tile;
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
import static ch.fritteli.labyrinth.generator.serialization.SerializerDeserializer.*;
|
||||
|
||||
public class LabyrinthOutputStream extends ByteArrayOutputStream {
|
||||
public void writeHeader() {
|
||||
this.writeByte(MAGIC_BYTE_1);
|
||||
this.writeByte(MAGIC_BYTE_2);
|
||||
this.writeByte(VERSION_BYTE);
|
||||
}
|
||||
|
||||
public void writeByte(final byte value) {
|
||||
this.write(value);
|
||||
}
|
||||
|
||||
public void writeLabyrinthData(@NonNull final Labyrinth labyrinth) {
|
||||
final long randomSeed = labyrinth.getRandomSeed();
|
||||
final int width = labyrinth.getWidth();
|
||||
final int height = labyrinth.getHeight();
|
||||
this.writeLong(randomSeed);
|
||||
this.writeInt(width);
|
||||
this.writeInt(height);
|
||||
|
||||
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 = labyrinth.getTileAt(x, y).get();
|
||||
final byte bitmask = SerializerDeserializer.getBitmaskForTile(tile);
|
||||
this.writeByte(bitmask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void writeLong(final long value) {
|
||||
this.writeInt((int) (value >> 32));
|
||||
this.writeInt((int) value);
|
||||
}
|
||||
|
||||
public void writeInt(final int value) {
|
||||
this.write(value >> 24);
|
||||
this.write(value >> 16);
|
||||
this.write(value >> 8);
|
||||
this.write(value);
|
||||
}
|
||||
|
||||
public void writeLong(final long value) {
|
||||
this.writeInt((int) (value >> 32));
|
||||
this.writeInt((int) value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,19 +36,19 @@ import java.util.EnumSet;
|
|||
* byte hex meaning
|
||||
* 00 0x1a magic
|
||||
* 01 0xb1 magic
|
||||
* 02 0x00 version (0x00 -> dev / unstable; will be bumped to 0x01 once stabilized)
|
||||
* 02 0x01 version (0x00 -> dev, 0x01 -> stable)
|
||||
* 03..06 width (int)
|
||||
* 07..10 height (int)
|
||||
* 11..18 random seed number (long)
|
||||
* 19.. tiles
|
||||
* </pre>
|
||||
* exteaneous space (poss. last nibble) is ignored.
|
||||
* Extraneous space (poss. last nibble) is ignored.
|
||||
*/
|
||||
@UtilityClass
|
||||
public class SerializerDeserializer {
|
||||
private final byte MAGIC_BYTE_1 = 0x1a;
|
||||
private final byte MAGIC_BYTE_2 = (byte) 0xb1;
|
||||
private final byte VERSION_BYTE = 0x01;
|
||||
static final byte MAGIC_BYTE_1 = 0x1a;
|
||||
static final byte MAGIC_BYTE_2 = (byte) 0xb1;
|
||||
static final byte VERSION_BYTE = 0x01;
|
||||
|
||||
private final byte TOP_BIT = 0b0000_0001;
|
||||
private final byte RIGHT_BIT = 0b0000_0010;
|
||||
|
@ -65,8 +65,8 @@ public class SerializerDeserializer {
|
|||
@NonNull
|
||||
public byte[] serialize(@NonNull final Labyrinth labyrinth) {
|
||||
final LabyrinthOutputStream stream = new LabyrinthOutputStream();
|
||||
writeHeader(stream);
|
||||
writeLabyrinthData(stream, labyrinth);
|
||||
stream.writeHeader();
|
||||
stream.writeLabyrinthData(labyrinth);
|
||||
return stream.toByteArray();
|
||||
}
|
||||
|
||||
|
@ -79,79 +79,18 @@ public class SerializerDeserializer {
|
|||
@NonNull
|
||||
public Labyrinth deserialize(@NonNull final byte[] bytes) {
|
||||
final LabyrinthInputStream stream = new LabyrinthInputStream(bytes);
|
||||
checkHeader(stream);
|
||||
return readLabyrinthData(stream);
|
||||
}
|
||||
|
||||
private static void writeHeader(@NonNull final LabyrinthOutputStream stream) {
|
||||
stream.writeByte(MAGIC_BYTE_1);
|
||||
stream.writeByte(MAGIC_BYTE_2);
|
||||
stream.writeByte(VERSION_BYTE);
|
||||
|
||||
}
|
||||
|
||||
private static void checkHeader(@NonNull final LabyrinthInputStream stream) {
|
||||
final byte magic1 = stream.readByte();
|
||||
if (magic1 != MAGIC_BYTE_1) {
|
||||
throw new IllegalArgumentException("Invalid labyrinth data.");
|
||||
}
|
||||
final byte magic2 = stream.readByte();
|
||||
if (magic2 != MAGIC_BYTE_2) {
|
||||
throw new IllegalArgumentException("Invalid labyrinth data.");
|
||||
}
|
||||
final int version = stream.readByte();
|
||||
if (version != VERSION_BYTE) {
|
||||
throw new IllegalArgumentException("Unknown Labyrinth data version: " + version);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeLabyrinthData(@NonNull final LabyrinthOutputStream stream, @NonNull final Labyrinth labyrinth) {
|
||||
final long randomSeed = labyrinth.getRandomSeed();
|
||||
final int width = labyrinth.getWidth();
|
||||
final int height = labyrinth.getHeight();
|
||||
stream.writeLong(randomSeed);
|
||||
stream.writeInt(width);
|
||||
stream.writeInt(height);
|
||||
|
||||
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 = labyrinth.getTileAt(x, y).get();
|
||||
final byte bitmask = getBitmaskForTile(tile);
|
||||
stream.writeByte(bitmask);
|
||||
}
|
||||
}
|
||||
|
||||
stream.checkHeader();
|
||||
return stream.readLabyrinthData();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static Labyrinth readLabyrinthData(@NonNull final LabyrinthInputStream stream) {
|
||||
final long randomSeed = stream.readLong();
|
||||
final int width = stream.readInt();
|
||||
final int height = stream.readInt();
|
||||
|
||||
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 = stream.readByte();
|
||||
tiles[x][y] = getTileForBitmask(bitmask);
|
||||
}
|
||||
}
|
||||
|
||||
return createLabyrinth(tiles, width, height, randomSeed);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Labyrinth createLabyrinth(@NonNull final Tile[][] field, final int width, final int height, final long randomSeed) {
|
||||
Labyrinth createLabyrinth(@NonNull final Tile[][] field, final int width, final int height, final long randomSeed) {
|
||||
try {
|
||||
@NonNull final Constructor<Labyrinth> constructor = Labyrinth.class.getDeclaredConstructor(Tile[][].class, Integer.TYPE, Integer.TYPE, Long.TYPE);
|
||||
final Constructor<Labyrinth> constructor = Labyrinth.class.getDeclaredConstructor(Tile[][].class, Integer.TYPE, Integer.TYPE, Long.TYPE);
|
||||
constructor.setAccessible(true);
|
||||
return constructor.newInstance(field, width, height, randomSeed);
|
||||
} catch (final NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
|
||||
} catch (final NoSuchMethodException | IllegalAccessException | InstantiationException |
|
||||
InvocationTargetException e) {
|
||||
throw new RuntimeException("Can not deserialize Labyrinth from labyrinth data.", e);
|
||||
}
|
||||
}
|
||||
|
@ -162,12 +101,13 @@ public class SerializerDeserializer {
|
|||
@NonNull final Constructor<Tile> constructor = Tile.class.getDeclaredConstructor(EnumSet.class, Boolean.TYPE);
|
||||
constructor.setAccessible(true);
|
||||
return constructor.newInstance(walls, solution);
|
||||
} catch (final NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||
} catch (final NoSuchMethodException | InstantiationException | IllegalAccessException |
|
||||
InvocationTargetException e) {
|
||||
throw new RuntimeException("Can not deserialize Tile from labyrinth data.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private byte getBitmaskForTile(@NonNull final Tile tile) {
|
||||
byte getBitmaskForTile(@NonNull final Tile tile) {
|
||||
byte bitmask = 0;
|
||||
if (tile.hasWallAt(Direction.TOP)) {
|
||||
bitmask |= TOP_BIT;
|
||||
|
@ -188,7 +128,7 @@ public class SerializerDeserializer {
|
|||
}
|
||||
|
||||
@NonNull
|
||||
private Tile getTileForBitmask(final byte bitmask) {
|
||||
Tile getTileForBitmask(final byte bitmask) {
|
||||
final EnumSet<Direction> walls = EnumSet.noneOf(Direction.class);
|
||||
if ((bitmask & TOP_BIT) == TOP_BIT) {
|
||||
walls.add(Direction.TOP);
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>
|
||||
<shutdownHook class="ch.qos.logback.core.hook.DefaultShutdownHook"/>
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<!-- encoders are by default assigned the type
|
||||
ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
<pattern>%d{HH:mm:ss.SSS} %-5level %X{correlationId} [%thread] %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue