diff --git a/src/main/java/ch/fritteli/labyrinth/Labyrinth.java b/src/main/java/ch/fritteli/labyrinth/Labyrinth.java index c8baa80..93d19a9 100644 --- a/src/main/java/ch/fritteli/labyrinth/Labyrinth.java +++ b/src/main/java/ch/fritteli/labyrinth/Labyrinth.java @@ -1,14 +1,17 @@ package ch.fritteli.labyrinth; import io.vavr.control.Option; +import lombok.Getter; import lombok.NonNull; import java.util.Deque; import java.util.LinkedList; public class Labyrinth { - final Tile[][] field; + private final Tile[][] field; + @Getter private final int width; + @Getter private final int height; public Labyrinth(final int width, final int height) { @@ -41,34 +44,35 @@ public class Labyrinth { private void hardenWalls(@NonNull final Tile tile, final int x, final int y) { if (x == 0) { tile.preventDiggingToOrFrom(Direction.LEFT); - } else if (x == width - 1) { + } else if (x == this.width - 1) { tile.preventDiggingToOrFrom(Direction.RIGHT); } if (y == 0) { tile.preventDiggingToOrFrom(Direction.TOP); - } else if (y == height - 1) { + } else if (y == this.height - 1) { tile.preventDiggingToOrFrom(Direction.BOTTOM); } } private void generate() { - new Generator(); + new Generator().run(); } - public class Generator { + private class Generator { private final Deque positions = new LinkedList<>(); - Generator() { + void run() { + this.preDig(); + this.dig(); + this.postDig(); + } + + private void preDig() { 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.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() { @@ -94,5 +98,12 @@ public class Labyrinth { } } } + + private void postDig() { + final Position topLeft = new Position(0, 0); + final Tile topLeftTile = Labyrinth.this.getTileAt(topLeft); + topLeftTile.enableDiggingToOrFrom(Direction.TOP); + topLeftTile.digTo(Direction.TOP); + } } } diff --git a/src/main/java/ch/fritteli/labyrinth/TextRenderer.java b/src/main/java/ch/fritteli/labyrinth/TextRenderer.java index be16a91..7b60640 100644 --- a/src/main/java/ch/fritteli/labyrinth/TextRenderer.java +++ b/src/main/java/ch/fritteli/labyrinth/TextRenderer.java @@ -1,33 +1,19 @@ package ch.fritteli.labyrinth; -import lombok.Builder; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; import lombok.NonNull; -import lombok.Value; -import lombok.With; +import lombok.experimental.FieldDefaults; 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 int line = 0; private TextRenderer(@NonNull final Labyrinth labyrinth, final int width, final int height) { this.labyrinth = labyrinth; @@ -36,11 +22,11 @@ public class TextRenderer { } public static String render(@NonNull final Labyrinth labyrinth) { - final int width = labyrinth.field.length; + final int width = labyrinth.getWidth(); if (width == 0) { return ""; } - final int height = labyrinth.field[0].length; + final int height = labyrinth.getHeight(); final TextRenderer renderer = new TextRenderer(labyrinth, width, height); final StringBuilder sb = new StringBuilder(); @@ -54,135 +40,200 @@ public class TextRenderer { return this.y < this.height; } + private Tile getTileOrNull(final int x, final int y) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + return null; + } + return this.labyrinth.getTileAt(x, y); + } + 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) { + 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.renderTop(currentTile, leftTile, topTile); + s = this.renderTopLine(currentTile, leftTile, topTile); break; case 1: - s = this.renderCenter(currentTile); + s = this.renderCenterLine(currentTile); break; case 2: - s = this.renderBottom(currentTile, leftTile); + s = this.renderBottomLine(currentTile, leftTile); + break; + default: + s = ""; break; } // do some magic ... this.x++; if (this.x == this.width) { this.x = 0; - this.border++; + this.line++; } - if (this.border == 2 && this.y < this.height - 1) { - this.border = 0; + if (this.line == 2 && this.y < this.height - 1) { + this.line = 0; this.y++; } - if (this.border == 3) { - this.border = 0; + if (this.line == 3) { + this.line = 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(); + 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)) { - builder1.down(true); + charDef1.down(); } if (currentTile.hasWallAt(Direction.TOP)) { - builder1.right(true); - builder2.left(true).right(true); - builder3.left(true); + charDef1.right(); + charDef2.horizontal(); + charDef3.left(); } if (currentTile.hasWallAt(Direction.RIGHT)) { - builder3.down(true); + charDef3.down(); } if (leftTile != null && leftTile.hasWallAt(Direction.TOP)) { - builder1.left(true); + charDef1.left(); } if (topTile != null) { if (topTile.hasWallAt(Direction.LEFT)) { - builder1.up(true); + charDef1.up(); } if (topTile.hasWallAt(Direction.RIGHT)) { - builder3.up(true); + charDef3.up(); } } - result = builder1.build().toString() + builder2.build(); + result = charDef1.toString() + charDef2; if (this.x == this.width - 1) { - result += builder3.build() + "\n"; + result += charDef3 + "\n"; } return result; } - private String renderCenter(@NonNull final Tile currentTile) { - final CharDefinition.CharDefinitionBuilder builder1 = CharDefinition.builder(); - final CharDefinition.CharDefinitionBuilder builder2 = CharDefinition.builder(); + private String renderCenterLine(@NonNull final Tile currentTile) { + final CharDefinition charDef1 = new CharDefinition(); + final CharDefinition charDef2 = new CharDefinition(); String result; if (currentTile.hasWallAt(Direction.LEFT)) { - builder1.up(true).down(true); + charDef1.vertical(); } if (currentTile.hasWallAt(Direction.RIGHT)) { - builder2.up(true).down(true); + charDef2.vertical(); } - result = builder1.build() + " "; + result = charDef1 + " "; if (this.x == this.width - 1) { - result += builder2.build() + "\n"; + result += charDef2 + "\n"; } return result; } - private String renderBottom(@NonNull final Tile currentTile, @Nullable final Tile leftTile) { + private String renderBottomLine(@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(); + final CharDefinition charDef1 = new CharDefinition(); + final CharDefinition charDef2 = new CharDefinition(); + final CharDefinition charDef3 = new CharDefinition(); if (currentTile.hasWallAt(Direction.LEFT)) { - builder1.up(true); + charDef1.up(); } if (currentTile.hasWallAt(Direction.BOTTOM)) { - builder1.right(true); - builder2.left(true).right(true); - builder3.left(true); + charDef1.right(); + charDef2.horizontal(); + charDef3.left(); } if (currentTile.hasWallAt(Direction.RIGHT)) { - builder3.up(true); + charDef3.up(); } if (leftTile != null && leftTile.hasWallAt(Direction.BOTTOM)) { - builder1.left(true); + charDef1.left(); } - result = builder1.build().toString() + builder2.build(); + result = charDef1.toString() + charDef2; if (this.x == this.width - 1) { - result += builder3.build(); + result += charDef3; } return result; } - @Value - @Builder - @With + @FieldDefaults(level = AccessLevel.PRIVATE) + @NoArgsConstructor + @AllArgsConstructor static class CharDefinition { - @Builder.Default + // ─ + 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; - @Builder.Default boolean down = false; - @Builder.Default boolean left = false; - @Builder.Default 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) { diff --git a/src/main/java/ch/fritteli/labyrinth/Tile.java b/src/main/java/ch/fritteli/labyrinth/Tile.java index 20695f7..3ac0a80 100644 --- a/src/main/java/ch/fritteli/labyrinth/Tile.java +++ b/src/main/java/ch/fritteli/labyrinth/Tile.java @@ -3,16 +3,12 @@ package ch.fritteli.labyrinth; import io.vavr.collection.Stream; import io.vavr.control.Option; import lombok.AccessLevel; -import lombok.Getter; import lombok.NonNull; import lombok.experimental.FieldDefaults; @FieldDefaults(level = AccessLevel.PRIVATE) public class Tile { - // FIXME remove me; only for debugging - @Getter final Walls walls = new Walls(); - @Getter boolean visited = false; public Tile() { diff --git a/src/test/java/ch/fritteli/labyrinth/TextRendererTest.java b/src/test/java/ch/fritteli/labyrinth/TextRendererTest.java index 9453876..b5ed6de 100644 --- a/src/test/java/ch/fritteli/labyrinth/TextRendererTest.java +++ b/src/test/java/ch/fritteli/labyrinth/TextRendererTest.java @@ -1,27 +1,32 @@ package ch.fritteli.labyrinth; +import ch.fritteli.labyrinth.TextRenderer.CharDefinition; +import org.junit.jupiter.api.Nested; 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()); + @Nested + class CharDefinitionTest { + @Test + void testRendering() { + assertEquals(" ", new CharDefinition(false, false, false, false).toString()); + assertEquals("╶", new CharDefinition(false, false, false, true).toString()); + assertEquals("╴", new CharDefinition(false, false, true, false).toString()); + assertEquals("─", new CharDefinition(false, false, true, true).toString()); + assertEquals("╷", new CharDefinition(false, true, false, false).toString()); + assertEquals("┌", new CharDefinition(false, true, false, true).toString()); + assertEquals("┐", new CharDefinition(false, true, true, false).toString()); + assertEquals("┬", new CharDefinition(false, true, true, true).toString()); + assertEquals("╵", new CharDefinition(true, false, false, false).toString()); + assertEquals("└", new CharDefinition(true, false, false, true).toString()); + assertEquals("┘", new CharDefinition(true, false, true, false).toString()); + assertEquals("┴", new CharDefinition(true, false, true, true).toString()); + assertEquals("│", new CharDefinition(true, true, false, false).toString()); + assertEquals("├", new CharDefinition(true, true, false, true).toString()); + assertEquals("┤", new CharDefinition(true, true, true, false).toString()); + assertEquals("┼", new CharDefinition(true, true, true, true).toString()); + } } }