Compare commits
	
		
			No commits in common. "3bf438ffb92dadeb19e42140549bcad098eeb8d2" and "cdfa1f4476d9eef3893bf0a403947f34b3532409" have entirely different histories.
		
	
	
		
			3bf438ffb9
			...
			cdfa1f4476
		
	
		
					 22 changed files with 303 additions and 680 deletions
				
			
		|  | @ -12,9 +12,8 @@ public abstract class AbstractMazeGeneratorAlgorithm implements MazeGeneratorAlg | ||||||
|     protected final Random random; |     protected final Random random; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     protected AbstractMazeGeneratorAlgorithm(@NotNull final Maze maze, @NotNull final String algorithmName) { |     public AbstractMazeGeneratorAlgorithm(@NotNull final Maze maze) { | ||||||
|         this.maze = maze; |         this.maze = maze; | ||||||
|         this.random = new Random(maze.getRandomSeed()); |         this.random = new Random(maze.getRandomSeed()); | ||||||
|         this.maze.setAlgorithm(algorithmName); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ public class RandomDepthFirst extends AbstractMazeGeneratorAlgorithm { | ||||||
|     private final Deque<Position> positions = new LinkedList<>(); |     private final Deque<Position> positions = new LinkedList<>(); | ||||||
| 
 | 
 | ||||||
|     public RandomDepthFirst(@NotNull final Maze maze) { |     public RandomDepthFirst(@NotNull final Maze maze) { | ||||||
|         super(maze, "Random Depth First"); |         super(maze); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public void run() { |     public void run() { | ||||||
|  |  | ||||||
|  | @ -24,7 +24,7 @@ import java.util.Random; | ||||||
|  */ |  */ | ||||||
| public class Wilson extends AbstractMazeGeneratorAlgorithm { | public class Wilson extends AbstractMazeGeneratorAlgorithm { | ||||||
|     public Wilson(@NotNull final Maze maze) { |     public Wilson(@NotNull final Maze maze) { | ||||||
|         super(maze, "Wilson"); |         super(maze); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|  | @ -86,21 +86,6 @@ public class Wilson extends AbstractMazeGeneratorAlgorithm { | ||||||
|         direction = determineDirectionForDigging(maze.getEnd()); |         direction = determineDirectionForDigging(maze.getEnd()); | ||||||
|         t = maze.getEndTile(); |         t = maze.getEndTile(); | ||||||
|         this.digTo(t, direction); |         this.digTo(t, direction); | ||||||
|         // seal all walls, mark all as visited |  | ||||||
|         for (int x = 0; x < maze.getWidth(); x++) { |  | ||||||
|             for (int y = 0; y < maze.getHeight(); y++) { |  | ||||||
|                 maze.getTileAt(x, y).forEach(tile -> { |  | ||||||
|                     Stream.of(Direction.values()) |  | ||||||
|                             .forEach(d -> { |  | ||||||
|                                 if (tile.hasWallAt(d)) { |  | ||||||
|                                     tile.preventDiggingToOrFrom(d); |  | ||||||
|                                 } else { |  | ||||||
|                                     tile.digFrom(d); |  | ||||||
|                                 } |  | ||||||
|                             }); |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Nullable |     @Nullable | ||||||
|  |  | ||||||
|  | @ -3,7 +3,6 @@ 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.Setter; |  | ||||||
| import lombok.ToString; | import lombok.ToString; | ||||||
| import org.jetbrains.annotations.NotNull; | import org.jetbrains.annotations.NotNull; | ||||||
| 
 | 
 | ||||||
|  | @ -22,9 +21,6 @@ public class Maze { | ||||||
|     private final Position start; |     private final Position start; | ||||||
|     @Getter |     @Getter | ||||||
|     private final Position end; |     private final Position end; | ||||||
|     @Getter |  | ||||||
|     @Setter |  | ||||||
|     private String algorithm; |  | ||||||
| 
 | 
 | ||||||
|     public Maze(final int width, final int height) { |     public Maze(final int width, final int height) { | ||||||
|         this(width, height, System.nanoTime()); |         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)); |         this(width, height, randomSeed, new Position(0, 0), new Position(width - 1, height - 1)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public Maze(final int width, |     public Maze(final int width, final int height, final long randomSeed, @NotNull final Position start, @NotNull final Position end) { | ||||||
|                 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"); | ||||||
|         } |         } | ||||||
|  | @ -79,12 +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(@NotNull final Tile[][] field, |     private Maze(@NotNull final Tile[][] field, final int width, final int height, @NotNull final Position start, @NotNull final Position end, final long randomSeed) { | ||||||
|                  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; | ||||||
|  | @ -93,25 +80,6 @@ public class Maze { | ||||||
|         this.end = end; |         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 |     @NotNull | ||||||
|     public Option<Tile> getTileAt(@NotNull final Position position) { |     public Option<Tile> getTileAt(@NotNull final Position position) { | ||||||
|         return this.getTileAt(position.x(), position.y()); |         return this.getTileAt(position.x(), position.y()); | ||||||
|  |  | ||||||
|  | @ -6,109 +6,108 @@ import org.jetbrains.annotations.NotNull; | ||||||
| 
 | 
 | ||||||
| public class HTMLRenderer implements Renderer<String> { | public class HTMLRenderer implements Renderer<String> { | ||||||
| 
 | 
 | ||||||
|     private static final String POSTAMBLE = """ |     private static final String POSTAMBLE = "<script>" | ||||||
|             <script> |             + "let userPath = [];" | ||||||
|             let userPath = []; |             + "const DIR_UNDEF = -1;" | ||||||
|             const DIR_UNDEF = -1; |             + "const DIR_SAME = 0;" | ||||||
|             const DIR_SAME = 0; |             + "const DIR_UP = 1;" | ||||||
|             const DIR_UP = 1; |             + "const DIR_RIGHT = 2;" | ||||||
|             const DIR_RIGHT = 2; |             + "const DIR_DOWN = 3;" | ||||||
|             const DIR_DOWN = 3; |             + "const DIR_LEFT = 4;" | ||||||
|             const DIR_LEFT = 4; |             + "function getCoords(cell) {" | ||||||
|             function getCoords(cell) { |             + "    return {" | ||||||
|                 return { |             + "        x: cell.cellIndex," | ||||||
|                     x: cell.cellIndex, |             + "        y: cell.parentElement.rowIndex" | ||||||
|                     y: cell.parentElement.rowIndex |             + "    };" | ||||||
|                 }; |             + "}" | ||||||
|             } |             + "function distance(prev, next) {" | ||||||
|             function distance(prev, next) { |             + "    return Math.abs(prev.x - next.x) + Math.abs(prev.y - next.y);" | ||||||
|                 return Math.abs(prev.x - next.x) + Math.abs(prev.y - next.y); |             + "}" | ||||||
|             } |             + "function direction(prev, next) {" | ||||||
|             function direction(prev, next) { |             + "    const dist = distance(prev, next);" | ||||||
|                 const dist = distance(prev, next); |             + "    if (dist === 0) {" | ||||||
|                 if (dist === 0) { |             + "        return DIR_SAME;" | ||||||
|                     return DIR_SAME; |             + "    }" | ||||||
|                 } |             + "    if (dist !== 1) {" | ||||||
|                 if (dist !== 1) { |             + "        return DIR_UNDEF;" | ||||||
|                     return DIR_UNDEF; |             + "    }" | ||||||
|                 } |             + "    if (next.x === prev.x) {" | ||||||
|                 if (next.x === prev.x) { |             + "        if (next.y === prev.y + 1) {" | ||||||
|                     if (next.y === prev.y + 1) { |             + "            return DIR_DOWN;" | ||||||
|                         return DIR_DOWN; |             + "        }" | ||||||
|                     } |             + "        return DIR_UP;" | ||||||
|                     return DIR_UP; |             + "    }" | ||||||
|                 } |             + "    if (next.x === prev.x + 1) {" | ||||||
|                 if (next.x === prev.x + 1) { |             + "        return DIR_RIGHT;" | ||||||
|                     return DIR_RIGHT; |             + "    }" | ||||||
|                 } |             + "    return DIR_LEFT;" | ||||||
|                 return DIR_LEFT; |             + "}" | ||||||
|             } |             + "(function () {" | ||||||
|             (function () { |             + "    const labyrinthTable = document.getElementById(\"labyrinth\");" | ||||||
|                 const labyrinthTable = document.getElementById("labyrinth"); |             + "    const labyrinthCells = labyrinthTable.getElementsByTagName(\"td\");" | ||||||
|                 const labyrinthCells = labyrinthTable.getElementsByTagName("td"); |             + "    const start = {x: 0, y: 0};" | ||||||
|                 const start = {x: 0, y: 0}; |             + "    const end = {" | ||||||
|                 const end = { |             + "        x: labyrinthTable.getElementsByTagName(\"tr\")[0].getElementsByTagName(\"td\").length - 1," | ||||||
|                     x: labyrinthTable.getElementsByTagName("tr")[0].getElementsByTagName("td").length - 1, |             + "        y: labyrinthTable.getElementsByTagName(\"tr\").length - 1" | ||||||
|                     y: labyrinthTable.getElementsByTagName("tr").length - 1 |             + "    };" | ||||||
|                 }; |             + "    for (let i = 0; i < labyrinthCells.length; i++) {" | ||||||
|                 for (let i = 0; i < labyrinthCells.length; i++) { |             + "        let cell = labyrinthCells.item(i);" | ||||||
|                     let cell = labyrinthCells.item(i); |             + "        cell.onclick = (event) => {" | ||||||
|                     cell.onclick = (event) => { |             + "            let target = event.target;" | ||||||
|                         let target = event.target; |             + "            const coords = getCoords(target);" | ||||||
|                         const coords = getCoords(target); |             + "            if (coords.x === end.x && coords.y === end.y) {" | ||||||
|                         if (coords.x === end.x && coords.y === end.y) { |             + "                alert(\"HOORAY! You did it! Congratulations!\")" | ||||||
|                             alert("HOORAY! You did it! Congratulations!") |             + "            }" | ||||||
|                         } |             + "            if (userPath.length === 0) {" | ||||||
|                         if (userPath.length === 0) { |             + "                if (coords.x === start.x && coords.y === start.y) {" | ||||||
|                             if (coords.x === start.x && coords.y === start.y) { |             + "                    userPath.push(coords);" | ||||||
|                                 userPath.push(coords); |             + "                    target.classList.toggle(\"user\");" | ||||||
|                                 target.classList.toggle("user"); |             + "                }" | ||||||
|                             } |             + "            } else {" | ||||||
|                         } else { |             + "                const dir = direction(userPath[userPath.length - 1], coords);" | ||||||
|                             const dir = direction(userPath[userPath.length - 1], coords); |             + "                switch (dir) {" | ||||||
|                             switch (dir) { |             + "                    case DIR_UNDEF:" | ||||||
|                                 case DIR_UNDEF: |             + "                        return;" | ||||||
|                                     return; |             + "                    case DIR_SAME:" | ||||||
|                                 case DIR_SAME: |             + "                        userPath.pop();" | ||||||
|                                     userPath.pop(); |             + "                        target.classList.toggle(\"user\");" | ||||||
|                                     target.classList.toggle("user"); |             + "                        return;" | ||||||
|                                     return; |             + "                    default:" | ||||||
|                                 default: |             + "                        if (userPath.find(value => value.x === coords.x && value.y === coords.y)) {" | ||||||
|                                     if (userPath.find(value => value.x === coords.x && value.y === coords.y)) { |             + "                            return;" | ||||||
|                                         return; |             + "                        } else {" | ||||||
|                                     } else { |             + "                            switch (dir) {" | ||||||
|                                         switch (dir) { |             + "                                case DIR_UP:" | ||||||
|                                             case DIR_UP: |             + "                                    if (target.classList.contains(\"bottom\")) {" | ||||||
|                                                 if (target.classList.contains("bottom")) { |             + "                                        return;" | ||||||
|                                                     return; |             + "                                    }" | ||||||
|                                                 } |             + "                                    break;" | ||||||
|                                                 break; |             + "                                case DIR_RIGHT:" | ||||||
|                                             case DIR_RIGHT: |             + "                                    if (target.classList.contains(\"left\")) {" | ||||||
|                                                 if (target.classList.contains("left")) { |             + "                                        return;" | ||||||
|                                                     return; |             + "                                    }" | ||||||
|                                                 } |             + "                                    break;" | ||||||
|                                                 break; |             + "                                case DIR_DOWN:" | ||||||
|                                             case DIR_DOWN: |             + "                                    if (target.classList.contains(\"top\")) {" | ||||||
|                                                 if (target.classList.contains("top")) { |             + "                                        return;" | ||||||
|                                                     return; |             + "                                    }" | ||||||
|                                                 } |             + "                                    break;" | ||||||
|                                                 break; |             + "                                case DIR_LEFT:" | ||||||
|                                             case DIR_LEFT: |             + "                                    if (target.classList.contains(\"right\")) {" | ||||||
|                                                 if (target.classList.contains("right")) { |             + "                                        return;" | ||||||
|                                                     return; |             + "                                    }" | ||||||
|                                                 } |             + "                                    break;" | ||||||
|                                                 break; |             + "                            }" | ||||||
|                                         } |             + "                            userPath.push(coords);" | ||||||
|                                         userPath.push(coords); |             + "                            target.classList.toggle(\"user\");" | ||||||
|                                         target.classList.toggle("user"); |             + "                            return;" | ||||||
|                                         return; |             + "                        }" | ||||||
|                                     } |             + "                }" | ||||||
|                             } |             + "            }" | ||||||
|                         } |             + "        };" | ||||||
|                     }; |             + "    }" | ||||||
|                 } |             + "})();" | ||||||
|             })(); |             + "</script></body></html>"; | ||||||
|             </script></body></html>"""; |  | ||||||
| 
 | 
 | ||||||
|     private HTMLRenderer() { |     private HTMLRenderer() { | ||||||
|     } |     } | ||||||
|  | @ -136,42 +135,33 @@ public class HTMLRenderer implements Renderer<String> { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private String getPreamble(@NotNull final Maze maze) { |     private String getPreamble(@NotNull final Maze maze) { | ||||||
|         return """ |         return "<!DOCTYPE html><html lang=\"en\">" + | ||||||
|                 <!DOCTYPE html> |                 "<head>" + | ||||||
|                 <html lang="en"> |                 "<title>Maze " + maze.getWidth() + "x" + maze.getHeight() + ", ID " + maze.getRandomSeed() + "</title>" + | ||||||
|                     <head> |                 "<meta charset=\"utf-8\">" + | ||||||
|                         <title>Maze %dx%d, ID %d, Algorithm %s</title> |                 "<style>" + | ||||||
|                         <meta charset="utf-8"> |                 "table{border-collapse:collapse;}" + | ||||||
|                         <style> |                 "td{border:0 solid black;height:1em;width:1em;cursor:pointer;}" + | ||||||
|                             table{border-collapse:collapse;} |                 "td.top{border-top-width:1px;}" + | ||||||
|                             td{border:0 solid black;height:1em;width:1em;cursor:pointer;} |                 "td.right{border-right-width:1px;}" + | ||||||
|                             td.top{border-top-width:1px;} |                 "td.bottom{border-bottom-width:1px;}" + | ||||||
|                             td.right{border-right-width:1px;} |                 "td.left{border-left-width:1px;}" + | ||||||
|                             td.bottom{border-bottom-width:1px;} |                 "td.user{background:hotpink;}" + | ||||||
|                             td.left{border-left-width:1px;} |                 "</style>" + | ||||||
|                             td.user{background:hotpink;} |                 "<script>" + | ||||||
|                         </style> |                 "let solution = false;" + | ||||||
|                         <script> |                 "function toggleSolution() {" + | ||||||
|                             let solution = false; |                 "let stylesheet = document.styleSheets[0];" + | ||||||
|                             function toggleSolution() { |                 "if(solution){" + | ||||||
|                                 let stylesheet = document.styleSheets[0]; |                 "stylesheet.deleteRule(0);" + | ||||||
|                                 if (solution) { |                 "}else{" + | ||||||
|                                     stylesheet.deleteRule(0); |                 "stylesheet.insertRule(\"td.solution{background-color:lightgray;}\", 0);" + | ||||||
|                                 } else { |                 "}" + | ||||||
|                                     stylesheet.insertRule("td.solution{background-color:lightgray;}", 0); |                 "solution = !solution;" + | ||||||
|                                 } |                 "}" + | ||||||
|                                 solution = !solution; |                 "</script>" + | ||||||
|                             } |                 "</head>" + | ||||||
|                         </script> |                 "<body>" + | ||||||
|                     </head> |                 "<input id=\"solutionbox\" type=\"checkbox\" onclick=\"toggleSolution()\"/><label for=\"solutionbox\">show solution</label>"; | ||||||
|                     <body> |  | ||||||
|                         <input id="solutionbox" type="checkbox" onclick="toggleSolution()"/> |  | ||||||
|                         <label for="solutionbox">show solution</label>""" |  | ||||||
|                 .formatted( |  | ||||||
|                         maze.getWidth(), |  | ||||||
|                         maze.getHeight(), |  | ||||||
|                         maze.getRandomSeed(), |  | ||||||
|                         maze.getAlgorithm() |  | ||||||
|                 ); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -23,7 +23,6 @@ class Generator { | ||||||
|     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())); | ||||||
|         result.setAlgorithm(this.maze.getAlgorithm()); |  | ||||||
|         result.setWidth(this.maze.getWidth()); |         result.setWidth(this.maze.getWidth()); | ||||||
|         result.setHeight(this.maze.getHeight()); |         result.setHeight(this.maze.getHeight()); | ||||||
|         final List<List<JsonCell>> rows = new ArrayList<>(); |         final List<List<JsonCell>> rows = new ArrayList<>(); | ||||||
|  |  | ||||||
|  | @ -34,7 +34,7 @@ class Generator { | ||||||
| 
 | 
 | ||||||
|         final PDDocument pdDocument = new PDDocument(); |         final PDDocument pdDocument = new PDDocument(); | ||||||
|         final PDDocumentInformation info = new PDDocumentInformation(); |         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); |         pdDocument.setDocumentInformation(info); | ||||||
|         final PDPage puzzlePage = new PDPage(new PDRectangle(pageWidth, pageHeight)); |         final PDPage puzzlePage = new PDPage(new PDRectangle(pageWidth, pageHeight)); | ||||||
|         final PDPage solutionPage = new PDPage(new PDRectangle(pageWidth, pageHeight)); |         final PDPage solutionPage = new PDPage(new PDRectangle(pageWidth, pageHeight)); | ||||||
|  |  | ||||||
|  | @ -4,7 +4,6 @@ import ch.fritteli.maze.generator.model.Maze; | ||||||
| import org.jetbrains.annotations.NotNull; | import org.jetbrains.annotations.NotNull; | ||||||
| 
 | 
 | ||||||
| import java.io.ByteArrayInputStream; | import java.io.ByteArrayInputStream; | ||||||
| import java.io.IOException; |  | ||||||
| 
 | 
 | ||||||
| public abstract class AbstractMazeInputStream extends ByteArrayInputStream { | public abstract class AbstractMazeInputStream extends ByteArrayInputStream { | ||||||
| 
 | 
 | ||||||
|  | @ -15,7 +14,7 @@ public abstract class AbstractMazeInputStream extends ByteArrayInputStream { | ||||||
|     public abstract void checkHeader(); |     public abstract void checkHeader(); | ||||||
| 
 | 
 | ||||||
|     @NotNull |     @NotNull | ||||||
|     public abstract Maze readMazeData() throws IOException; |     public abstract Maze readMazeData(); | ||||||
| 
 | 
 | ||||||
|     public byte readByte() { |     public byte readByte() { | ||||||
|         final int read = this.read(); |         final int read = this.read(); | ||||||
|  | @ -26,10 +25,6 @@ public abstract class AbstractMazeInputStream extends ByteArrayInputStream { | ||||||
|         return (byte) read; |         return (byte) read; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public int readByteAsInt() { |  | ||||||
|         return 0xff & this.readByte(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public int readInt() { |     public int readInt() { | ||||||
|         int result = 0; |         int result = 0; | ||||||
|         result |= (0xff & this.readByte()) << 24; |         result |= (0xff & this.readByte()) << 24; | ||||||
|  |  | ||||||
|  | @ -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}.<br> |  | ||||||
|  * A tile is stored in one byte: |  | ||||||
|  * <ul> |  | ||||||
|  *     <li>bits 0..2: always 0</li> |  | ||||||
|  *     <li>bit 3: 1=solution, 0=not solution</li> |  | ||||||
|  *     <li>bits 4..7: encode walls</li> |  | ||||||
|  * </ul> |  | ||||||
|  * The values for bits 4..7 are as follows: |  | ||||||
|  * <pre> |  | ||||||
|  * 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 |  | ||||||
|  * </pre> |  | ||||||
|  */ |  | ||||||
| @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<Direction> 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<Direction> walls, boolean solution) { |  | ||||||
|         try { |  | ||||||
|             final Constructor<Tile> 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); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -3,7 +3,6 @@ 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 ch.fritteli.maze.generator.serialization.CommonTileHandler; |  | ||||||
| import org.jetbrains.annotations.NotNull; | import org.jetbrains.annotations.NotNull; | ||||||
| 
 | 
 | ||||||
| public class MazeInputStreamV1 extends AbstractMazeInputStream { | public class MazeInputStreamV1 extends AbstractMazeInputStream { | ||||||
|  | @ -14,9 +13,6 @@ public class MazeInputStreamV1 extends AbstractMazeInputStream { | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void checkHeader() { |     public void checkHeader() { | ||||||
|         // 00 0x1a magic |  | ||||||
|         // 01 0xb1 magic |  | ||||||
|         // 02 0x01 version |  | ||||||
|         final byte magic1 = this.readByte(); |         final byte magic1 = this.readByte(); | ||||||
|         if (magic1 != SerializerDeserializerV1.MAGIC_BYTE_1) { |         if (magic1 != SerializerDeserializerV1.MAGIC_BYTE_1) { | ||||||
|             throw new IllegalArgumentException("Invalid maze data."); |             throw new IllegalArgumentException("Invalid maze data."); | ||||||
|  | @ -34,10 +30,6 @@ public class MazeInputStreamV1 extends AbstractMazeInputStream { | ||||||
|     @NotNull |     @NotNull | ||||||
|     @Override |     @Override | ||||||
|     public Maze readMazeData() { |     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 long randomSeed = this.readLong(); | ||||||
|         final int width = this.readInt(); |         final int width = this.readInt(); | ||||||
|         final int height = this.readInt(); |         final int height = this.readInt(); | ||||||
|  | @ -50,7 +42,7 @@ public class MazeInputStreamV1 extends AbstractMazeInputStream { | ||||||
|         for (int y = 0; y < height; y++) { |         for (int y = 0; y < height; y++) { | ||||||
|             for (int x = 0; x < width; x++) { |             for (int x = 0; x < width; x++) { | ||||||
|                 final byte bitmask = this.readByte(); |                 final byte bitmask = this.readByte(); | ||||||
|                 tiles[x][y] = CommonTileHandler.getTileForBitmask(bitmask); |                 tiles[x][y] = SerializerDeserializerV1.getTileForBitmask(bitmask); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,16 +3,12 @@ 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 ch.fritteli.maze.generator.serialization.CommonTileHandler; |  | ||||||
| import org.jetbrains.annotations.NotNull; | import org.jetbrains.annotations.NotNull; | ||||||
| 
 | 
 | ||||||
| public class MazeOutputStreamV1 extends AbstractMazeOutputStream { | public class MazeOutputStreamV1 extends AbstractMazeOutputStream { | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void writeHeader() { |     public void writeHeader() { | ||||||
|         // 00 0x1a magic |  | ||||||
|         // 01 0xb1 magic |  | ||||||
|         // 02 0x02 version |  | ||||||
|         this.writeByte(SerializerDeserializerV1.MAGIC_BYTE_1); |         this.writeByte(SerializerDeserializerV1.MAGIC_BYTE_1); | ||||||
|         this.writeByte(SerializerDeserializerV1.MAGIC_BYTE_2); |         this.writeByte(SerializerDeserializerV1.MAGIC_BYTE_2); | ||||||
|         this.writeByte(SerializerDeserializerV1.VERSION_BYTE); |         this.writeByte(SerializerDeserializerV1.VERSION_BYTE); | ||||||
|  | @ -20,10 +16,6 @@ public class MazeOutputStreamV1 extends AbstractMazeOutputStream { | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void writeMazeData(@NotNull final Maze maze) { |     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 long randomSeed = maze.getRandomSeed(); | ||||||
|         final int width = maze.getWidth(); |         final int width = maze.getWidth(); | ||||||
|         final int height = maze.getHeight(); |         final int height = maze.getHeight(); | ||||||
|  | @ -35,7 +27,7 @@ public class MazeOutputStreamV1 extends AbstractMazeOutputStream { | ||||||
|             for (int x = 0; x < width; x++) { |             for (int x = 0; x < width; x++) { | ||||||
|                 // We .get() it, because we want to crash hard if it is not available. |                 // We .get() it, because we want to crash hard if it is not available. | ||||||
|                 final Tile tile = maze.getTileAt(x, y).get(); |                 final Tile tile = maze.getTileAt(x, y).get(); | ||||||
|                 final byte bitmask = CommonTileHandler.getBitmaskForTile(tile); |                 final byte bitmask = SerializerDeserializerV1.getBitmaskForTile(tile); | ||||||
|                 this.writeByte(bitmask); |                 this.writeByte(bitmask); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| package ch.fritteli.maze.generator.serialization.v1; | 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.Maze; | ||||||
| import ch.fritteli.maze.generator.model.Tile; | import ch.fritteli.maze.generator.model.Tile; | ||||||
| import lombok.experimental.UtilityClass; | import lombok.experimental.UtilityClass; | ||||||
|  | @ -7,17 +8,38 @@ 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; | ||||||
|  | import java.util.EnumSet; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Header bytes are: |  * <pre> | ||||||
|  |  * 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 | ||||||
|  |  * </pre> | ||||||
|  |  * ==> bits 0..2: always 0; bit 3: 1=solution, 0=not solution; bits 4..7: encode walls | ||||||
|  |  * ==> first bytes are: | ||||||
|  * <pre> |  * <pre> | ||||||
|  *   byte  hex meaning |  *   byte  hex meaning | ||||||
|  *     00 0x1a magic |  *     00 0x1a magic | ||||||
|  *     01 0xb1 magic |  *     01 0xb1 magic | ||||||
|  *     02 0x01 version (0x00 -> dev, 0x01 -> stable) |  *     02 0x01 version (0x00 -> dev, 0x01 -> stable) | ||||||
|  * 03..10      random seed number (long) |  * 03..06      width (int) | ||||||
|  * 11..14      width (int) |  * 07..10      height (int) | ||||||
|  * 15..18      height (int) |  * 11..18      random seed number (long) | ||||||
|  * 19..        tiles |  * 19..        tiles | ||||||
|  * </pre> |  * </pre> | ||||||
|  * Extraneous space (poss. last nibble) is ignored. |  * Extraneous space (poss. last nibble) is ignored. | ||||||
|  | @ -28,6 +50,12 @@ public class SerializerDeserializerV1 { | ||||||
|     final byte MAGIC_BYTE_2 = (byte) 0xb1; |     final byte MAGIC_BYTE_2 = (byte) 0xb1; | ||||||
|     final byte VERSION_BYTE = 0x01; |     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. |      * Serializes the {@code maze} into a byte array. | ||||||
|      * |      * | ||||||
|  | @ -66,4 +94,55 @@ public class SerializerDeserializerV1 { | ||||||
|             throw new RuntimeException("Can not deserialize Maze from maze data.", e); |             throw new RuntimeException("Can not deserialize Maze from maze data.", e); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     @NotNull | ||||||
|  |     private Tile createTile(@NotNull final EnumSet<Direction> walls, boolean solution) { | ||||||
|  |         try { | ||||||
|  |             final Constructor<Tile> 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<Direction> 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); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -4,7 +4,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 ch.fritteli.maze.generator.serialization.AbstractMazeInputStream; | import ch.fritteli.maze.generator.serialization.AbstractMazeInputStream; | ||||||
| import ch.fritteli.maze.generator.serialization.CommonTileHandler; |  | ||||||
| import org.jetbrains.annotations.NotNull; | import org.jetbrains.annotations.NotNull; | ||||||
| 
 | 
 | ||||||
| public class MazeInputStreamV2 extends AbstractMazeInputStream { | public class MazeInputStreamV2 extends AbstractMazeInputStream { | ||||||
|  | @ -59,7 +58,7 @@ public class MazeInputStreamV2 extends AbstractMazeInputStream { | ||||||
|         for (int y = 0; y < height; y++) { |         for (int y = 0; y < height; y++) { | ||||||
|             for (int x = 0; x < width; x++) { |             for (int x = 0; x < width; x++) { | ||||||
|                 final byte bitmask = this.readByte(); |                 final byte bitmask = this.readByte(); | ||||||
|                 tiles[x][y] = CommonTileHandler.getTileForBitmask(bitmask); |                 tiles[x][y] = SerializerDeserializerV2.getTileForBitmask(bitmask); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,7 +4,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 ch.fritteli.maze.generator.serialization.AbstractMazeOutputStream; | import ch.fritteli.maze.generator.serialization.AbstractMazeOutputStream; | ||||||
| import ch.fritteli.maze.generator.serialization.CommonTileHandler; |  | ||||||
| import org.jetbrains.annotations.NotNull; | import org.jetbrains.annotations.NotNull; | ||||||
| 
 | 
 | ||||||
| public class MazeOutputStreamV2 extends AbstractMazeOutputStream { | public class MazeOutputStreamV2 extends AbstractMazeOutputStream { | ||||||
|  | @ -46,7 +45,7 @@ public class MazeOutputStreamV2 extends AbstractMazeOutputStream { | ||||||
|             for (int x = 0; x < width; x++) { |             for (int x = 0; x < width; x++) { | ||||||
|                 // We .get() it, because we want to crash hard if it is not available. |                 // We .get() it, because we want to crash hard if it is not available. | ||||||
|                 final Tile tile = maze.getTileAt(x, y).get(); |                 final Tile tile = maze.getTileAt(x, y).get(); | ||||||
|                 final byte bitmask = CommonTileHandler.getBitmaskForTile(tile); |                 final byte bitmask = SerializerDeserializerV2.getBitmaskForTile(tile); | ||||||
|                 this.writeByte(bitmask); |                 this.writeByte(bitmask); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| package ch.fritteli.maze.generator.serialization.v2; | 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.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; | ||||||
|  | @ -8,9 +9,29 @@ 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; | ||||||
|  | import java.util.EnumSet; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Header bytes are: |  * <pre> | ||||||
|  |  * 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 | ||||||
|  |  * </pre> | ||||||
|  |  * ==> bits 0..2: always 0; bit 3: 1=solution, 0=not solution; bits 4..7: encode walls ==> first bytes are: | ||||||
|  * <pre> |  * <pre> | ||||||
|  *   byte  hex meaning |  *   byte  hex meaning | ||||||
|  *     00 0x1a magic |  *     00 0x1a magic | ||||||
|  | @ -34,6 +55,12 @@ public class SerializerDeserializerV2 { | ||||||
|     final byte MAGIC_BYTE_2 = (byte) 0xb1; |     final byte MAGIC_BYTE_2 = (byte) 0xb1; | ||||||
|     final byte VERSION_BYTE = 0x02; |     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. |      * Serializes the {@code maze} into a byte array. | ||||||
|      * |      * | ||||||
|  | @ -72,4 +99,55 @@ public class SerializerDeserializerV2 { | ||||||
|             throw new RuntimeException("Can not deserialize Maze from maze data.", e); |             throw new RuntimeException("Can not deserialize Maze from maze data.", e); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     @NotNull | ||||||
|  |     private Tile createTile(@NotNull final EnumSet<Direction> walls, boolean solution) { | ||||||
|  |         try { | ||||||
|  |             final Constructor<Tile> 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<Direction> 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); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,78 +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 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 != SerializerDeserializerV3.MAGIC_BYTE_1) { |  | ||||||
|             throw new IllegalArgumentException("Invalid maze data."); |  | ||||||
|         } |  | ||||||
|         final byte magic2 = this.readByte(); |  | ||||||
|         if (magic2 != SerializerDeserializerV3.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); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,85 +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 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(SerializerDeserializerV3.MAGIC_BYTE_1); |  | ||||||
|         this.writeByte(SerializerDeserializerV3.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) { |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,92 +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: |  | ||||||
|  * <pre> |  | ||||||
|  *   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 |  | ||||||
|  * </pre> |  | ||||||
|  * Extraneous space (poss. last nibble) is ignored. |  | ||||||
|  */ |  | ||||||
| @UtilityClass |  | ||||||
| public class SerializerDeserializerV3 { |  | ||||||
| 
 |  | ||||||
|     final byte MAGIC_BYTE_1 = 0x1a; |  | ||||||
|     final byte MAGIC_BYTE_2 = (byte) 0xb1; |  | ||||||
|     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<Maze> 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); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -6,7 +6,6 @@ | ||||||
|   "additionalProperties": false, |   "additionalProperties": false, | ||||||
|   "required": [ |   "required": [ | ||||||
|     "id", |     "id", | ||||||
|     "algorithm", |  | ||||||
|     "width", |     "width", | ||||||
|     "height", |     "height", | ||||||
|     "start", |     "start", | ||||||
|  | @ -18,10 +17,6 @@ | ||||||
|       "type": "string", |       "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." |       "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": { |     "width": { | ||||||
|       "type": "integer", |       "type": "integer", | ||||||
|       "minimum": 1 |       "minimum": 1 | ||||||
|  |  | ||||||
|  | @ -13,8 +13,6 @@ class SerializerDeserializerV1Test { | ||||||
|         new RandomDepthFirst(expected).run(); |         new RandomDepthFirst(expected).run(); | ||||||
|         final byte[] bytes = SerializerDeserializerV1.serialize(expected); |         final byte[] bytes = SerializerDeserializerV1.serialize(expected); | ||||||
|         final Maze result = SerializerDeserializerV1.deserialize(bytes); |         final Maze result = SerializerDeserializerV1.deserialize(bytes); | ||||||
|         assertThat(result.getAlgorithm()).isNull(); |  | ||||||
|         expected.setAlgorithm(null); |  | ||||||
|         assertThat(result).isEqualTo(expected); |         assertThat(result).isEqualTo(expected); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -24,8 +22,6 @@ class SerializerDeserializerV1Test { | ||||||
|         new RandomDepthFirst(expected).run(); |         new RandomDepthFirst(expected).run(); | ||||||
|         final byte[] bytes = SerializerDeserializerV1.serialize(expected); |         final byte[] bytes = SerializerDeserializerV1.serialize(expected); | ||||||
|         final Maze result = SerializerDeserializerV1.deserialize(bytes); |         final Maze result = SerializerDeserializerV1.deserialize(bytes); | ||||||
|         assertThat(result.getAlgorithm()).isNull(); |  | ||||||
|         expected.setAlgorithm(null); |  | ||||||
|         assertThat(result).isEqualTo(expected); |         assertThat(result).isEqualTo(expected); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -35,8 +31,6 @@ class SerializerDeserializerV1Test { | ||||||
|         new RandomDepthFirst(expected).run(); |         new RandomDepthFirst(expected).run(); | ||||||
|         final byte[] bytes = SerializerDeserializerV1.serialize(expected); |         final byte[] bytes = SerializerDeserializerV1.serialize(expected); | ||||||
|         final Maze result = SerializerDeserializerV1.deserialize(bytes); |         final Maze result = SerializerDeserializerV1.deserialize(bytes); | ||||||
|         assertThat(result.getAlgorithm()).isNull(); |  | ||||||
|         expected.setAlgorithm(null); |  | ||||||
|         assertThat(result).isEqualTo(expected); |         assertThat(result).isEqualTo(expected); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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; |  | ||||||
| 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); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,40 +0,0 @@ | ||||||
| package ch.fritteli.maze.generator.serialization.v3; |  | ||||||
| 
 |  | ||||||
| import ch.fritteli.maze.generator.algorithm.RandomDepthFirst; |  | ||||||
| import ch.fritteli.maze.generator.algorithm.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); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue