Implement a nicer text renderer.
This commit is contained in:
		
							parent
							
								
									483e00d964
								
							
						
					
					
						commit
						0a27870fb8
					
				
					 7 changed files with 302 additions and 125 deletions
				
			
		|  | @ -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<Position> 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() { | ||||
|  |  | |||
|  | @ -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)); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										250
									
								
								src/main/java/labyrinth/TextRenderer.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								src/main/java/labyrinth/TextRenderer.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 " "; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -46,4 +46,8 @@ public class Tile { | |||
|     public Option<Direction> getRandomAvailableDirection() { | ||||
|         return Stream.ofAll(this.walls.getSet(true)).shuffle().headOption(); | ||||
|     } | ||||
| 
 | ||||
|     public boolean hasWallAt(@NonNull final Direction direction) { | ||||
|         return this.walls.isSet(direction); | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										27
									
								
								src/test/java/labyrinth/TextRendererTest.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/test/java/labyrinth/TextRendererTest.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -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()); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue