Refactoring, more tests.
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			This commit is contained in:
		
							parent
							
								
									ad0759b36f
								
							
						
					
					
						commit
						d6029471f0
					
				
					 7 changed files with 93 additions and 64 deletions
				
			
		|  | @ -7,26 +7,16 @@ import ch.fritteli.labyrinth.renderer.htmlfile.HTMLFileRenderer; | |||
| import ch.fritteli.labyrinth.renderer.pdffile.PDFFileRenderer; | ||||
| import ch.fritteli.labyrinth.renderer.text.TextRenderer; | ||||
| import ch.fritteli.labyrinth.renderer.textfile.TextFileRenderer; | ||||
| import io.vavr.control.Try; | ||||
| import io.vavr.control.Option; | ||||
| import lombok.NonNull; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.nio.file.Path; | ||||
| import java.nio.file.Paths; | ||||
| 
 | ||||
| public class Main { | ||||
|     public static void main(@NonNull final String[] args) throws IOException { | ||||
|         final String listenerPort = System.getProperty("fritteli.labyrinth.listenerport"); | ||||
|         if (listenerPort != null) { | ||||
|             final Try<Integer> portTry = Try.of(() -> Integer.valueOf(listenerPort)); | ||||
|             if (portTry.isFailure()) { | ||||
|                 System.err.println("Invalid port specified via sysprop 'fritteli.labyrinth.listenerport': " + listenerPort + ". Not starting webserver."); | ||||
|             } else { | ||||
|                 final TheListener listener = new TheListener(portTry.get()); | ||||
|                 listener.start(); | ||||
|                 System.out.println("Listening on port " + portTry.get()); | ||||
|             } | ||||
|         } else { | ||||
|     public static void main(@NonNull final String[] args) { | ||||
|         final Option<TheListener> listener = TheListener.createListener(); | ||||
|         if (listener.isEmpty()) { | ||||
|             System.out.println("In order to start the webserver, specify the port to listen to via the system property 'fritteli.labyrinth.listenerport', i.e.: -Dfritteli.labyrinth.listenerport=12345"); | ||||
|             int width = 100; | ||||
|             int height = 100; | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ package ch.fritteli.labyrinth.model; | |||
| import io.vavr.control.Option; | ||||
| import lombok.Getter; | ||||
| import lombok.NonNull; | ||||
| import org.jetbrains.annotations.Nullable; | ||||
| 
 | ||||
| import java.util.Deque; | ||||
| import java.util.LinkedList; | ||||
|  | @ -43,36 +42,26 @@ public class Labyrinth { | |||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public Tile getTileAt(@NonNull final Position position) { | ||||
|     public Option<Tile> getTileAt(@NonNull final Position position) { | ||||
|         return this.getTileAt(position.getX(), position.getY()); | ||||
|     } | ||||
| 
 | ||||
|     @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) { | ||||
|     public Option<Tile> getTileAt(final int x, final int y) { | ||||
|         if (x < 0 || y < 0 || x >= this.width || y >= this.height) { | ||||
|             return null; | ||||
|             return Option.none(); | ||||
|         } | ||||
|         return this.getTileAt(x, y); | ||||
|         return Option.of(this.field[x][y]); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     Tile getStartTile() { | ||||
|         return this.getTileAt(this.start); | ||||
|         return this.getTileAt(this.start).get(); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     Tile getEndTile() { | ||||
|         return this.getTileAt(this.end); | ||||
|         return this.getTileAt(this.end).get(); | ||||
|     } | ||||
| 
 | ||||
|     private void initField() { | ||||
|  | @ -124,13 +113,13 @@ public class Labyrinth { | |||
|         private void dig() { | ||||
|             while (!this.positions.isEmpty()) { | ||||
|                 final Position currentPosition = this.positions.peek(); | ||||
|                 final Tile currentTile = Labyrinth.this.getTileAt(currentPosition); | ||||
|                 final Tile currentTile = Labyrinth.this.getTileAt(currentPosition).get(); | ||||
|                 final Option<Direction> directionToDigTo = currentTile.getRandomAvailableDirection(Labyrinth.this.random); | ||||
|                 if (directionToDigTo.isDefined()) { | ||||
|                     final Direction digTo = directionToDigTo.get(); | ||||
|                     final Direction digFrom = digTo.invert(); | ||||
|                     final Position neighborPosition = currentPosition.move(digTo); | ||||
|                     final Tile neighborTile = Labyrinth.this.getTileAt(neighborPosition); | ||||
|                     final Tile neighborTile = Labyrinth.this.getTileAt(neighborPosition).get(); | ||||
|                     if (currentTile.digTo(digTo) && neighborTile.digFrom(digFrom)) { | ||||
|                         // all ok! | ||||
|                         this.positions.push(neighborPosition); | ||||
|  | @ -149,7 +138,7 @@ public class Labyrinth { | |||
|         } | ||||
| 
 | ||||
|         private void markSolution() { | ||||
|             this.positions.forEach(position -> Labyrinth.this.getTileAt(position).setSolution()); | ||||
|             this.positions.forEach(position -> Labyrinth.this.getTileAt(position).get().setSolution()); | ||||
|         } | ||||
| 
 | ||||
|         private void postDig() { | ||||
|  |  | |||
|  | @ -76,6 +76,21 @@ public class TheListener { | |||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     public static Option<TheListener> createListener() { | ||||
|         final String listenerPort = System.getProperty("fritteli.labyrinth.listenerport"); | ||||
|         final Option<TheListener> listenerOption = Option.of(listenerPort) | ||||
|                 .toTry() | ||||
|                 .map(Integer::valueOf) | ||||
|                 .onFailure(cause -> System.err.println("Invalid port specified via system property 'fritteli.labyrinth.listenerport': " | ||||
|                         + listenerPort | ||||
|                         + ". Not starting webserver.")) | ||||
|                 .mapTry(TheListener::new) | ||||
|                 .onFailure(cause -> System.err.println("Failed to create Listener: " + cause)) | ||||
|                 .toOption(); | ||||
|         listenerOption.forEach(TheListener::start); | ||||
|         return listenerOption; | ||||
|     } | ||||
| 
 | ||||
|     private <T> T getOrDefault(@NonNull final Option<String> input, @NonNull final Function<String, T> mapper, @Nullable final T defaultValue) { | ||||
|         return input.toTry().map(mapper).getOrElse(defaultValue); | ||||
|     } | ||||
|  | @ -111,11 +126,15 @@ public class TheListener { | |||
|     } | ||||
| 
 | ||||
|     public void start() { | ||||
|         Runtime.getRuntime().addShutdownHook(new Thread(this::stop, "listener-stopper")); | ||||
|         this.httpServer.start(); | ||||
|         System.out.println("Listening on " + this.httpServer.getAddress()); | ||||
|     } | ||||
| 
 | ||||
|     public void stop() { | ||||
|         this.httpServer.stop(10); | ||||
|         System.out.println("Stopping listener ..."); | ||||
|         this.httpServer.stop(5); | ||||
|         System.out.println("Listener stopped."); | ||||
|     } | ||||
| 
 | ||||
|     private enum RequestParameter { | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ class Generator { | |||
|     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); | ||||
|             final Tile currentTile = this.labyrinth.getTileAt(x, this.y).get(); | ||||
|             sb.append("<td class=\""); | ||||
|             sb.append(this.getClasses(currentTile).mkString(" ")); | ||||
|             sb.append("\"> </td>"); | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import ch.fritteli.labyrinth.model.Direction; | |||
| import ch.fritteli.labyrinth.model.Labyrinth; | ||||
| import ch.fritteli.labyrinth.model.Position; | ||||
| import ch.fritteli.labyrinth.model.Tile; | ||||
| import io.vavr.control.Option; | ||||
| import lombok.NonNull; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.Value; | ||||
|  | @ -74,7 +75,7 @@ class Generator { | |||
|             boolean isPainting = false; | ||||
|             coordinate = coordinate.withY(y); | ||||
|             for (int x = 0; x < this.labyrinth.getWidth(); x++) { | ||||
|                 final Tile currentTile = this.labyrinth.getTileAt(x, y); | ||||
|                 final Tile currentTile = this.labyrinth.getTileAt(x, y).get(); | ||||
|                 coordinate = coordinate.withX(x); | ||||
|                 if (currentTile.hasWallAt(Direction.TOP)) { | ||||
|                     if (!isPainting) { | ||||
|  | @ -105,7 +106,7 @@ class Generator { | |||
|         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); | ||||
|             final Tile currentTile = this.labyrinth.getTileAt(x, y - 1).get(); | ||||
|             coordinate = coordinate.withX(x); | ||||
|             if (currentTile.hasWallAt(Direction.BOTTOM)) { | ||||
|                 if (!isPainting) { | ||||
|  | @ -140,7 +141,7 @@ class Generator { | |||
|             boolean isPainting = false; | ||||
|             coordinate = coordinate.withX(x); | ||||
|             for (int y = 0; y < this.labyrinth.getHeight(); y++) { | ||||
|                 final Tile currentTile = this.labyrinth.getTileAt(x, y); | ||||
|                 final Tile currentTile = this.labyrinth.getTileAt(x, y).get(); | ||||
|                 coordinate = coordinate.withY(y); | ||||
|                 if (currentTile.hasWallAt(Direction.LEFT)) { | ||||
|                     if (!isPainting) { | ||||
|  | @ -171,7 +172,7 @@ class Generator { | |||
|         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); | ||||
|             final Tile currentTile = this.labyrinth.getTileAt(x - 1, y).get(); | ||||
|             coordinate = coordinate.withY(y); | ||||
|             if (currentTile.hasWallAt(Direction.RIGHT)) { | ||||
|                 if (!isPainting) { | ||||
|  | @ -220,15 +221,15 @@ class Generator { | |||
| 
 | ||||
|     @NonNull | ||||
|     private Position findNextSolutionPosition(@Nullable final Position previousPosition, @NonNull final Position currentPosition) { | ||||
|         final Tile currentTile = this.labyrinth.getTileAt(currentPosition); | ||||
|         final Tile currentTile = this.labyrinth.getTileAt(currentPosition).get(); | ||||
|         for (final Direction direction : Direction.values()) { | ||||
|             if (!currentTile.hasWallAt(direction)) { | ||||
|                 final Position position = currentPosition.move(direction); | ||||
|                 final Tile tileAtPosition = this.labyrinth.getTileAtOrNull(position); | ||||
|                 if (position.equals(previousPosition) || tileAtPosition == null) { | ||||
|                 final Option<Tile> tileAtPosition = this.labyrinth.getTileAt(position); | ||||
|                 if (position.equals(previousPosition)) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (tileAtPosition.isSolution()) { | ||||
|                 if (tileAtPosition.map(Tile::isSolution).getOrElse(false)) { | ||||
|                     return position; | ||||
|                 } | ||||
|             } | ||||
|  |  | |||
|  | @ -3,10 +3,10 @@ package ch.fritteli.labyrinth.renderer.text; | |||
| import ch.fritteli.labyrinth.model.Direction; | ||||
| import ch.fritteli.labyrinth.model.Labyrinth; | ||||
| import ch.fritteli.labyrinth.model.Tile; | ||||
| import io.vavr.control.Option; | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NonNull; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import org.jetbrains.annotations.Nullable; | ||||
| 
 | ||||
| @RequiredArgsConstructor(access = AccessLevel.PACKAGE) | ||||
| class Generator { | ||||
|  | @ -22,11 +22,11 @@ class Generator { | |||
|     } | ||||
| 
 | ||||
|     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 Tile currentTile = this.labyrinth.getTileAt(this.x, this.y).get(); | ||||
|         final Option<Tile> topTile = this.labyrinth.getTileAt(this.x, this.y - 1); | ||||
|         final Option<Tile> rightTile = this.labyrinth.getTileAt(this.x + 1, this.y); | ||||
|         final Option<Tile> bottomTile = this.labyrinth.getTileAt(this.x, this.y + 1); | ||||
|         final Option<Tile> leftTile = this.labyrinth.getTileAt(this.x - 1, this.y); | ||||
|         final String s; | ||||
|         switch (this.line) { | ||||
|             case 0: | ||||
|  | @ -69,7 +69,7 @@ class Generator { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private String renderTopLine(@NonNull final Tile currentTile, @Nullable final Tile leftTile, @Nullable final Tile topTile) { | ||||
|     private String renderTopLine(@NonNull final Tile currentTile, @NonNull final Option<Tile> leftTile, @NonNull final Option<Tile> topTile) { | ||||
|         final CharDefinition charDef1 = new CharDefinition(); | ||||
|         final CharDefinition charDef2 = new CharDefinition(); | ||||
|         final CharDefinition charDef3 = new CharDefinition(); | ||||
|  | @ -82,7 +82,7 @@ class Generator { | |||
|             charDef2.horizontal(); | ||||
|             charDef3.left(); | ||||
|         } else { | ||||
|             if (this.isSolution(currentTile) && (this.isSolution(topTile) || topTile == null)) { | ||||
|             if (this.isSolution(currentTile) && (this.isSolution(topTile) || topTile.isEmpty())) { | ||||
|                 charDef2.solution().vertical(); | ||||
|             } | ||||
|         } | ||||
|  | @ -106,10 +106,10 @@ class Generator { | |||
|     } | ||||
| 
 | ||||
|     private String renderCenterLine(@NonNull final Tile currentTile, | ||||
|                                     @Nullable final Tile topTile, | ||||
|                                     @Nullable final Tile rightTile, | ||||
|                                     @Nullable final Tile bottomTile, | ||||
|                                     @Nullable final Tile leftTile) { | ||||
|                                     @NonNull final Option<Tile> topTile, | ||||
|                                     @NonNull final Option<Tile> rightTile, | ||||
|                                     @NonNull final Option<Tile> bottomTile, | ||||
|                                     @NonNull final Option<Tile> leftTile) { | ||||
|         final CharDefinition charDef1 = new CharDefinition(); | ||||
|         final CharDefinition charDef2 = new CharDefinition(); | ||||
|         final CharDefinition charDef3 = new CharDefinition(); | ||||
|  | @ -123,16 +123,16 @@ class Generator { | |||
|         } | ||||
|         if (this.isSolution(currentTile)) { | ||||
|             charDef2.solution(); | ||||
|             if (!currentTile.hasWallAt(Direction.LEFT) && (this.isSolution(leftTile) || leftTile == null)) { | ||||
|             if (!currentTile.hasWallAt(Direction.LEFT) && (this.isSolution(leftTile) || leftTile.isEmpty())) { | ||||
|                 charDef2.left(); | ||||
|             } | ||||
|             if (!currentTile.hasWallAt(Direction.TOP) && (this.isSolution(topTile) || topTile == null)) { | ||||
|             if (!currentTile.hasWallAt(Direction.TOP) && (this.isSolution(topTile) || topTile.isEmpty())) { | ||||
|                 charDef2.up(); | ||||
|             } | ||||
|             if (!currentTile.hasWallAt(Direction.RIGHT) && (this.isSolution(rightTile) || rightTile == null)) { | ||||
|             if (!currentTile.hasWallAt(Direction.RIGHT) && (this.isSolution(rightTile) || rightTile.isEmpty())) { | ||||
|                 charDef2.right(); | ||||
|             } | ||||
|             if (!currentTile.hasWallAt(Direction.BOTTOM) && (this.isSolution(bottomTile) || bottomTile == null)) { | ||||
|             if (!currentTile.hasWallAt(Direction.BOTTOM) && (this.isSolution(bottomTile) || bottomTile.isEmpty())) { | ||||
|                 charDef2.down(); | ||||
|             } | ||||
|         } | ||||
|  | @ -153,7 +153,7 @@ class Generator { | |||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     private String renderBottomLine(@NonNull final Tile currentTile, @Nullable final Tile leftTile) { | ||||
|     private String renderBottomLine(@NonNull final Tile currentTile, @NonNull final Option<Tile> leftTile) { | ||||
|         String result; | ||||
|         final CharDefinition charDef1 = new CharDefinition(); | ||||
|         final CharDefinition charDef2 = new CharDefinition(); | ||||
|  | @ -186,11 +186,15 @@ class Generator { | |||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     private boolean hasWallAt(@Nullable final Tile tile, @NonNull final Direction direction) { | ||||
|         return tile != null && tile.hasWallAt(direction); | ||||
|     private boolean hasWallAt(@NonNull final Option<Tile> tile, @NonNull final Direction direction) { | ||||
|         return tile.map(t -> t.hasWallAt(direction)).getOrElse(false); | ||||
|     } | ||||
| 
 | ||||
|     private boolean isSolution(@Nullable final Tile tile) { | ||||
|     private boolean isSolution(@NonNull final Tile tile) { | ||||
|         return this.renderSolution && tile != null && tile.isSolution(); | ||||
|     } | ||||
| 
 | ||||
|     private boolean isSolution(@NonNull final Option<Tile> tile) { | ||||
|         return this.renderSolution && tile.map(Tile::isSolution).getOrElse(false); | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										26
									
								
								src/test/java/ch/fritteli/labyrinth/model/LabyrinthTest.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/test/java/ch/fritteli/labyrinth/model/LabyrinthTest.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | |||
| package ch.fritteli.labyrinth.model; | ||||
| 
 | ||||
| import org.junit.jupiter.api.Test; | ||||
| 
 | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| import static org.junit.jupiter.api.Assertions.assertThrows; | ||||
| 
 | ||||
| class LabyrinthTest { | ||||
|     @Test | ||||
|     void testConstruct() { | ||||
|         // act / assert on simple cases | ||||
|         assertThrows(IllegalArgumentException.class, () -> new Labyrinth(0, 0)); | ||||
|         assertThrows(IllegalArgumentException.class, () -> new Labyrinth(0, 0, 0)); | ||||
| 
 | ||||
|         // now for the real work: | ||||
|         // arrange | ||||
|         final Labyrinth sut = new Labyrinth(2, 3, 5); | ||||
| 
 | ||||
|         // assert | ||||
|         assertEquals(2, sut.getWidth()); | ||||
|         assertEquals(3, sut.getHeight()); | ||||
|         assertEquals(5, sut.getRandomSeed()); | ||||
|         assertEquals(new Position(0, 0), sut.getStart()); | ||||
|         assertEquals(new Position(1, 2), sut.getEnd()); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue