diff --git a/src/main/java/ch/fritteli/labyrinth/HTMLFileRenderer.java b/src/main/java/ch/fritteli/labyrinth/HTMLFileRenderer.java index ade1da5..85decec 100644 --- a/src/main/java/ch/fritteli/labyrinth/HTMLFileRenderer.java +++ b/src/main/java/ch/fritteli/labyrinth/HTMLFileRenderer.java @@ -9,6 +9,8 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; public class HTMLFileRenderer implements Renderer { + @NonNull + private static final HTMLRenderer HTML_RENDERER = HTMLRenderer.newInstance(); @Setter private Path targetFile; @@ -35,7 +37,7 @@ public class HTMLFileRenderer implements Renderer { if (!this.isTargetFileDefinedAndWritable()) { throw new IllegalArgumentException("Cannot write to target file. See previous log messages for details."); } - final String html = HTMLRenderer.newInstance().render(labyrinth); + final String html = HTML_RENDERER.render(labyrinth); try { Files.writeString(this.targetFile, html, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE); } catch (IOException e) { diff --git a/src/main/java/ch/fritteli/labyrinth/HTMLRenderer.java b/src/main/java/ch/fritteli/labyrinth/HTMLRenderer.java index 9d39291..73983ec 100644 --- a/src/main/java/ch/fritteli/labyrinth/HTMLRenderer.java +++ b/src/main/java/ch/fritteli/labyrinth/HTMLRenderer.java @@ -3,6 +3,7 @@ package ch.fritteli.labyrinth; import io.vavr.collection.HashSet; import io.vavr.collection.Set; import lombok.NonNull; +import lombok.RequiredArgsConstructor; public class HTMLRenderer implements Renderer { private static final String PREAMBLE = "" + @@ -33,11 +34,6 @@ public class HTMLRenderer implements Renderer { "" + "show solution"; private static final String POSTAMBLE = ""; - private Labyrinth labyrinth; - private int width; - private int height; - // row counter - private int y = 0; private HTMLRenderer() { } @@ -49,56 +45,60 @@ public class HTMLRenderer implements Renderer { @NonNull public String render(@NonNull final Labyrinth labyrinth) { - this.labyrinth = labyrinth; - this.width = labyrinth.getWidth(); - this.height = labyrinth.getHeight(); - if (this.width == 0 || this.height == 0) { + if (labyrinth.getWidth() == 0 || labyrinth.getHeight() == 0) { return PREAMBLE + POSTAMBLE; } + final Generator generator = new Generator(labyrinth); final StringBuilder sb = new StringBuilder(PREAMBLE); sb.append(""); - while (this.hasNext()) { - sb.append(this.next()); + while (generator.hasNext()) { + sb.append(generator.next()); } sb.append("
"); sb.append(POSTAMBLE); return sb.toString(); } - private boolean hasNext() { - return this.y < this.height; - } + @RequiredArgsConstructor + private static class Generator { + private final Labyrinth labyrinth; + private int y = 0; - private String next() { - StringBuilder sb = new StringBuilder(""); - for (int x = 0; x < this.width; x++) { - final Tile currentTile = this.labyrinth.getTileAt(x, this.y); - sb.append(" "); + private boolean hasNext() { + return this.y < this.labyrinth.getHeight(); } - sb.append(""); - this.y++; - return sb.toString(); - } - private Set getClasses(@NonNull final Tile tile) { - Set result = HashSet.empty(); - if (tile.hasWallAt(Direction.TOP)) { - result = result.add("top"); + private String next() { + StringBuilder sb = new StringBuilder(""); + for (int x = 0; x < this.labyrinth.getWidth(); x++) { + final Tile currentTile = this.labyrinth.getTileAt(x, this.y); + sb.append(" "); + } + sb.append(""); + this.y++; + return sb.toString(); } - if (tile.hasWallAt(Direction.RIGHT)) { - result = result.add("right"); + + private Set getClasses(@NonNull final Tile tile) { + Set 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; } - 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; } } diff --git a/src/main/java/ch/fritteli/labyrinth/Main.java b/src/main/java/ch/fritteli/labyrinth/Main.java index 9e133d4..c38c796 100644 --- a/src/main/java/ch/fritteli/labyrinth/Main.java +++ b/src/main/java/ch/fritteli/labyrinth/Main.java @@ -9,7 +9,7 @@ public class Main { final Labyrinth labyrinth = new Labyrinth(width, height); final TextRenderer textRenderer = TextRenderer.newInstance(); System.out.println(textRenderer.render(labyrinth)); - System.out.println(textRenderer.setRenderingSolution(true).render(labyrinth)); + System.out.println(textRenderer.setRenderSolution(true).render(labyrinth)); final HTMLRenderer htmlRenderer = HTMLRenderer.newInstance(); System.out.println(htmlRenderer.render(labyrinth)); System.out.println(HTMLFileRenderer.newInstance().render(labyrinth)); diff --git a/src/main/java/ch/fritteli/labyrinth/TextRenderer.java b/src/main/java/ch/fritteli/labyrinth/TextRenderer.java index bc28293..4cf07b9 100644 --- a/src/main/java/ch/fritteli/labyrinth/TextRenderer.java +++ b/src/main/java/ch/fritteli/labyrinth/TextRenderer.java @@ -4,20 +4,12 @@ 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 { - private Labyrinth labyrinth; - private int width; - private int height; private boolean renderSolution; - // column counter - private int x; - // row counter - private int y; - // line counter (top-, center- or bottom line of a row) - private int line; private TextRenderer() { this.renderSolution = false; @@ -29,295 +21,299 @@ public class TextRenderer implements Renderer { } @NonNull - public TextRenderer setRenderingSolution(final boolean renderSolution) { + public TextRenderer setRenderSolution(final boolean renderSolution) { this.renderSolution = renderSolution; return this; } @NonNull public String render(@NonNull final Labyrinth labyrinth) { - this.labyrinth = labyrinth; - this.width = labyrinth.getWidth(); - this.height = labyrinth.getHeight(); - if (this.width == 0 || this.height == 0) { + if (labyrinth.getWidth() == 0 || labyrinth.getHeight() == 0) { return ""; } - this.x = 0; - this.y = 0; - this.line = 0; + final Generator generator = new Generator(labyrinth, this.renderSolution); final StringBuilder sb = new StringBuilder(); - while (this.hasNext()) { - sb.append(this.next()); + while (generator.hasNext()) { + sb.append(generator.next()); } return sb.toString(); } - private boolean hasNext() { - return this.y < this.height; - } + @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 String next() { - final Tile currentTile = this.labyrinth.getTileAt(this.x, this.y); - final Tile leftTile = this.getTileOrNull(this.x - 1, this.y); - final Tile topTile = this.getTileOrNull(this.x, this.y - 1); - final String s; - switch (this.line) { - case 0: - s = this.renderTopLine(currentTile, leftTile, topTile); - break; - case 1: - s = this.renderCenterLine(currentTile); - break; - case 2: - s = this.renderBottomLine(currentTile, leftTile); - break; - default: - s = ""; - break; + private boolean hasNext() { + return this.y < this.labyrinth.getHeight(); } - this.prepareNextStep(); - return s; - } - private void prepareNextStep() { - // do some magic ... - this.x++; - if (this.x == this.width) { - // Reached the end of the row? - // On to the next line then! - this.x = 0; - this.line++; + private String next() { + final Tile currentTile = this.labyrinth.getTileAt(this.x, this.y); + final Tile leftTile = this.getTileOrNull(this.x - 1, this.y); + final Tile topTile = this.getTileOrNull(this.x, this.y - 1); + final String s; + switch (this.line) { + case 0: + s = this.renderTopLine(currentTile, leftTile, topTile); + break; + case 1: + s = this.renderCenterLine(currentTile); + break; + case 2: + s = this.renderBottomLine(currentTile, leftTile); + break; + default: + s = ""; + break; + } + this.prepareNextStep(); + return s; } - if (this.line == 2 && this.y < this.height - 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.width || y >= this.height) { - return null; + 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++; + } } - 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(); + 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); } - if (currentTile.hasWallAt(Direction.TOP)) { - charDef1.right(); - charDef2.horizontal(); - charDef3.left(); + + 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(); + } + if (currentTile.hasWallAt(Direction.RIGHT)) { + charDef3.down(); + } + if (leftTile != null && leftTile.hasWallAt(Direction.TOP)) { + charDef1.left(); + } + if (topTile != null) { + if (topTile.hasWallAt(Direction.LEFT)) { + charDef1.up(); + } + if (topTile.hasWallAt(Direction.RIGHT)) { + charDef3.up(); + } + } + result = charDef1.toString() + charDef2; + if (this.x == this.labyrinth.getWidth() - 1) { + result += charDef3 + "\n"; + } + return result; } - if (currentTile.hasWallAt(Direction.RIGHT)) { - charDef3.down(); + + private String renderCenterLine(@NonNull final Tile currentTile) { + final CharDefinition charDef1 = new CharDefinition(); + final CharDefinition charDef2 = new CharDefinition(); + String result; + if (currentTile.hasWallAt(Direction.LEFT)) { + charDef1.vertical(); + } + if (currentTile.hasWallAt(Direction.RIGHT)) { + charDef2.vertical(); + } + + result = charDef1 + (currentTile.isSolution() && this.renderSolution ? "x" : " "); + + if (this.x == this.labyrinth.getWidth() - 1) { + result += charDef2 + "\n"; + } + + return result; } - if (leftTile != null && leftTile.hasWallAt(Direction.TOP)) { - charDef1.left(); - } - if (topTile != null) { - if (topTile.hasWallAt(Direction.LEFT)) { + + 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 (topTile.hasWallAt(Direction.RIGHT)) { + if (currentTile.hasWallAt(Direction.BOTTOM)) { + charDef1.right(); + charDef2.horizontal(); + charDef3.left(); + } + if (currentTile.hasWallAt(Direction.RIGHT)) { charDef3.up(); } - } - result = charDef1.toString() + charDef2; - if (this.x == this.width - 1) { - result += charDef3 + "\n"; - } - return result; - } + if (leftTile != null && leftTile.hasWallAt(Direction.BOTTOM)) { + charDef1.left(); + } - private String renderCenterLine(@NonNull final Tile currentTile) { - final CharDefinition charDef1 = new CharDefinition(); - final CharDefinition charDef2 = new CharDefinition(); - String result; - if (currentTile.hasWallAt(Direction.LEFT)) { - charDef1.vertical(); - } - if (currentTile.hasWallAt(Direction.RIGHT)) { - charDef2.vertical(); + result = charDef1.toString() + charDef2; + + if (this.x == this.labyrinth.getWidth() - 1) { + result += charDef3; + } + return result; } - result = charDef1 + (currentTile.isSolution() && this.renderSolution ? "x" : " "); + @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"; + boolean up = false; + boolean down = false; + boolean left = false; + boolean right = false; - if (this.x == this.width - 1) { - result += charDef2 + "\n"; - } + CharDefinition up() { + this.up = true; + return this; + } - return result; - } + CharDefinition down() { + this.down = true; + return this; + } - 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(); + CharDefinition vertical() { + return this.up().down(); + } - if (currentTile.hasWallAt(Direction.LEFT)) { - charDef1.up(); - } - if (currentTile.hasWallAt(Direction.BOTTOM)) { - charDef1.right(); - charDef2.horizontal(); - charDef3.left(); - } - if (currentTile.hasWallAt(Direction.RIGHT)) { - charDef3.up(); - } - if (leftTile != null && leftTile.hasWallAt(Direction.BOTTOM)) { - charDef1.left(); - } + CharDefinition left() { + this.left = true; + return this; + } - result = charDef1.toString() + charDef2; + CharDefinition right() { + this.right = true; + return this; + } - if (this.x == this.width - 1) { - result += charDef3; - } - return result; - } + CharDefinition horizontal() { + return this.left().right(); + } - @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"; - boolean up = false; - boolean down = false; - boolean left = false; - boolean right = false; - - 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; + public String toString() { + if (this.up) { + if (this.down) { + if (this.left) { + if (this.right) { + return CROSS; + } else { + return VERTICAL_LEFT; + } } else { - return VERTICAL_LEFT; + if (this.right) { + return VERTICAL_RIGHT; + } else { + return VERTICAL; + } } } else { - if (this.right) { - return VERTICAL_RIGHT; + if (this.left) { + if (this.right) { + return HORIZONTAL_UP; + } else { + return UP_LEFT; + } } else { - return VERTICAL; + if (this.right) { + return UP_RIGHT; + } else { + return UP; + } } } } else { - if (this.left) { - if (this.right) { - return HORIZONTAL_UP; + if (this.down) { + if (this.left) { + if (this.right) { + return HORIZONTAL_DOWN; + } else { + return DOWN_LEFT; + } } else { - return UP_LEFT; + if (this.right) { + return DOWN_RIGHT; + } else { + return DOWN; + } } } else { - if (this.right) { - return UP_RIGHT; + if (this.left) { + if (this.right) { + return HORIZONTAL; + } else { + return LEFT; + } } else { - return UP; - } - } - } - } else { - if (this.down) { - if (this.left) { - if (this.right) { - return HORIZONTAL_DOWN; - } else { - return DOWN_LEFT; - } - } else { - if (this.right) { - return 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 " "; + if (this.right) { + return RIGHT; + } else { + return " "; + } } } } diff --git a/src/test/java/ch/fritteli/labyrinth/TextRendererTest.java b/src/test/java/ch/fritteli/labyrinth/TextRendererTest.java index b5ed6de..2eabeea 100644 --- a/src/test/java/ch/fritteli/labyrinth/TextRendererTest.java +++ b/src/test/java/ch/fritteli/labyrinth/TextRendererTest.java @@ -1,6 +1,6 @@ package ch.fritteli.labyrinth; -import ch.fritteli.labyrinth.TextRenderer.CharDefinition; +import ch.fritteli.labyrinth.TextRenderer.Generator.CharDefinition; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test;