diff --git a/pom.xml b/pom.xml index 8afd225..d825c3c 100644 --- a/pom.xml +++ b/pom.xml @@ -25,5 +25,10 @@ io.vavr vavr + + org.junit.jupiter + junit-jupiter-api + test + diff --git a/src/main/java/labyrinth/Labyrinth.java b/src/main/java/labyrinth/Labyrinth.java index c2599e5..5480fcb 100644 --- a/src/main/java/labyrinth/Labyrinth.java +++ b/src/main/java/labyrinth/Labyrinth.java @@ -20,7 +20,11 @@ public class Labyrinth { } Tile getTileAt(@NonNull final Position position) { - return this.field[position.getX()][position.getY()]; + return this.getTileAt(position.getX(), position.getY()); + } + + Tile getTileAt(final int x, final int y) { + return this.field[x][y]; } private void initField() { @@ -41,9 +45,7 @@ public class Labyrinth { tile.preventDiggingToOrFrom(Direction.RIGHT); } if (y == 0) { - if (x != 0) { - tile.preventDiggingToOrFrom(Direction.TOP); - } + tile.preventDiggingToOrFrom(Direction.TOP); } else if (y == height - 1) { tile.preventDiggingToOrFrom(Direction.BOTTOM); } @@ -57,14 +59,16 @@ public class Labyrinth { private final Deque positions = new LinkedList<>(); Generator() { - final Position topLeft = new Position(0, 0); - Labyrinth.this.getTileAt(topLeft).digFrom(Direction.TOP); - this.positions.push(topLeft); - this.dig(); final Position bottomRight = new Position(Labyrinth.this.width - 1, Labyrinth.this.height - 1); final Tile bottomRightTile = Labyrinth.this.getTileAt(bottomRight); bottomRightTile.enableDiggingToOrFrom(Direction.BOTTOM); - bottomRightTile.digTo(Direction.BOTTOM); + bottomRightTile.digFrom(Direction.BOTTOM); + this.positions.push(bottomRight); + this.dig(); + final Position topLeft = new Position(0, 0); + final Tile topLeftTile = Labyrinth.this.getTileAt(topLeft); + topLeftTile.enableDiggingToOrFrom(Direction.TOP); + topLeftTile.digTo(Direction.TOP); } private void dig() { diff --git a/src/main/java/labyrinth/Main.java b/src/main/java/labyrinth/Main.java index 26f0971..2dfaada 100644 --- a/src/main/java/labyrinth/Main.java +++ b/src/main/java/labyrinth/Main.java @@ -4,9 +4,9 @@ import lombok.NonNull; public class Main { public static void main(@NonNull final String[] args) { - int width = 5; - int height = 8; + int width = 110; + int height = 15; final Labyrinth labyrinth = new Labyrinth(width, height); - System.out.println(SimpleTextRenderer.render(labyrinth)); + System.out.println(TextRenderer.render(labyrinth)); } } diff --git a/src/main/java/labyrinth/SimpleTextRenderer.java b/src/main/java/labyrinth/SimpleTextRenderer.java deleted file mode 100644 index e80bc69..0000000 --- a/src/main/java/labyrinth/SimpleTextRenderer.java +++ /dev/null @@ -1,113 +0,0 @@ -package labyrinth; - -import lombok.NonNull; -import lombok.experimental.UtilityClass; - -@UtilityClass -public class SimpleTextRenderer { - private static final char TOP_LEFT = '\u250c'; - private static final char TOP_RIGHT = '\u2510'; - private static final char MIDDLE_LEFT = '\u251c'; - private static final char MIDDLE_RIGHT = '\u2524'; - private static final char BOTTOM_LEFT = '\u2514'; - private static final char BOTTOM_RIGHT = '\u2518'; - private static final char HORIZONTAL = '\u2500'; - private static final char VERTICAL = '\u2502'; - - public String render(@NonNull final Labyrinth labyrinth) { - final int width; - final int height; - width = labyrinth.field.length; - if (width == 0) { - return ""; - } - height = labyrinth.field[0].length; - StringBuilder sb = new StringBuilder(); - for (int y = 0; y < height; y++) { - // TOP WALL - for (int x = 0; x < width; x++) { - final Tile tile = labyrinth.getTileAt(new Position(x, y)); - final boolean top = tile.getWalls().isSet(Direction.TOP); - final boolean topHard = tile.getWalls().getHardened().contains(Direction.TOP); - sb.append(TOP_LEFT); - if (topHard) { - if (top) { - sb.append(HORIZONTAL); - } else { - sb.append("?"); - } - } else { - if (top) { - sb.append("-"); - } else { - sb.append(" "); - } - } - sb.append(TOP_RIGHT); - } - sb.append("\n"); - // LEFT WALL, CENTER, RIGHT WALL - for (int x = 0; x < width; x++) { - final Tile tile = labyrinth.getTileAt(new Position(x, y)); - // left - final boolean left = tile.getWalls().isSet(Direction.LEFT); - final boolean leftHard = tile.getWalls().getHardened().contains(Direction.LEFT); - if (leftHard) { - if (left) { - sb.append(VERTICAL); - } else { - sb.append("?"); - } - } else { - if (left) { - sb.append(":"); - } else { - sb.append(" "); - } - } - // center - sb.append(" "); - // right - final boolean right = tile.getWalls().isSet(Direction.RIGHT); - final boolean rightHard = tile.getWalls().getHardened().contains(Direction.RIGHT); - if (rightHard) { - if (right) { - sb.append(VERTICAL); - } else { - sb.append("?"); - } - } else { - if (right) { - sb.append(":"); - } else { - sb.append(" "); - } - } - } - sb.append("\n"); - // BOTTOM WALL - for (int x = 0; x < width; x++) { - final Tile tile = labyrinth.getTileAt(new Position(x, y)); - final boolean bottom = tile.getWalls().isSet(Direction.BOTTOM); - final boolean bottomHard = tile.getWalls().getHardened().contains(Direction.BOTTOM); - sb.append(BOTTOM_LEFT); - if (bottomHard) { - if (bottom) { - sb.append(HORIZONTAL); - } else { - sb.append("?"); - } - } else { - if (bottom) { - sb.append("-"); - } else { - sb.append(" "); - } - } - sb.append(BOTTOM_RIGHT); - } - sb.append("\n"); - } - return sb.toString(); - } -} diff --git a/src/main/java/labyrinth/TextRenderer.java b/src/main/java/labyrinth/TextRenderer.java new file mode 100644 index 0000000..d04ae0c --- /dev/null +++ b/src/main/java/labyrinth/TextRenderer.java @@ -0,0 +1,250 @@ +package labyrinth; + +import lombok.Builder; +import lombok.NonNull; +import lombok.Value; +import lombok.With; +import org.jetbrains.annotations.Nullable; + +public class TextRenderer { + 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"; + private final Labyrinth labyrinth; + private final int width; + private final int height; + private int x = 0; + private int y = 0; + private int border = 0; + + private TextRenderer(@NonNull final Labyrinth labyrinth, final int width, final int height) { + this.labyrinth = labyrinth; + this.width = width; + this.height = height; + } + + public static String render(@NonNull final Labyrinth labyrinth) { + final int width = labyrinth.field.length; + if (width == 0) { + return ""; + } + final int height = labyrinth.field[0].length; + final TextRenderer renderer = new TextRenderer(labyrinth, width, height); + + final StringBuilder sb = new StringBuilder(); + while (renderer.hasNext()) { + sb.append(renderer.next()); + } + return sb.toString(); + } + + private boolean hasNext() { + return this.y < this.height; + } + + private String next() { + final Tile currentTile = this.labyrinth.getTileAt(this.x, this.y); + final Tile leftTile = this.x > 0 ? this.labyrinth.getTileAt(this.x - 1, this.y) : null; + final Tile topTile = this.y > 0 ? this.labyrinth.getTileAt(this.x, this.y - 1) : null; + String s = ""; + switch (this.border) { + case 0: + s = this.renderTop(currentTile, leftTile, topTile); + break; + case 1: + s = this.renderCenter(currentTile); + break; + case 2: + s = this.renderBottom(currentTile, leftTile); + break; + } + // do some magic ... + this.x++; + if (this.x == this.width) { + this.x = 0; + this.border++; + } + if (this.border == 2 && this.y < this.height - 1) { + this.border = 0; + this.y++; + } + if (this.border == 3) { + this.border = 0; + this.y++; + } + return s; + } + + private String renderTop(@NonNull final Tile currentTile, @Nullable final Tile leftTile, @Nullable final Tile topTile) { + final CharDefinition.CharDefinitionBuilder builder1 = CharDefinition.builder(); + final CharDefinition.CharDefinitionBuilder builder2 = CharDefinition.builder(); + final CharDefinition.CharDefinitionBuilder builder3 = CharDefinition.builder(); + String result; + if (currentTile.hasWallAt(Direction.LEFT)) { + builder1.down(true); + } + if (currentTile.hasWallAt(Direction.TOP)) { + builder1.right(true); + builder2.left(true).right(true); + builder3.left(true); + } + if (currentTile.hasWallAt(Direction.RIGHT)) { + builder3.down(true); + } + if (leftTile != null && leftTile.hasWallAt(Direction.TOP)) { + builder1.left(true); + } + if (topTile != null) { + if (topTile.hasWallAt(Direction.LEFT)) { + builder1.up(true); + } + if (topTile.hasWallAt(Direction.RIGHT)) { + builder3.up(true); + } + } + result = builder1.build().toString() + builder2.build(); + if (this.x == this.width - 1) { + result += builder3.build() + "\n"; + } + return result; + } + + private String renderCenter(@NonNull final Tile currentTile) { + final CharDefinition.CharDefinitionBuilder builder1 = CharDefinition.builder(); + final CharDefinition.CharDefinitionBuilder builder2 = CharDefinition.builder(); + String result; + if (currentTile.hasWallAt(Direction.LEFT)) { + builder1.up(true).down(true); + } + if (currentTile.hasWallAt(Direction.RIGHT)) { + builder2.up(true).down(true); + } + + result = builder1.build() + " "; + + if (this.x == this.width - 1) { + result += builder2.build() + "\n"; + } + + return result; + } + + private String renderBottom(@NonNull final Tile currentTile, @Nullable final Tile leftTile) { + String result; + final CharDefinition.CharDefinitionBuilder builder1 = CharDefinition.builder(); + final CharDefinition.CharDefinitionBuilder builder2 = CharDefinition.builder(); + final CharDefinition.CharDefinitionBuilder builder3 = CharDefinition.builder(); + + if (currentTile.hasWallAt(Direction.LEFT)) { + builder1.up(true); + } + if (currentTile.hasWallAt(Direction.BOTTOM)) { + builder1.right(true); + builder2.left(true).right(true); + builder3.left(true); + } + if (currentTile.hasWallAt(Direction.RIGHT)) { + builder3.up(true); + } + if (leftTile != null && leftTile.hasWallAt(Direction.BOTTOM)) { + builder1.left(true); + } + + result = builder1.build().toString() + builder2.build(); + + if (this.x == this.width - 1) { + result += builder3.build(); + } + return result; + } + + @Value + @Builder + @With + static class CharDefinition { + @Builder.Default + boolean up = false; + @Builder.Default + boolean down = false; + @Builder.Default + boolean left = false; + @Builder.Default + boolean right = false; + + 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 UP_LEFT; + } + } else { + if (this.right) { + return UP_RIGHT; + } 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 " "; + } + } + } + } + } + } +} diff --git a/src/main/java/labyrinth/Tile.java b/src/main/java/labyrinth/Tile.java index 7ad5135..5ba725c 100644 --- a/src/main/java/labyrinth/Tile.java +++ b/src/main/java/labyrinth/Tile.java @@ -46,4 +46,8 @@ public class Tile { public Option getRandomAvailableDirection() { return Stream.ofAll(this.walls.getSet(true)).shuffle().headOption(); } + + public boolean hasWallAt(@NonNull final Direction direction) { + return this.walls.isSet(direction); + } } diff --git a/src/test/java/labyrinth/TextRendererTest.java b/src/test/java/labyrinth/TextRendererTest.java new file mode 100644 index 0000000..d24dec5 --- /dev/null +++ b/src/test/java/labyrinth/TextRendererTest.java @@ -0,0 +1,27 @@ +package labyrinth; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class TextRendererTest { + @Test + void charDefTest() { + assertEquals(" ", new TextRenderer.CharDefinition(false, false, false, false).toString()); + assertEquals("╶", new TextRenderer.CharDefinition(false, false, false, true).toString()); + assertEquals("╴", new TextRenderer.CharDefinition(false, false, true, false).toString()); + assertEquals("─", new TextRenderer.CharDefinition(false, false, true, true).toString()); + assertEquals("╷", new TextRenderer.CharDefinition(false, true, false, false).toString()); + assertEquals("┌", new TextRenderer.CharDefinition(false, true, false, true).toString()); + assertEquals("┐", new TextRenderer.CharDefinition(false, true, true, false).toString()); + assertEquals("┬", new TextRenderer.CharDefinition(false, true, true, true).toString()); + assertEquals("╵", new TextRenderer.CharDefinition(true, false, false, false).toString()); + assertEquals("└", new TextRenderer.CharDefinition(true, false, false, true).toString()); + assertEquals("┘", new TextRenderer.CharDefinition(true, false, true, false).toString()); + assertEquals("┴", new TextRenderer.CharDefinition(true, false, true, true).toString()); + assertEquals("│", new TextRenderer.CharDefinition(true, true, false, false).toString()); + assertEquals("├", new TextRenderer.CharDefinition(true, true, false, true).toString()); + assertEquals("┤", new TextRenderer.CharDefinition(true, true, true, false).toString()); + assertEquals("┼", new TextRenderer.CharDefinition(true, true, true, true).toString()); + } +}