Refactor package structure.
This commit is contained in:
		
							parent
							
								
									0bdbd7d0ef
								
							
						
					
					
						commit
						afd05f6871
					
				
					 17 changed files with 840 additions and 786 deletions
				
			
		|  | @ -3,6 +3,7 @@ package ch.fritteli.labyrinth; | |||
| import io.vavr.control.Option; | ||||
| import lombok.Getter; | ||||
| import lombok.NonNull; | ||||
| import org.jetbrains.annotations.Nullable; | ||||
| 
 | ||||
| import java.util.Deque; | ||||
| import java.util.LinkedList; | ||||
|  | @ -38,18 +39,35 @@ public class Labyrinth { | |||
|         this.generate(); | ||||
|     } | ||||
| 
 | ||||
|     Tile getTileAt(@NonNull final Position position) { | ||||
|     @NonNull | ||||
|     public Tile getTileAt(@NonNull final Position position) { | ||||
|         return this.getTileAt(position.getX(), position.getY()); | ||||
|     } | ||||
| 
 | ||||
|     Tile getTileAt(final int x, final int y) { | ||||
|     @NonNull | ||||
|     public Tile getTileAtOrNull(@NonNull final Position position) { | ||||
|         return this.getTileAtOrNull(position.getX(), position.getY()); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public Tile getTileAt(final int x, final int y) { | ||||
|         return this.field[x][y]; | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     public Tile getTileAtOrNull(final int x, final int y) { | ||||
|         if (x < 0 || y < 0 || x >= this.width || y >= this.height) { | ||||
|             return null; | ||||
|         } | ||||
|         return this.getTileAt(x, y); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     Tile getStartTile() { | ||||
|         return this.getTileAt(this.start); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     Tile getEndTile() { | ||||
|         return this.getTileAt(this.end); | ||||
|     } | ||||
|  |  | |||
|  | @ -1,5 +1,10 @@ | |||
| package ch.fritteli.labyrinth; | ||||
| 
 | ||||
| import ch.fritteli.labyrinth.renderer.html.HTMLRenderer; | ||||
| import ch.fritteli.labyrinth.renderer.htmlfile.HTMLFileRenderer; | ||||
| import ch.fritteli.labyrinth.renderer.pdffile.PDFFileRenderer; | ||||
| import ch.fritteli.labyrinth.renderer.textfile.TextFileRenderer; | ||||
| import ch.fritteli.labyrinth.renderer.text.TextRenderer; | ||||
| import lombok.NonNull; | ||||
| 
 | ||||
| import java.nio.file.Path; | ||||
|  |  | |||
|  | @ -1,301 +0,0 @@ | |||
| package ch.fritteli.labyrinth; | ||||
| 
 | ||||
| import lombok.NonNull; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.Value; | ||||
| import org.apache.pdfbox.pdmodel.PDDocument; | ||||
| import org.apache.pdfbox.pdmodel.PDDocumentInformation; | ||||
| import org.apache.pdfbox.pdmodel.PDPage; | ||||
| import org.apache.pdfbox.pdmodel.PDPageContentStream; | ||||
| import org.apache.pdfbox.pdmodel.common.PDRectangle; | ||||
| import org.jetbrains.annotations.Nullable; | ||||
| 
 | ||||
| import java.awt.*; | ||||
| import java.io.ByteArrayOutputStream; | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| public class PDFRenderer implements Renderer<byte[]> { | ||||
|     private static final float MARGIN = 10; | ||||
|     private static final float SCALE = 10; | ||||
| 
 | ||||
|     private PDFRenderer() { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public static PDFRenderer newInstance() { | ||||
|         return new PDFRenderer(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     @NonNull | ||||
|     public byte[] render(@NonNull final Labyrinth labyrinth) { | ||||
|         final Generator generator = new Generator(labyrinth); | ||||
|         return generator.generate(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     @RequiredArgsConstructor | ||||
|     private static class Generator { | ||||
|         @NonNull | ||||
|         private final Labyrinth labyrinth; | ||||
| 
 | ||||
|         private static boolean isValid(@NonNull final Position position) { | ||||
|             return position.getX() >= 0 && position.getY() >= 0; | ||||
|         } | ||||
| 
 | ||||
|         public byte[] generate() { | ||||
|             final float pageWidth = this.labyrinth.getWidth() * SCALE + 2 * MARGIN; | ||||
|             final float pageHeight = this.labyrinth.getHeight() * SCALE + 2 * MARGIN; | ||||
| 
 | ||||
|             final PDDocument pdDocument = new PDDocument(); | ||||
|             final PDDocumentInformation info = new PDDocumentInformation(); | ||||
|             info.setTitle("Labyrinth " + this.labyrinth.getWidth() + "x" + this.labyrinth.getHeight() + ", ID " + this.labyrinth.getRandomSeed()); | ||||
|             pdDocument.setDocumentInformation(info); | ||||
|             final PDPage page = new PDPage(new PDRectangle(pageWidth, pageHeight)); | ||||
|             final PDPage solution = new PDPage(new PDRectangle(pageWidth, pageHeight)); | ||||
|             pdDocument.addPage(page); | ||||
|             pdDocument.addPage(solution); | ||||
|             try (final PDPageContentStream labyrinthPageContentStream = new PDPageContentStream(pdDocument, page); | ||||
|                  final PDPageContentStream solutionPageContentStream = new PDPageContentStream(pdDocument, solution)) { | ||||
|                 setUpPageContentStream(labyrinthPageContentStream); | ||||
|                 setUpPageContentStream(solutionPageContentStream); | ||||
|                 this.drawHorizonzalLines(labyrinthPageContentStream, solutionPageContentStream); | ||||
|                 this.drawVerticalLines(labyrinthPageContentStream, solutionPageContentStream); | ||||
|                 this.drawSolution(solutionPageContentStream); | ||||
|             } catch (IOException e) { | ||||
|                 e.printStackTrace(); | ||||
|             } | ||||
|             final ByteArrayOutputStream output = new ByteArrayOutputStream(); | ||||
|             try { | ||||
|                 pdDocument.save(output); | ||||
|                 pdDocument.close(); | ||||
|             } catch (IOException e) { | ||||
|                 e.printStackTrace(); | ||||
|             } | ||||
|             return output.toByteArray(); | ||||
|         } | ||||
| 
 | ||||
|         private void setUpPageContentStream(@NonNull final PDPageContentStream pageContentStream) throws IOException { | ||||
|             pageContentStream.setLineCapStyle(BasicStroke.CAP_ROUND); | ||||
|             pageContentStream.setLineJoinStyle(BasicStroke.JOIN_ROUND); | ||||
|             pageContentStream.setLineWidth(1.0f); | ||||
|             pageContentStream.setStrokingColor(Color.BLACK); | ||||
|             pageContentStream.setNonStrokingColor(Color.BLACK); | ||||
|         } | ||||
| 
 | ||||
|         private void drawHorizonzalLines(@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. | ||||
|             Coordinate coordinate = new Coordinate(0f, 0f); | ||||
|             for (int y = 0; y < this.labyrinth.getHeight(); y++) { | ||||
|                 boolean isPainting = false; | ||||
|                 coordinate = coordinate.withY(y); | ||||
|                 for (int x = 0; x < this.labyrinth.getWidth(); x++) { | ||||
|                     final Tile currentTile = this.labyrinth.getTileAt(x, y); | ||||
|                     coordinate = coordinate.withX(x); | ||||
|                     if (currentTile.hasWallAt(Direction.TOP)) { | ||||
|                         if (!isPainting) { | ||||
|                             for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                                 contentStream.moveTo(coordinate.getX(), coordinate.getY()); | ||||
|                             } | ||||
|                             isPainting = true; | ||||
|                         } | ||||
|                     } else { | ||||
|                         if (isPainting) { | ||||
|                             for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                                 contentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|                                 contentStream.stroke(); | ||||
|                             } | ||||
|                             isPainting = false; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if (isPainting) { | ||||
|                     coordinate = coordinate.withX(this.labyrinth.getWidth()); | ||||
|                     for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                         contentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|                         contentStream.stroke(); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             boolean isPainting = false; | ||||
|             int y = this.labyrinth.getHeight(); | ||||
|             coordinate = coordinate.withY(this.labyrinth.getHeight()); | ||||
|             for (int x = 0; x < this.labyrinth.getWidth(); x++) { | ||||
|                 final Tile currentTile = this.labyrinth.getTileAt(x, y - 1); | ||||
|                 coordinate = coordinate.withX(x); | ||||
|                 if (currentTile.hasWallAt(Direction.BOTTOM)) { | ||||
|                     if (!isPainting) { | ||||
|                         for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                             contentStream.moveTo(coordinate.getX(), coordinate.getY()); | ||||
|                         } | ||||
|                         isPainting = true; | ||||
|                     } | ||||
|                 } else { | ||||
|                     if (isPainting) { | ||||
|                         for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                             contentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|                             contentStream.stroke(); | ||||
|                         } | ||||
|                         isPainting = false; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if (isPainting) { | ||||
|                 coordinate = coordinate.withX(this.labyrinth.getWidth()); | ||||
|                 for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                     contentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|                     contentStream.stroke(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         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. | ||||
|             Coordinate coordinate = new Coordinate(0f, 0f); | ||||
|             for (int x = 0; x < this.labyrinth.getWidth(); x++) { | ||||
|                 boolean isPainting = false; | ||||
|                 coordinate = coordinate.withX(x); | ||||
|                 for (int y = 0; y < this.labyrinth.getHeight(); y++) { | ||||
|                     final Tile currentTile = this.labyrinth.getTileAt(x, y); | ||||
|                     coordinate = coordinate.withY(y); | ||||
|                     if (currentTile.hasWallAt(Direction.LEFT)) { | ||||
|                         if (!isPainting) { | ||||
|                             for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                                 contentStream.moveTo(coordinate.getX(), coordinate.getY()); | ||||
|                             } | ||||
|                             isPainting = true; | ||||
|                         } | ||||
|                     } else { | ||||
|                         if (isPainting) { | ||||
|                             for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                                 contentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|                                 contentStream.stroke(); | ||||
|                             } | ||||
|                             isPainting = false; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if (isPainting) { | ||||
|                     coordinate = coordinate.withY(this.labyrinth.getHeight()); | ||||
|                     for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                         contentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|                         contentStream.stroke(); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             boolean isPainting = false; | ||||
|             int x = this.labyrinth.getWidth(); | ||||
|             coordinate = coordinate.withX(this.labyrinth.getWidth()); | ||||
|             for (int y = 0; y < this.labyrinth.getHeight(); y++) { | ||||
|                 final Tile currentTile = this.labyrinth.getTileAt(x - 1, y); | ||||
|                 coordinate = coordinate.withY(y); | ||||
|                 if (currentTile.hasWallAt(Direction.RIGHT)) { | ||||
|                     if (!isPainting) { | ||||
|                         for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                             contentStream.moveTo(coordinate.getX(), coordinate.getY()); | ||||
|                         } | ||||
|                         isPainting = true; | ||||
|                     } | ||||
|                 } else { | ||||
|                     if (isPainting) { | ||||
|                         for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                             contentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|                             contentStream.stroke(); | ||||
|                         } | ||||
|                         isPainting = false; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if (isPainting) { | ||||
|                 coordinate = coordinate.withY(this.labyrinth.getHeight()); | ||||
|                 for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                     contentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|                     contentStream.stroke(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         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. | ||||
|             final Position end = this.labyrinth.getEnd(); | ||||
|             Position currentPosition = this.labyrinth.getStart(); | ||||
|             Position previousPosition = null; | ||||
|             SolutionCoordinate coordinate = new SolutionCoordinate(currentPosition.getX(), currentPosition.getY()); | ||||
|             pageContentStream.moveTo(coordinate.getX(), coordinate.getY()); | ||||
|             do { | ||||
|                 Position newCurrent = this.findNextSolutionPosition(previousPosition, currentPosition); | ||||
|                 previousPosition = currentPosition; | ||||
|                 currentPosition = newCurrent; | ||||
|                 coordinate = new SolutionCoordinate(currentPosition.getX(), currentPosition.getY()); | ||||
|                 pageContentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|             } while (!currentPosition.equals(end)); | ||||
|             pageContentStream.stroke(); | ||||
|         } | ||||
| 
 | ||||
|         @NonNull | ||||
|         private Position findNextSolutionPosition(@Nullable final Position previousPosition, @NonNull final Position currentPosition) { | ||||
|             final Tile currentTile = this.labyrinth.getTileAt(currentPosition); | ||||
|             for (final Direction direction : Direction.values()) { | ||||
|                 if (!currentTile.hasWallAt(direction)) { | ||||
|                     final Position position = direction.translate(currentPosition); | ||||
|                     if (position.equals(previousPosition) || !isValid(position)) { | ||||
|                         continue; | ||||
|                     } | ||||
|                     if (this.labyrinth.getTileAt(position).isSolution()) { | ||||
|                         return position; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             throw new IllegalStateException("We *SHOULD* never have gotten here. ... famous last words ..."); | ||||
|         } | ||||
| 
 | ||||
|         @Value | ||||
|         private class Coordinate { | ||||
|             float x; | ||||
|             float y; | ||||
| 
 | ||||
|             public Coordinate(final float x, final float y) { | ||||
|                 this.x = x; | ||||
|                 this.y = y; | ||||
|             } | ||||
| 
 | ||||
|             private float calcX(final int x) { | ||||
|                 return x * SCALE + MARGIN; | ||||
|             } | ||||
| 
 | ||||
|             private float calcY(final int y) { | ||||
|                 return (Generator.this.labyrinth.getHeight() - y) * SCALE + MARGIN; | ||||
|             } | ||||
| 
 | ||||
|             public Coordinate withX(final int x) { | ||||
|                 return new Coordinate(calcX(x), this.y); | ||||
|             } | ||||
| 
 | ||||
|             public Coordinate withY(final int y) { | ||||
|                 return new Coordinate(this.x, calcY(y)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         @Value | ||||
|         private class SolutionCoordinate { | ||||
|             float x; | ||||
|             float y; | ||||
| 
 | ||||
|             public SolutionCoordinate(final int x, final int y) { | ||||
|                 this.x = calcX(x); | ||||
|                 this.y = calcY(y); | ||||
|             } | ||||
| 
 | ||||
|             private float calcX(final int x) { | ||||
|                 return x * SCALE + SCALE / 2 + MARGIN; | ||||
|             } | ||||
| 
 | ||||
|             private float calcY(final int y) { | ||||
|                 return (Generator.this.labyrinth.getHeight() - y) * SCALE - SCALE / 2 + MARGIN; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1,381 +0,0 @@ | |||
| package ch.fritteli.labyrinth; | ||||
| 
 | ||||
| import lombok.AccessLevel; | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.NoArgsConstructor; | ||||
| import lombok.NonNull; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.experimental.FieldDefaults; | ||||
| import org.jetbrains.annotations.Nullable; | ||||
| 
 | ||||
| public class TextRenderer implements Renderer<String> { | ||||
|     private boolean renderSolution; | ||||
| 
 | ||||
|     private TextRenderer() { | ||||
|         this.renderSolution = false; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public static TextRenderer newInstance() { | ||||
|         return new TextRenderer(); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public TextRenderer setRenderSolution(final boolean renderSolution) { | ||||
|         this.renderSolution = renderSolution; | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public String render(@NonNull final Labyrinth labyrinth) { | ||||
|         if (labyrinth.getWidth() == 0 || labyrinth.getHeight() == 0) { | ||||
|             return ""; | ||||
|         } | ||||
|         final Generator generator = new Generator(labyrinth, this.renderSolution); | ||||
|         final StringBuilder sb = new StringBuilder(); | ||||
|         while (generator.hasNext()) { | ||||
|             sb.append(generator.next()); | ||||
|         } | ||||
|         return sb.toString(); | ||||
|     } | ||||
| 
 | ||||
|     @RequiredArgsConstructor(access = AccessLevel.PRIVATE) | ||||
|     static class Generator { | ||||
|         @NonNull | ||||
|         private final Labyrinth labyrinth; | ||||
|         private final boolean renderSolution; | ||||
|         private int x = 0; | ||||
|         private int y = 0; | ||||
|         private int line = 0; | ||||
| 
 | ||||
|         private boolean hasNext() { | ||||
|             return this.y < this.labyrinth.getHeight(); | ||||
|         } | ||||
| 
 | ||||
|         private String next() { | ||||
|             final Tile currentTile = this.labyrinth.getTileAt(this.x, this.y); | ||||
|             final Tile topTile = this.getTileOrNull(this.x, this.y - 1); | ||||
|             final Tile rightTile = this.getTileOrNull(this.x + 1, this.y); | ||||
|             final Tile bottomTile = this.getTileOrNull(this.x, this.y + 1); | ||||
|             final Tile leftTile = this.getTileOrNull(this.x - 1, this.y); | ||||
|             final String s; | ||||
|             switch (this.line) { | ||||
|                 case 0: | ||||
|                     s = this.renderTopLine(currentTile, leftTile, topTile); | ||||
|                     break; | ||||
|                 case 1: | ||||
|                     s = this.renderCenterLine(currentTile, topTile, rightTile, bottomTile, leftTile); | ||||
|                     break; | ||||
|                 case 2: | ||||
|                     s = this.renderBottomLine(currentTile, leftTile); | ||||
|                     break; | ||||
|                 default: | ||||
|                     s = ""; | ||||
|                     break; | ||||
|             } | ||||
|             this.prepareNextStep(); | ||||
|             return s; | ||||
|         } | ||||
| 
 | ||||
|         private void prepareNextStep() { | ||||
|             // do some magic ... | ||||
|             this.x++; | ||||
|             if (this.x == this.labyrinth.getWidth()) { | ||||
|                 // Reached the end of the row? | ||||
|                 // On to the next line then! | ||||
|                 this.x = 0; | ||||
|                 this.line++; | ||||
|             } | ||||
|             if (this.line == 2 && this.y < this.labyrinth.getHeight() - 1) { | ||||
|                 // Finished rendering the center line, and more rows available? | ||||
|                 // On to the next row then! | ||||
|                 this.line = 0; | ||||
|                 this.y++; | ||||
|             } | ||||
|             if (this.line == 3) { | ||||
|                 // Finished rendering the bottom line (of the last row)? | ||||
|                 // Increment row counter one more time => this is the exit condition. | ||||
|                 this.line = 0; | ||||
|                 this.y++; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private Tile getTileOrNull(final int x, final int y) { | ||||
|             if (x < 0 || y < 0 || x >= this.labyrinth.getWidth() || y >= this.labyrinth.getHeight()) { | ||||
|                 return null; | ||||
|             } | ||||
|             return this.labyrinth.getTileAt(x, y); | ||||
|         } | ||||
| 
 | ||||
|         private String renderTopLine(@NonNull final Tile currentTile, @Nullable final Tile leftTile, @Nullable final Tile topTile) { | ||||
|             final CharDefinition charDef1 = new CharDefinition(); | ||||
|             final CharDefinition charDef2 = new CharDefinition(); | ||||
|             final CharDefinition charDef3 = new CharDefinition(); | ||||
|             String result; | ||||
|             if (currentTile.hasWallAt(Direction.LEFT)) { | ||||
|                 charDef1.down(); | ||||
|             } | ||||
|             if (currentTile.hasWallAt(Direction.TOP)) { | ||||
|                 charDef1.right(); | ||||
|                 charDef2.horizontal(); | ||||
|                 charDef3.left(); | ||||
|             } else { | ||||
|                 if (this.isSolution(currentTile) && (this.isSolution(topTile) || topTile == null)) { | ||||
|                     charDef2.solution().vertical(); | ||||
|                 } | ||||
|             } | ||||
|             if (currentTile.hasWallAt(Direction.RIGHT)) { | ||||
|                 charDef3.down(); | ||||
|             } | ||||
|             if (this.hasWallAt(leftTile, Direction.TOP)) { | ||||
|                 charDef1.left(); | ||||
|             } | ||||
|             if (this.hasWallAt(topTile, Direction.LEFT)) { | ||||
|                 charDef1.up(); | ||||
|             } | ||||
|             if (this.hasWallAt(topTile, Direction.RIGHT)) { | ||||
|                 charDef3.up(); | ||||
|             } | ||||
|             result = charDef1.toString() + charDef2; | ||||
|             if (this.x == this.labyrinth.getWidth() - 1) { | ||||
|                 result += charDef3 + "\n"; | ||||
|             } | ||||
|             return result; | ||||
|         } | ||||
| 
 | ||||
|         private String renderCenterLine(@NonNull final Tile currentTile, | ||||
|                                         @Nullable final Tile topTile, | ||||
|                                         @Nullable final Tile rightTile, | ||||
|                                         @Nullable final Tile bottomTile, | ||||
|                                         @Nullable final Tile leftTile) { | ||||
|             final CharDefinition charDef1 = new CharDefinition(); | ||||
|             final CharDefinition charDef2 = new CharDefinition(); | ||||
|             final CharDefinition charDef3 = new CharDefinition(); | ||||
|             String result; | ||||
|             if (currentTile.hasWallAt(Direction.LEFT)) { | ||||
|                 charDef1.vertical(); | ||||
|             } else { | ||||
|                 if (this.isSolution(currentTile) && this.isSolution(leftTile)) { | ||||
|                     charDef1.solution().horizontal(); | ||||
|                 } | ||||
|             } | ||||
|             if (this.isSolution(currentTile)) { | ||||
|                 charDef2.solution(); | ||||
|                 if (!currentTile.hasWallAt(Direction.LEFT) && (this.isSolution(leftTile) || leftTile == null)) { | ||||
|                     charDef2.left(); | ||||
|                 } | ||||
|                 if (!currentTile.hasWallAt(Direction.TOP) && (this.isSolution(topTile) || topTile == null)) { | ||||
|                     charDef2.up(); | ||||
|                 } | ||||
|                 if (!currentTile.hasWallAt(Direction.RIGHT) && (this.isSolution(rightTile) || rightTile == null)) { | ||||
|                     charDef2.right(); | ||||
|                 } | ||||
|                 if (!currentTile.hasWallAt(Direction.BOTTOM) && (this.isSolution(bottomTile) || bottomTile == null)) { | ||||
|                     charDef2.down(); | ||||
|                 } | ||||
|             } | ||||
|             if (currentTile.hasWallAt(Direction.RIGHT)) { | ||||
|                 charDef3.vertical(); | ||||
|             } else { | ||||
|                 if (this.isSolution(currentTile) && this.isSolution(rightTile)) { | ||||
|                     charDef3.solution().horizontal(); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             result = charDef1.toString() + charDef2; | ||||
| 
 | ||||
|             if (this.x == this.labyrinth.getWidth() - 1) { | ||||
|                 result += charDef3 + "\n"; | ||||
|             } | ||||
| 
 | ||||
|             return result; | ||||
|         } | ||||
| 
 | ||||
|         private String renderBottomLine(@NonNull final Tile currentTile, @Nullable final Tile leftTile) { | ||||
|             String result; | ||||
|             final CharDefinition charDef1 = new CharDefinition(); | ||||
|             final CharDefinition charDef2 = new CharDefinition(); | ||||
|             final CharDefinition charDef3 = new CharDefinition(); | ||||
| 
 | ||||
|             if (currentTile.hasWallAt(Direction.LEFT)) { | ||||
|                 charDef1.up(); | ||||
|             } | ||||
|             if (currentTile.hasWallAt(Direction.BOTTOM)) { | ||||
|                 charDef1.right(); | ||||
|                 charDef2.horizontal(); | ||||
|                 charDef3.left(); | ||||
|             } else { | ||||
|                 if (this.isSolution(currentTile)) { | ||||
|                     charDef2.solution().vertical(); | ||||
|                 } | ||||
|             } | ||||
|             if (currentTile.hasWallAt(Direction.RIGHT)) { | ||||
|                 charDef3.up(); | ||||
|             } | ||||
|             if (this.hasWallAt(leftTile, Direction.BOTTOM)) { | ||||
|                 charDef1.left(); | ||||
|             } | ||||
| 
 | ||||
|             result = charDef1.toString() + charDef2; | ||||
| 
 | ||||
|             if (this.x == this.labyrinth.getWidth() - 1) { | ||||
|                 result += charDef3; | ||||
|             } | ||||
|             return result; | ||||
|         } | ||||
| 
 | ||||
|         private boolean hasWallAt(@Nullable final Tile tile, @NonNull final Direction direction) { | ||||
|             return tile != null && tile.hasWallAt(direction); | ||||
|         } | ||||
| 
 | ||||
|         private boolean isSolution(@Nullable final Tile tile) { | ||||
|             return this.renderSolution && tile != null && tile.isSolution(); | ||||
|         } | ||||
| 
 | ||||
|         @FieldDefaults(level = AccessLevel.PRIVATE) | ||||
|         @NoArgsConstructor | ||||
|         @AllArgsConstructor | ||||
|         static class CharDefinition { | ||||
|             // ─ | ||||
|             static final String HORIZONTAL = "\u2500"; | ||||
|             // │ | ||||
|             static final String VERTICAL = "\u2502"; | ||||
|             // ╴ | ||||
|             static final String LEFT = "\u2574"; | ||||
|             // ╵ | ||||
|             static final String UP = "\u2575"; | ||||
|             // ╶ | ||||
|             static final String RIGHT = "\u2576"; | ||||
|             // ╷ | ||||
|             static final String DOWN = "\u2577"; | ||||
|             // ┌ | ||||
|             static final String DOWN_RIGHT = "\u250c"; | ||||
|             // ┐ | ||||
|             static final String DOWN_LEFT = "\u2510"; | ||||
|             // └ | ||||
|             static final String UP_RIGHT = "\u2514"; | ||||
|             // ┘ | ||||
|             static final String UP_LEFT = "\u2518"; | ||||
|             // ├ | ||||
|             static final String VERTICAL_RIGHT = "\u251c"; | ||||
|             // ┤ | ||||
|             static final String VERTICAL_LEFT = "\u2524"; | ||||
|             // ┬ | ||||
|             static final String HORIZONTAL_DOWN = "\u252c"; | ||||
|             // ┴ | ||||
|             static final String HORIZONTAL_UP = "\u2534"; | ||||
|             // ┼ | ||||
|             static final String CROSS = "\u253c"; | ||||
|             // ╭ | ||||
|             static final String SOLUTION_DOWN_RIGHT = "\u256d"; | ||||
|             // ╮ | ||||
|             static final String SOLUTION_DOWN_LEFT = "\u256e"; | ||||
|             // ╯ | ||||
|             static final String SOLUTION_UP_LEFT = "\u256f"; | ||||
|             // ╰ | ||||
|             static final String SOLUTION_UP_RIGHT = "\u2570"; | ||||
|             boolean up = false; | ||||
|             boolean down = false; | ||||
|             boolean left = false; | ||||
|             boolean right = false; | ||||
|             boolean solution = false; | ||||
| 
 | ||||
|             CharDefinition solution() { | ||||
|                 this.solution = true; | ||||
|                 return this; | ||||
|             } | ||||
| 
 | ||||
|             CharDefinition up() { | ||||
|                 this.up = true; | ||||
|                 return this; | ||||
|             } | ||||
| 
 | ||||
|             CharDefinition down() { | ||||
|                 this.down = true; | ||||
|                 return this; | ||||
|             } | ||||
| 
 | ||||
|             CharDefinition vertical() { | ||||
|                 return this.up().down(); | ||||
|             } | ||||
| 
 | ||||
|             CharDefinition left() { | ||||
|                 this.left = true; | ||||
|                 return this; | ||||
|             } | ||||
| 
 | ||||
|             CharDefinition right() { | ||||
|                 this.right = true; | ||||
|                 return this; | ||||
|             } | ||||
| 
 | ||||
|             CharDefinition horizontal() { | ||||
|                 return this.left().right(); | ||||
|             } | ||||
| 
 | ||||
|             public String toString() { | ||||
|                 if (this.up) { | ||||
|                     if (this.down) { | ||||
|                         if (this.left) { | ||||
|                             if (this.right) { | ||||
|                                 return CROSS; | ||||
|                             } else { | ||||
|                                 return VERTICAL_LEFT; | ||||
|                             } | ||||
|                         } else { | ||||
|                             if (this.right) { | ||||
|                                 return VERTICAL_RIGHT; | ||||
|                             } else { | ||||
|                                 return VERTICAL; | ||||
|                             } | ||||
|                         } | ||||
|                     } else { | ||||
|                         if (this.left) { | ||||
|                             if (this.right) { | ||||
|                                 return HORIZONTAL_UP; | ||||
|                             } else { | ||||
|                                 return this.solution ? SOLUTION_UP_LEFT : UP_LEFT; | ||||
|                             } | ||||
|                         } else { | ||||
|                             if (this.right) { | ||||
|                                 return this.solution ? SOLUTION_UP_RIGHT : UP_RIGHT; | ||||
|                             } else { | ||||
|                                 return UP; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } else { | ||||
|                     if (this.down) { | ||||
|                         if (this.left) { | ||||
|                             if (this.right) { | ||||
|                                 return HORIZONTAL_DOWN; | ||||
|                             } else { | ||||
|                                 return this.solution ? SOLUTION_DOWN_LEFT : DOWN_LEFT; | ||||
|                             } | ||||
|                         } else { | ||||
|                             if (this.right) { | ||||
|                                 return this.solution ? SOLUTION_DOWN_RIGHT : DOWN_RIGHT; | ||||
|                             } else { | ||||
|                                 return DOWN; | ||||
|                             } | ||||
|                         } | ||||
|                     } else { | ||||
|                         if (this.left) { | ||||
|                             if (this.right) { | ||||
|                                 return HORIZONTAL; | ||||
|                             } else { | ||||
|                                 return LEFT; | ||||
|                             } | ||||
|                         } else { | ||||
|                             if (this.right) { | ||||
|                                 return RIGHT; | ||||
|                             } else { | ||||
|                                 return " "; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1,5 +1,6 @@ | |||
| package ch.fritteli.labyrinth; | ||||
| package ch.fritteli.labyrinth.renderer; | ||||
| 
 | ||||
| import ch.fritteli.labyrinth.Labyrinth; | ||||
| import lombok.NonNull; | ||||
| 
 | ||||
| public interface Renderer<T> { | ||||
|  | @ -0,0 +1,53 @@ | |||
| package ch.fritteli.labyrinth.renderer.html; | ||||
| 
 | ||||
| import ch.fritteli.labyrinth.Direction; | ||||
| import ch.fritteli.labyrinth.Labyrinth; | ||||
| import ch.fritteli.labyrinth.Tile; | ||||
| import io.vavr.collection.HashSet; | ||||
| import io.vavr.collection.Set; | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NonNull; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| 
 | ||||
| @RequiredArgsConstructor(access = AccessLevel.PACKAGE) | ||||
| class Generator { | ||||
|     private final Labyrinth labyrinth; | ||||
|     private int y = 0; | ||||
| 
 | ||||
|     boolean hasNext() { | ||||
|         return this.y < this.labyrinth.getHeight(); | ||||
|     } | ||||
| 
 | ||||
|     String next() { | ||||
|         StringBuilder sb = new StringBuilder("<tr>"); | ||||
|         for (int x = 0; x < this.labyrinth.getWidth(); x++) { | ||||
|             final Tile currentTile = this.labyrinth.getTileAt(x, this.y); | ||||
|             sb.append("<td class=\""); | ||||
|             sb.append(this.getClasses(currentTile).mkString(" ")); | ||||
|             sb.append("\"> </td>"); | ||||
|         } | ||||
|         sb.append("</tr>"); | ||||
|         this.y++; | ||||
|         return sb.toString(); | ||||
|     } | ||||
| 
 | ||||
|     private Set<String> getClasses(@NonNull final Tile tile) { | ||||
|         Set<String> result = HashSet.empty(); | ||||
|         if (tile.hasWallAt(Direction.TOP)) { | ||||
|             result = result.add("top"); | ||||
|         } | ||||
|         if (tile.hasWallAt(Direction.RIGHT)) { | ||||
|             result = result.add("right"); | ||||
|         } | ||||
|         if (tile.hasWallAt(Direction.BOTTOM)) { | ||||
|             result = result.add("bottom"); | ||||
|         } | ||||
|         if (tile.hasWallAt(Direction.LEFT)) { | ||||
|             result = result.add("left"); | ||||
|         } | ||||
|         if (tile.isSolution()) { | ||||
|             result = result.add("solution"); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| } | ||||
|  | @ -1,9 +1,8 @@ | |||
| package ch.fritteli.labyrinth; | ||||
| package ch.fritteli.labyrinth.renderer.html; | ||||
| 
 | ||||
| import io.vavr.collection.HashSet; | ||||
| import io.vavr.collection.Set; | ||||
| import ch.fritteli.labyrinth.Labyrinth; | ||||
| import ch.fritteli.labyrinth.renderer.Renderer; | ||||
| import lombok.NonNull; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| 
 | ||||
| public class HTMLRenderer implements Renderer<String> { | ||||
|     private static final String POSTAMBLE = "</body></html>"; | ||||
|  | @ -62,46 +61,4 @@ public class HTMLRenderer implements Renderer<String> { | |||
|                 "<input type=\"checkbox\" onclick=\"toggleSolution()\">show solution</input>"; | ||||
|     } | ||||
| 
 | ||||
|     @RequiredArgsConstructor | ||||
|     private static class Generator { | ||||
|         private final Labyrinth labyrinth; | ||||
|         private int y = 0; | ||||
| 
 | ||||
|         private boolean hasNext() { | ||||
|             return this.y < this.labyrinth.getHeight(); | ||||
|         } | ||||
| 
 | ||||
|         private String next() { | ||||
|             StringBuilder sb = new StringBuilder("<tr>"); | ||||
|             for (int x = 0; x < this.labyrinth.getWidth(); x++) { | ||||
|                 final Tile currentTile = this.labyrinth.getTileAt(x, this.y); | ||||
|                 sb.append("<td class=\""); | ||||
|                 sb.append(this.getClasses(currentTile).mkString(" ")); | ||||
|                 sb.append("\"> </td>"); | ||||
|             } | ||||
|             sb.append("</tr>"); | ||||
|             this.y++; | ||||
|             return sb.toString(); | ||||
|         } | ||||
| 
 | ||||
|         private Set<String> getClasses(@NonNull final Tile tile) { | ||||
|             Set<String> result = HashSet.empty(); | ||||
|             if (tile.hasWallAt(Direction.TOP)) { | ||||
|                 result = result.add("top"); | ||||
|             } | ||||
|             if (tile.hasWallAt(Direction.RIGHT)) { | ||||
|                 result = result.add("right"); | ||||
|             } | ||||
|             if (tile.hasWallAt(Direction.BOTTOM)) { | ||||
|                 result = result.add("bottom"); | ||||
|             } | ||||
|             if (tile.hasWallAt(Direction.LEFT)) { | ||||
|                 result = result.add("left"); | ||||
|             } | ||||
|             if (tile.isSolution()) { | ||||
|                 result = result.add("solution"); | ||||
|             } | ||||
|             return result; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1,5 +1,8 @@ | |||
| package ch.fritteli.labyrinth; | ||||
| package ch.fritteli.labyrinth.renderer.htmlfile; | ||||
| 
 | ||||
| import ch.fritteli.labyrinth.Labyrinth; | ||||
| import ch.fritteli.labyrinth.renderer.Renderer; | ||||
| import ch.fritteli.labyrinth.renderer.html.HTMLRenderer; | ||||
| import lombok.NonNull; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
							
								
								
									
										284
									
								
								src/main/java/ch/fritteli/labyrinth/renderer/pdf/Generator.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										284
									
								
								src/main/java/ch/fritteli/labyrinth/renderer/pdf/Generator.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,284 @@ | |||
| package ch.fritteli.labyrinth.renderer.pdf; | ||||
| 
 | ||||
| import ch.fritteli.labyrinth.Direction; | ||||
| import ch.fritteli.labyrinth.Labyrinth; | ||||
| import ch.fritteli.labyrinth.Position; | ||||
| import ch.fritteli.labyrinth.Tile; | ||||
| import lombok.NonNull; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.Value; | ||||
| import org.apache.pdfbox.pdmodel.PDDocument; | ||||
| import org.apache.pdfbox.pdmodel.PDDocumentInformation; | ||||
| import org.apache.pdfbox.pdmodel.PDPage; | ||||
| import org.apache.pdfbox.pdmodel.PDPageContentStream; | ||||
| import org.apache.pdfbox.pdmodel.common.PDRectangle; | ||||
| import org.jetbrains.annotations.Nullable; | ||||
| 
 | ||||
| import java.awt.*; | ||||
| import java.io.ByteArrayOutputStream; | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| @RequiredArgsConstructor | ||||
| class Generator { | ||||
|     @NonNull | ||||
|     private final Labyrinth labyrinth; | ||||
| 
 | ||||
|     private static boolean isValid(@NonNull final Position position) { | ||||
|         return position.getX() >= 0 && position.getY() >= 0; | ||||
|     } | ||||
| 
 | ||||
|     public byte[] generate() { | ||||
|         final float pageWidth = this.labyrinth.getWidth() * PDFRenderer.SCALE + 2 * PDFRenderer.MARGIN; | ||||
|         final float pageHeight = this.labyrinth.getHeight() * PDFRenderer.SCALE + 2 * PDFRenderer.MARGIN; | ||||
| 
 | ||||
|         final PDDocument pdDocument = new PDDocument(); | ||||
|         final PDDocumentInformation info = new PDDocumentInformation(); | ||||
|         info.setTitle("Labyrinth " + this.labyrinth.getWidth() + "x" + this.labyrinth.getHeight() + ", ID " + this.labyrinth.getRandomSeed()); | ||||
|         pdDocument.setDocumentInformation(info); | ||||
|         final PDPage page = new PDPage(new PDRectangle(pageWidth, pageHeight)); | ||||
|         final PDPage solution = new PDPage(new PDRectangle(pageWidth, pageHeight)); | ||||
|         pdDocument.addPage(page); | ||||
|         pdDocument.addPage(solution); | ||||
|         try (final PDPageContentStream labyrinthPageContentStream = new PDPageContentStream(pdDocument, page); | ||||
|              final PDPageContentStream solutionPageContentStream = new PDPageContentStream(pdDocument, solution)) { | ||||
|             setUpPageContentStream(labyrinthPageContentStream); | ||||
|             setUpPageContentStream(solutionPageContentStream); | ||||
|             this.drawHorizonzalLines(labyrinthPageContentStream, solutionPageContentStream); | ||||
|             this.drawVerticalLines(labyrinthPageContentStream, solutionPageContentStream); | ||||
|             this.drawSolution(solutionPageContentStream); | ||||
|         } catch (IOException e) { | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|         final ByteArrayOutputStream output = new ByteArrayOutputStream(); | ||||
|         try { | ||||
|             pdDocument.save(output); | ||||
|             pdDocument.close(); | ||||
|         } catch (IOException e) { | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|         return output.toByteArray(); | ||||
|     } | ||||
| 
 | ||||
|     private void setUpPageContentStream(@NonNull final PDPageContentStream pageContentStream) throws IOException { | ||||
|         pageContentStream.setLineCapStyle(BasicStroke.CAP_ROUND); | ||||
|         pageContentStream.setLineJoinStyle(BasicStroke.JOIN_ROUND); | ||||
|         pageContentStream.setLineWidth(1.0f); | ||||
|         pageContentStream.setStrokingColor(Color.BLACK); | ||||
|         pageContentStream.setNonStrokingColor(Color.BLACK); | ||||
|     } | ||||
| 
 | ||||
|     private void drawHorizonzalLines(@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. | ||||
|         Coordinate coordinate = new Coordinate(0f, 0f); | ||||
|         for (int y = 0; y < this.labyrinth.getHeight(); y++) { | ||||
|             boolean isPainting = false; | ||||
|             coordinate = coordinate.withY(y); | ||||
|             for (int x = 0; x < this.labyrinth.getWidth(); x++) { | ||||
|                 final Tile currentTile = this.labyrinth.getTileAt(x, y); | ||||
|                 coordinate = coordinate.withX(x); | ||||
|                 if (currentTile.hasWallAt(Direction.TOP)) { | ||||
|                     if (!isPainting) { | ||||
|                         for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                             contentStream.moveTo(coordinate.getX(), coordinate.getY()); | ||||
|                         } | ||||
|                         isPainting = true; | ||||
|                     } | ||||
|                 } else { | ||||
|                     if (isPainting) { | ||||
|                         for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                             contentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|                             contentStream.stroke(); | ||||
|                         } | ||||
|                         isPainting = false; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if (isPainting) { | ||||
|                 coordinate = coordinate.withX(this.labyrinth.getWidth()); | ||||
|                 for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                     contentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|                     contentStream.stroke(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         boolean isPainting = false; | ||||
|         int y = this.labyrinth.getHeight(); | ||||
|         coordinate = coordinate.withY(this.labyrinth.getHeight()); | ||||
|         for (int x = 0; x < this.labyrinth.getWidth(); x++) { | ||||
|             final Tile currentTile = this.labyrinth.getTileAt(x, y - 1); | ||||
|             coordinate = coordinate.withX(x); | ||||
|             if (currentTile.hasWallAt(Direction.BOTTOM)) { | ||||
|                 if (!isPainting) { | ||||
|                     for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                         contentStream.moveTo(coordinate.getX(), coordinate.getY()); | ||||
|                     } | ||||
|                     isPainting = true; | ||||
|                 } | ||||
|             } else { | ||||
|                 if (isPainting) { | ||||
|                     for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                         contentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|                         contentStream.stroke(); | ||||
|                     } | ||||
|                     isPainting = false; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (isPainting) { | ||||
|             coordinate = coordinate.withX(this.labyrinth.getWidth()); | ||||
|             for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                 contentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|                 contentStream.stroke(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     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. | ||||
|         Coordinate coordinate = new Coordinate(0f, 0f); | ||||
|         for (int x = 0; x < this.labyrinth.getWidth(); x++) { | ||||
|             boolean isPainting = false; | ||||
|             coordinate = coordinate.withX(x); | ||||
|             for (int y = 0; y < this.labyrinth.getHeight(); y++) { | ||||
|                 final Tile currentTile = this.labyrinth.getTileAt(x, y); | ||||
|                 coordinate = coordinate.withY(y); | ||||
|                 if (currentTile.hasWallAt(Direction.LEFT)) { | ||||
|                     if (!isPainting) { | ||||
|                         for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                             contentStream.moveTo(coordinate.getX(), coordinate.getY()); | ||||
|                         } | ||||
|                         isPainting = true; | ||||
|                     } | ||||
|                 } else { | ||||
|                     if (isPainting) { | ||||
|                         for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                             contentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|                             contentStream.stroke(); | ||||
|                         } | ||||
|                         isPainting = false; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if (isPainting) { | ||||
|                 coordinate = coordinate.withY(this.labyrinth.getHeight()); | ||||
|                 for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                     contentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|                     contentStream.stroke(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         boolean isPainting = false; | ||||
|         int x = this.labyrinth.getWidth(); | ||||
|         coordinate = coordinate.withX(this.labyrinth.getWidth()); | ||||
|         for (int y = 0; y < this.labyrinth.getHeight(); y++) { | ||||
|             final Tile currentTile = this.labyrinth.getTileAt(x - 1, y); | ||||
|             coordinate = coordinate.withY(y); | ||||
|             if (currentTile.hasWallAt(Direction.RIGHT)) { | ||||
|                 if (!isPainting) { | ||||
|                     for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                         contentStream.moveTo(coordinate.getX(), coordinate.getY()); | ||||
|                     } | ||||
|                     isPainting = true; | ||||
|                 } | ||||
|             } else { | ||||
|                 if (isPainting) { | ||||
|                     for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                         contentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|                         contentStream.stroke(); | ||||
|                     } | ||||
|                     isPainting = false; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (isPainting) { | ||||
|             coordinate = coordinate.withY(this.labyrinth.getHeight()); | ||||
|             for (final PDPageContentStream contentStream : contentStreams) { | ||||
|                 contentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|                 contentStream.stroke(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     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. | ||||
|         final Position end = this.labyrinth.getEnd(); | ||||
|         Position currentPosition = this.labyrinth.getStart(); | ||||
|         Position previousPosition = null; | ||||
|         SolutionCoordinate coordinate = new SolutionCoordinate(currentPosition.getX(), currentPosition.getY()); | ||||
|         pageContentStream.moveTo(coordinate.getX(), coordinate.getY()); | ||||
|         do { | ||||
|             Position newCurrent = this.findNextSolutionPosition(previousPosition, currentPosition); | ||||
|             previousPosition = currentPosition; | ||||
|             currentPosition = newCurrent; | ||||
|             coordinate = new SolutionCoordinate(currentPosition.getX(), currentPosition.getY()); | ||||
|             pageContentStream.lineTo(coordinate.getX(), coordinate.getY()); | ||||
|         } while (!currentPosition.equals(end)); | ||||
|         pageContentStream.stroke(); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private Position findNextSolutionPosition(@Nullable final Position previousPosition, @NonNull final Position currentPosition) { | ||||
|         final Tile currentTile = this.labyrinth.getTileAt(currentPosition); | ||||
|         for (final Direction direction : Direction.values()) { | ||||
|             if (!currentTile.hasWallAt(direction)) { | ||||
|                 final Position position = direction.translate(currentPosition); | ||||
|                 final Tile tileAtPosition = this.labyrinth.getTileAtOrNull(position); | ||||
|                 if (position.equals(previousPosition) || tileAtPosition == null) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (tileAtPosition.isSolution()) { | ||||
|                     return position; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         throw new IllegalStateException("We *SHOULD* never have gotten here. ... famous last words ..."); | ||||
|     } | ||||
| 
 | ||||
|     @Value | ||||
|     private class Coordinate { | ||||
|         float x; | ||||
|         float y; | ||||
| 
 | ||||
|         public Coordinate(final float x, final float y) { | ||||
|             this.x = x; | ||||
|             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); | ||||
|         } | ||||
| 
 | ||||
|         public Coordinate withY(final int y) { | ||||
|             return new Coordinate(this.x, calcY(y)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Value | ||||
|     private class SolutionCoordinate { | ||||
|         float x; | ||||
|         float y; | ||||
| 
 | ||||
|         public SolutionCoordinate(final int x, final int y) { | ||||
|             this.x = calcX(x); | ||||
|             this.y = calcY(y); | ||||
|         } | ||||
| 
 | ||||
|         private float calcX(final int x) { | ||||
|             return x * PDFRenderer.SCALE + PDFRenderer.SCALE / 2 + PDFRenderer.MARGIN; | ||||
|         } | ||||
| 
 | ||||
|         private float calcY(final int y) { | ||||
|             return (Generator.this.labyrinth.getHeight() - y) * PDFRenderer.SCALE - PDFRenderer.SCALE / 2 + PDFRenderer.MARGIN; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,28 @@ | |||
| package ch.fritteli.labyrinth.renderer.pdf; | ||||
| 
 | ||||
| import ch.fritteli.labyrinth.Labyrinth; | ||||
| import ch.fritteli.labyrinth.renderer.Renderer; | ||||
| import lombok.NonNull; | ||||
| 
 | ||||
| public class PDFRenderer implements Renderer<byte[]> { | ||||
|     static final float MARGIN = 10; | ||||
|     static final float SCALE = 10; | ||||
| 
 | ||||
|     private PDFRenderer() { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public static PDFRenderer newInstance() { | ||||
|         return new PDFRenderer(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     @NonNull | ||||
|     public byte[] render(@NonNull final Labyrinth labyrinth) { | ||||
|         final Generator generator = new Generator(labyrinth); | ||||
|         return generator.generate(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|  | @ -1,5 +1,8 @@ | |||
| package ch.fritteli.labyrinth; | ||||
| package ch.fritteli.labyrinth.renderer.pdffile; | ||||
| 
 | ||||
| import ch.fritteli.labyrinth.Labyrinth; | ||||
| import ch.fritteli.labyrinth.renderer.pdf.PDFRenderer; | ||||
| import ch.fritteli.labyrinth.renderer.Renderer; | ||||
| import lombok.NonNull; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
|  | @ -0,0 +1,152 @@ | |||
| package ch.fritteli.labyrinth.renderer.text; | ||||
| 
 | ||||
| import lombok.AccessLevel; | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.NoArgsConstructor; | ||||
| import lombok.experimental.FieldDefaults; | ||||
| 
 | ||||
| @FieldDefaults(level = AccessLevel.PRIVATE) | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| class CharDefinition { | ||||
|     // ─ | ||||
|     static final String HORIZONTAL = "\u2500"; | ||||
|     // │ | ||||
|     static final String VERTICAL = "\u2502"; | ||||
|     // ╴ | ||||
|     static final String LEFT = "\u2574"; | ||||
|     // ╵ | ||||
|     static final String UP = "\u2575"; | ||||
|     // ╶ | ||||
|     static final String RIGHT = "\u2576"; | ||||
|     // ╷ | ||||
|     static final String DOWN = "\u2577"; | ||||
|     // ┌ | ||||
|     static final String DOWN_RIGHT = "\u250c"; | ||||
|     // ┐ | ||||
|     static final String DOWN_LEFT = "\u2510"; | ||||
|     // └ | ||||
|     static final String UP_RIGHT = "\u2514"; | ||||
|     // ┘ | ||||
|     static final String UP_LEFT = "\u2518"; | ||||
|     // ├ | ||||
|     static final String VERTICAL_RIGHT = "\u251c"; | ||||
|     // ┤ | ||||
|     static final String VERTICAL_LEFT = "\u2524"; | ||||
|     // ┬ | ||||
|     static final String HORIZONTAL_DOWN = "\u252c"; | ||||
|     // ┴ | ||||
|     static final String HORIZONTAL_UP = "\u2534"; | ||||
|     // ┼ | ||||
|     static final String CROSS = "\u253c"; | ||||
|     // ╭ | ||||
|     static final String SOLUTION_DOWN_RIGHT = "\u256d"; | ||||
|     // ╮ | ||||
|     static final String SOLUTION_DOWN_LEFT = "\u256e"; | ||||
|     // ╯ | ||||
|     static final String SOLUTION_UP_LEFT = "\u256f"; | ||||
|     // ╰ | ||||
|     static final String SOLUTION_UP_RIGHT = "\u2570"; | ||||
|     boolean up = false; | ||||
|     boolean down = false; | ||||
|     boolean left = false; | ||||
|     boolean right = false; | ||||
|     boolean solution = false; | ||||
| 
 | ||||
|     CharDefinition solution() { | ||||
|         this.solution = true; | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     CharDefinition up() { | ||||
|         this.up = true; | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     CharDefinition down() { | ||||
|         this.down = true; | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     CharDefinition vertical() { | ||||
|         return this.up().down(); | ||||
|     } | ||||
| 
 | ||||
|     CharDefinition left() { | ||||
|         this.left = true; | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     CharDefinition right() { | ||||
|         this.right = true; | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     CharDefinition horizontal() { | ||||
|         return this.left().right(); | ||||
|     } | ||||
| 
 | ||||
|     public String toString() { | ||||
|         if (this.up) { | ||||
|             if (this.down) { | ||||
|                 if (this.left) { | ||||
|                     if (this.right) { | ||||
|                         return CROSS; | ||||
|                     } else { | ||||
|                         return VERTICAL_LEFT; | ||||
|                     } | ||||
|                 } else { | ||||
|                     if (this.right) { | ||||
|                         return VERTICAL_RIGHT; | ||||
|                     } else { | ||||
|                         return VERTICAL; | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 if (this.left) { | ||||
|                     if (this.right) { | ||||
|                         return HORIZONTAL_UP; | ||||
|                     } else { | ||||
|                         return this.solution ? SOLUTION_UP_LEFT : UP_LEFT; | ||||
|                     } | ||||
|                 } else { | ||||
|                     if (this.right) { | ||||
|                         return this.solution ? SOLUTION_UP_RIGHT : UP_RIGHT; | ||||
|                     } else { | ||||
|                         return UP; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             if (this.down) { | ||||
|                 if (this.left) { | ||||
|                     if (this.right) { | ||||
|                         return HORIZONTAL_DOWN; | ||||
|                     } else { | ||||
|                         return this.solution ? SOLUTION_DOWN_LEFT : DOWN_LEFT; | ||||
|                     } | ||||
|                 } else { | ||||
|                     if (this.right) { | ||||
|                         return this.solution ? SOLUTION_DOWN_RIGHT : DOWN_RIGHT; | ||||
|                     } else { | ||||
|                         return DOWN; | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 if (this.left) { | ||||
|                     if (this.right) { | ||||
|                         return HORIZONTAL; | ||||
|                     } else { | ||||
|                         return LEFT; | ||||
|                     } | ||||
|                 } else { | ||||
|                     if (this.right) { | ||||
|                         return RIGHT; | ||||
|                     } else { | ||||
|                         return " "; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										196
									
								
								src/main/java/ch/fritteli/labyrinth/renderer/text/Generator.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								src/main/java/ch/fritteli/labyrinth/renderer/text/Generator.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,196 @@ | |||
| package ch.fritteli.labyrinth.renderer.text; | ||||
| 
 | ||||
| import ch.fritteli.labyrinth.Direction; | ||||
| import ch.fritteli.labyrinth.Labyrinth; | ||||
| import ch.fritteli.labyrinth.Tile; | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NonNull; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import org.jetbrains.annotations.Nullable; | ||||
| 
 | ||||
| @RequiredArgsConstructor(access = AccessLevel.PACKAGE) | ||||
| class Generator { | ||||
|     @NonNull | ||||
|     private final Labyrinth labyrinth; | ||||
|     private final boolean renderSolution; | ||||
|     private int x = 0; | ||||
|     private int y = 0; | ||||
|     private int line = 0; | ||||
| 
 | ||||
|     boolean hasNext() { | ||||
|         return this.y < this.labyrinth.getHeight(); | ||||
|     } | ||||
| 
 | ||||
|     String next() { | ||||
|         final Tile currentTile = this.labyrinth.getTileAt(this.x, this.y); | ||||
|         final Tile topTile = this.labyrinth.getTileAtOrNull(this.x, this.y - 1); | ||||
|         final Tile rightTile = this.labyrinth.getTileAtOrNull(this.x + 1, this.y); | ||||
|         final Tile bottomTile = this.labyrinth.getTileAtOrNull(this.x, this.y + 1); | ||||
|         final Tile leftTile = this.labyrinth.getTileAtOrNull(this.x - 1, this.y); | ||||
|         final String s; | ||||
|         switch (this.line) { | ||||
|             case 0: | ||||
|                 s = this.renderTopLine(currentTile, leftTile, topTile); | ||||
|                 break; | ||||
|             case 1: | ||||
|                 s = this.renderCenterLine(currentTile, topTile, rightTile, bottomTile, leftTile); | ||||
|                 break; | ||||
|             case 2: | ||||
|                 s = this.renderBottomLine(currentTile, leftTile); | ||||
|                 break; | ||||
|             default: | ||||
|                 s = ""; | ||||
|                 break; | ||||
|         } | ||||
|         this.prepareNextStep(); | ||||
|         return s; | ||||
|     } | ||||
| 
 | ||||
|     private void prepareNextStep() { | ||||
|         // do some magic ... | ||||
|         this.x++; | ||||
|         if (this.x == this.labyrinth.getWidth()) { | ||||
|             // Reached the end of the row? | ||||
|             // On to the next line then! | ||||
|             this.x = 0; | ||||
|             this.line++; | ||||
|         } | ||||
|         if (this.line == 2 && this.y < this.labyrinth.getHeight() - 1) { | ||||
|             // Finished rendering the center line, and more rows available? | ||||
|             // On to the next row then! | ||||
|             this.line = 0; | ||||
|             this.y++; | ||||
|         } | ||||
|         if (this.line == 3) { | ||||
|             // Finished rendering the bottom line (of the last row)? | ||||
|             // Increment row counter one more time => this is the exit condition. | ||||
|             this.line = 0; | ||||
|             this.y++; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private String renderTopLine(@NonNull final Tile currentTile, @Nullable final Tile leftTile, @Nullable final Tile topTile) { | ||||
|         final CharDefinition charDef1 = new CharDefinition(); | ||||
|         final CharDefinition charDef2 = new CharDefinition(); | ||||
|         final CharDefinition charDef3 = new CharDefinition(); | ||||
|         String result; | ||||
|         if (currentTile.hasWallAt(Direction.LEFT)) { | ||||
|             charDef1.down(); | ||||
|         } | ||||
|         if (currentTile.hasWallAt(Direction.TOP)) { | ||||
|             charDef1.right(); | ||||
|             charDef2.horizontal(); | ||||
|             charDef3.left(); | ||||
|         } else { | ||||
|             if (this.isSolution(currentTile) && (this.isSolution(topTile) || topTile == null)) { | ||||
|                 charDef2.solution().vertical(); | ||||
|             } | ||||
|         } | ||||
|         if (currentTile.hasWallAt(Direction.RIGHT)) { | ||||
|             charDef3.down(); | ||||
|         } | ||||
|         if (this.hasWallAt(leftTile, Direction.TOP)) { | ||||
|             charDef1.left(); | ||||
|         } | ||||
|         if (this.hasWallAt(topTile, Direction.LEFT)) { | ||||
|             charDef1.up(); | ||||
|         } | ||||
|         if (this.hasWallAt(topTile, Direction.RIGHT)) { | ||||
|             charDef3.up(); | ||||
|         } | ||||
|         result = charDef1.toString() + charDef2; | ||||
|         if (this.x == this.labyrinth.getWidth() - 1) { | ||||
|             result += charDef3 + "\n"; | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     private String renderCenterLine(@NonNull final Tile currentTile, | ||||
|                                     @Nullable final Tile topTile, | ||||
|                                     @Nullable final Tile rightTile, | ||||
|                                     @Nullable final Tile bottomTile, | ||||
|                                     @Nullable final Tile leftTile) { | ||||
|         final CharDefinition charDef1 = new CharDefinition(); | ||||
|         final CharDefinition charDef2 = new CharDefinition(); | ||||
|         final CharDefinition charDef3 = new CharDefinition(); | ||||
|         String result; | ||||
|         if (currentTile.hasWallAt(Direction.LEFT)) { | ||||
|             charDef1.vertical(); | ||||
|         } else { | ||||
|             if (this.isSolution(currentTile) && this.isSolution(leftTile)) { | ||||
|                 charDef1.solution().horizontal(); | ||||
|             } | ||||
|         } | ||||
|         if (this.isSolution(currentTile)) { | ||||
|             charDef2.solution(); | ||||
|             if (!currentTile.hasWallAt(Direction.LEFT) && (this.isSolution(leftTile) || leftTile == null)) { | ||||
|                 charDef2.left(); | ||||
|             } | ||||
|             if (!currentTile.hasWallAt(Direction.TOP) && (this.isSolution(topTile) || topTile == null)) { | ||||
|                 charDef2.up(); | ||||
|             } | ||||
|             if (!currentTile.hasWallAt(Direction.RIGHT) && (this.isSolution(rightTile) || rightTile == null)) { | ||||
|                 charDef2.right(); | ||||
|             } | ||||
|             if (!currentTile.hasWallAt(Direction.BOTTOM) && (this.isSolution(bottomTile) || bottomTile == null)) { | ||||
|                 charDef2.down(); | ||||
|             } | ||||
|         } | ||||
|         if (currentTile.hasWallAt(Direction.RIGHT)) { | ||||
|             charDef3.vertical(); | ||||
|         } else { | ||||
|             if (this.isSolution(currentTile) && this.isSolution(rightTile)) { | ||||
|                 charDef3.solution().horizontal(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         result = charDef1.toString() + charDef2; | ||||
| 
 | ||||
|         if (this.x == this.labyrinth.getWidth() - 1) { | ||||
|             result += charDef3 + "\n"; | ||||
|         } | ||||
| 
 | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     private String renderBottomLine(@NonNull final Tile currentTile, @Nullable final Tile leftTile) { | ||||
|         String result; | ||||
|         final CharDefinition charDef1 = new CharDefinition(); | ||||
|         final CharDefinition charDef2 = new CharDefinition(); | ||||
|         final CharDefinition charDef3 = new CharDefinition(); | ||||
| 
 | ||||
|         if (currentTile.hasWallAt(Direction.LEFT)) { | ||||
|             charDef1.up(); | ||||
|         } | ||||
|         if (currentTile.hasWallAt(Direction.BOTTOM)) { | ||||
|             charDef1.right(); | ||||
|             charDef2.horizontal(); | ||||
|             charDef3.left(); | ||||
|         } else { | ||||
|             if (this.isSolution(currentTile)) { | ||||
|                 charDef2.solution().vertical(); | ||||
|             } | ||||
|         } | ||||
|         if (currentTile.hasWallAt(Direction.RIGHT)) { | ||||
|             charDef3.up(); | ||||
|         } | ||||
|         if (this.hasWallAt(leftTile, Direction.BOTTOM)) { | ||||
|             charDef1.left(); | ||||
|         } | ||||
| 
 | ||||
|         result = charDef1.toString() + charDef2; | ||||
| 
 | ||||
|         if (this.x == this.labyrinth.getWidth() - 1) { | ||||
|             result += charDef3; | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     private boolean hasWallAt(@Nullable final Tile tile, @NonNull final Direction direction) { | ||||
|         return tile != null && tile.hasWallAt(direction); | ||||
|     } | ||||
| 
 | ||||
|     private boolean isSolution(@Nullable final Tile tile) { | ||||
|         return this.renderSolution && tile != null && tile.isSolution(); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,38 @@ | |||
| package ch.fritteli.labyrinth.renderer.text; | ||||
| 
 | ||||
| import ch.fritteli.labyrinth.Labyrinth; | ||||
| import ch.fritteli.labyrinth.renderer.Renderer; | ||||
| import lombok.NonNull; | ||||
| 
 | ||||
| public class TextRenderer implements Renderer<String> { | ||||
|     private boolean renderSolution; | ||||
| 
 | ||||
|     private TextRenderer() { | ||||
|         this.renderSolution = false; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public static TextRenderer newInstance() { | ||||
|         return new TextRenderer(); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public TextRenderer setRenderSolution(final boolean renderSolution) { | ||||
|         this.renderSolution = renderSolution; | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public String render(@NonNull final Labyrinth labyrinth) { | ||||
|         if (labyrinth.getWidth() == 0 || labyrinth.getHeight() == 0) { | ||||
|             return ""; | ||||
|         } | ||||
|         final Generator generator = new Generator(labyrinth, this.renderSolution); | ||||
|         final StringBuilder sb = new StringBuilder(); | ||||
|         while (generator.hasNext()) { | ||||
|             sb.append(generator.next()); | ||||
|         } | ||||
|         return sb.toString(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -1,5 +1,8 @@ | |||
| package ch.fritteli.labyrinth; | ||||
| package ch.fritteli.labyrinth.renderer.textfile; | ||||
| 
 | ||||
| import ch.fritteli.labyrinth.Labyrinth; | ||||
| import ch.fritteli.labyrinth.renderer.Renderer; | ||||
| import ch.fritteli.labyrinth.renderer.text.TextRenderer; | ||||
| import io.vavr.collection.List; | ||||
| import lombok.NonNull; | ||||
| 
 | ||||
|  | @ -1,52 +0,0 @@ | |||
| package ch.fritteli.labyrinth; | ||||
| 
 | ||||
| import ch.fritteli.labyrinth.TextRenderer.Generator.CharDefinition; | ||||
| import org.junit.jupiter.api.Nested; | ||||
| import org.junit.jupiter.api.Test; | ||||
| 
 | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| 
 | ||||
| class TextRendererTest { | ||||
|     @Nested | ||||
|     class CharDefinitionTest { | ||||
|         @Test | ||||
|         void testRenderingWall() { | ||||
|             assertEquals(" ", new CharDefinition(false, false, false, false, false).toString()); | ||||
|             assertEquals("╶", new CharDefinition(false, false, false, true, false).toString()); | ||||
|             assertEquals("╴", new CharDefinition(false, false, true, false, false).toString()); | ||||
|             assertEquals("─", new CharDefinition(false, false, true, true, false).toString()); | ||||
|             assertEquals("╷", new CharDefinition(false, true, false, false, false).toString()); | ||||
|             assertEquals("┌", new CharDefinition(false, true, false, true, false).toString()); | ||||
|             assertEquals("┐", new CharDefinition(false, true, true, false, false).toString()); | ||||
|             assertEquals("┬", new CharDefinition(false, true, true, true, false).toString()); | ||||
|             assertEquals("╵", new CharDefinition(true, false, false, false, false).toString()); | ||||
|             assertEquals("└", new CharDefinition(true, false, false, true, false).toString()); | ||||
|             assertEquals("┘", new CharDefinition(true, false, true, false, false).toString()); | ||||
|             assertEquals("┴", new CharDefinition(true, false, true, true, false).toString()); | ||||
|             assertEquals("│", new CharDefinition(true, true, false, false, false).toString()); | ||||
|             assertEquals("├", new CharDefinition(true, true, false, true, false).toString()); | ||||
|             assertEquals("┤", new CharDefinition(true, true, true, false, false).toString()); | ||||
|             assertEquals("┼", new CharDefinition(true, true, true, true, false).toString()); | ||||
|         } | ||||
| 
 | ||||
|         @Test | ||||
|         void testRenderingSolution() { | ||||
|             assertEquals(" ", new CharDefinition(false, false, false, false, true).toString()); | ||||
|             assertEquals("╶", new CharDefinition(false, false, false, true, true).toString()); | ||||
|             assertEquals("╴", new CharDefinition(false, false, true, false, true).toString()); | ||||
|             assertEquals("─", new CharDefinition(false, false, true, true, true).toString()); | ||||
|             assertEquals("╷", new CharDefinition(false, true, false, false, true).toString()); | ||||
|             assertEquals("╭", new CharDefinition(false, true, false, true, true).toString()); | ||||
|             assertEquals("╮", new CharDefinition(false, true, true, false, true).toString()); | ||||
|             assertEquals("┬", new CharDefinition(false, true, true, true, true).toString()); | ||||
|             assertEquals("╵", new CharDefinition(true, false, false, false, true).toString()); | ||||
|             assertEquals("╰", new CharDefinition(true, false, false, true, true).toString()); | ||||
|             assertEquals("╯", new CharDefinition(true, false, true, false, true).toString()); | ||||
|             assertEquals("┴", new CharDefinition(true, false, true, true, true).toString()); | ||||
|             assertEquals("│", new CharDefinition(true, true, false, false, true).toString()); | ||||
|             assertEquals("├", new CharDefinition(true, true, false, true, true).toString()); | ||||
|             assertEquals("┤", new CharDefinition(true, true, true, false, true).toString()); | ||||
|             assertEquals("┼", new CharDefinition(true, true, true, true, true).toString()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,47 @@ | |||
| package ch.fritteli.labyrinth.renderer.text; | ||||
| 
 | ||||
| import org.junit.jupiter.api.Test; | ||||
| 
 | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| 
 | ||||
| class CharDefinitionTest { | ||||
|     @Test | ||||
|     void testRenderingWall() { | ||||
|         assertEquals(" ", new CharDefinition(false, false, false, false, false).toString()); | ||||
|         assertEquals("╶", new CharDefinition(false, false, false, true, false).toString()); | ||||
|         assertEquals("╴", new CharDefinition(false, false, true, false, false).toString()); | ||||
|         assertEquals("─", new CharDefinition(false, false, true, true, false).toString()); | ||||
|         assertEquals("╷", new CharDefinition(false, true, false, false, false).toString()); | ||||
|         assertEquals("┌", new CharDefinition(false, true, false, true, false).toString()); | ||||
|         assertEquals("┐", new CharDefinition(false, true, true, false, false).toString()); | ||||
|         assertEquals("┬", new CharDefinition(false, true, true, true, false).toString()); | ||||
|         assertEquals("╵", new CharDefinition(true, false, false, false, false).toString()); | ||||
|         assertEquals("└", new CharDefinition(true, false, false, true, false).toString()); | ||||
|         assertEquals("┘", new CharDefinition(true, false, true, false, false).toString()); | ||||
|         assertEquals("┴", new CharDefinition(true, false, true, true, false).toString()); | ||||
|         assertEquals("│", new CharDefinition(true, true, false, false, false).toString()); | ||||
|         assertEquals("├", new CharDefinition(true, true, false, true, false).toString()); | ||||
|         assertEquals("┤", new CharDefinition(true, true, true, false, false).toString()); | ||||
|         assertEquals("┼", new CharDefinition(true, true, true, true, false).toString()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void testRenderingSolution() { | ||||
|         assertEquals(" ", new CharDefinition(false, false, false, false, true).toString()); | ||||
|         assertEquals("╶", new CharDefinition(false, false, false, true, true).toString()); | ||||
|         assertEquals("╴", new CharDefinition(false, false, true, false, true).toString()); | ||||
|         assertEquals("─", new CharDefinition(false, false, true, true, true).toString()); | ||||
|         assertEquals("╷", new CharDefinition(false, true, false, false, true).toString()); | ||||
|         assertEquals("╭", new CharDefinition(false, true, false, true, true).toString()); | ||||
|         assertEquals("╮", new CharDefinition(false, true, true, false, true).toString()); | ||||
|         assertEquals("┬", new CharDefinition(false, true, true, true, true).toString()); | ||||
|         assertEquals("╵", new CharDefinition(true, false, false, false, true).toString()); | ||||
|         assertEquals("╰", new CharDefinition(true, false, false, true, true).toString()); | ||||
|         assertEquals("╯", new CharDefinition(true, false, true, false, true).toString()); | ||||
|         assertEquals("┴", new CharDefinition(true, false, true, true, true).toString()); | ||||
|         assertEquals("│", new CharDefinition(true, true, false, false, true).toString()); | ||||
|         assertEquals("├", new CharDefinition(true, true, false, true, true).toString()); | ||||
|         assertEquals("┤", new CharDefinition(true, true, true, false, true).toString()); | ||||
|         assertEquals("┼", new CharDefinition(true, true, true, true, true).toString()); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue