initial commit.
This commit is contained in:
		
							parent
							
								
									817cfbe4f3
								
							
						
					
					
						commit
						f31821f100
					
				
					 8 changed files with 429 additions and 0 deletions
				
			
		
							
								
								
									
										14
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | .idea/ | ||||||
|  | ### Maven template | ||||||
|  | target/ | ||||||
|  | pom.xml.tag | ||||||
|  | pom.xml.releaseBackup | ||||||
|  | pom.xml.versionsBackup | ||||||
|  | pom.xml.next | ||||||
|  | release.properties | ||||||
|  | dependency-reduced-pom.xml | ||||||
|  | buildNumber.properties | ||||||
|  | .mvn/timing.properties | ||||||
|  | # https://github.com/takari/maven-wrapper#usage-without-binary-jar | ||||||
|  | .mvn/wrapper/maven-wrapper.jar | ||||||
|  | 
 | ||||||
							
								
								
									
										29
									
								
								pom.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								pom.xml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | ||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project xmlns="http://maven.apache.org/POM/4.0.0" | ||||||
|  | 		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||||
|  | 		 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||||
|  | 	<modelVersion>4.0.0</modelVersion> | ||||||
|  | 
 | ||||||
|  | 	<parent> | ||||||
|  | 		<groupId>ch.fritteli</groupId> | ||||||
|  | 		<artifactId>fritteli-build-parent</artifactId> | ||||||
|  | 		<version>2.0.3</version> | ||||||
|  | 	</parent> | ||||||
|  | 	<groupId>org.example</groupId> | ||||||
|  | 	<artifactId>labyrinth</artifactId> | ||||||
|  | 	<version>0.0.1-SNAPSHOT</version> | ||||||
|  | 	<dependencies> | ||||||
|  | 		<dependency> | ||||||
|  | 			<groupId>org.projectlombok</groupId> | ||||||
|  | 			<artifactId>lombok</artifactId> | ||||||
|  | 		</dependency> | ||||||
|  | 		<dependency> | ||||||
|  | 			<groupId>org.jetbrains</groupId> | ||||||
|  | 			<artifactId>annotations</artifactId> | ||||||
|  | 		</dependency> | ||||||
|  | 		<dependency> | ||||||
|  | 			<groupId>io.vavr</groupId> | ||||||
|  | 			<artifactId>vavr</artifactId> | ||||||
|  | 		</dependency> | ||||||
|  | 	</dependencies> | ||||||
|  | </project> | ||||||
							
								
								
									
										57
									
								
								src/main/java/labyrinth/Direction.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/main/java/labyrinth/Direction.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,57 @@ | ||||||
|  | package labyrinth; | ||||||
|  | 
 | ||||||
|  | import io.vavr.collection.Stream; | ||||||
|  | import io.vavr.control.Option; | ||||||
|  | import lombok.NonNull; | ||||||
|  | 
 | ||||||
|  | import java.util.EnumSet; | ||||||
|  | import java.util.Random; | ||||||
|  | import java.util.function.UnaryOperator; | ||||||
|  | 
 | ||||||
|  | public enum Direction { | ||||||
|  |     TOP(position -> position.withY(position.getY() - 1)), | ||||||
|  |     BOTTOM(position -> position.withY(position.getY() + 1)), | ||||||
|  |     LEFT(position -> position.withX(position.getX() - 1)), | ||||||
|  |     RIGHT(position -> position.withX(position.getX() + 1)); | ||||||
|  |     private static final Random random = new Random(); | ||||||
|  |     @NonNull | ||||||
|  |     private final UnaryOperator<Position> translation; | ||||||
|  | 
 | ||||||
|  |     Direction(@NonNull final UnaryOperator<Position> translation) { | ||||||
|  |         this.translation = translation; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static Direction getRandom() { | ||||||
|  |         return values()[random.nextInt(4)]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static Option<Direction> getRandomExcluding(@NonNull final EnumSet<Direction> directions) { | ||||||
|  |         final EnumSet<Direction> allowedDirections = EnumSet.complementOf(directions); | ||||||
|  |         return Stream.ofAll(allowedDirections).shuffle().headOption(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static Option<Direction> getRandomExcluding(@NonNull final Direction... directions) { | ||||||
|  |         return Stream.of(values()) | ||||||
|  |                 .removeAll(Stream.of(directions)) | ||||||
|  |                 .shuffle() | ||||||
|  |                 .headOption(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Direction getOpposite() { | ||||||
|  |         switch (this) { | ||||||
|  |             case TOP: | ||||||
|  |                 return BOTTOM; | ||||||
|  |             case LEFT: | ||||||
|  |                 return RIGHT; | ||||||
|  |             case RIGHT: | ||||||
|  |                 return LEFT; | ||||||
|  |             case BOTTOM: | ||||||
|  |                 return TOP; | ||||||
|  |         } | ||||||
|  |         throw new IllegalStateException("Programming error: Not all enum values covered in enum Direction#getOpposite()!"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Position translate(@NonNull final Position position) { | ||||||
|  |         return this.translation.apply(position); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										63
									
								
								src/main/java/labyrinth/Directions.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/main/java/labyrinth/Directions.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | ||||||
|  | package labyrinth; | ||||||
|  | 
 | ||||||
|  | import io.vavr.collection.Stream; | ||||||
|  | import lombok.Getter; | ||||||
|  | import lombok.NonNull; | ||||||
|  | 
 | ||||||
|  | import java.util.EnumSet; | ||||||
|  | import java.util.HashSet; | ||||||
|  | import java.util.Set; | ||||||
|  | 
 | ||||||
|  | public class Directions { | ||||||
|  |     private final Set<Direction> directions = new HashSet<>(); | ||||||
|  |     // FIXME remove getter, only for debugging | ||||||
|  |     @Getter | ||||||
|  |     private final Set<Direction> hardened = new HashSet<>(); | ||||||
|  | 
 | ||||||
|  |     public boolean set(@NonNull final Direction direction) { | ||||||
|  |         return this.directions.add(direction); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setAll() { | ||||||
|  |         this.directions.addAll(EnumSet.allOf(Direction.class)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public boolean clear(@NonNull final Direction direction) { | ||||||
|  |         if (this.hardened.contains(direction)) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         return this.directions.remove(direction); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void clearAll() { | ||||||
|  |         this.directions.clear(); | ||||||
|  |         this.directions.addAll(this.hardened); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public boolean isSet(@NonNull final Direction direction) { | ||||||
|  |         return this.directions.contains(direction); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Iterable<Direction> getSet() { | ||||||
|  |         return this.getSet(false); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Iterable<Direction> getSet(final boolean onlyBrittleWalls) { | ||||||
|  |         if (onlyBrittleWalls) { | ||||||
|  |             return Stream.ofAll(this.directions) | ||||||
|  |                     .removeAll(this.hardened); | ||||||
|  |         } | ||||||
|  |         return Stream.ofAll(this.directions); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void harden(@NonNull final Direction direction) { | ||||||
|  |         if (!this.directions.contains(direction)) { | ||||||
|  |             throw new IllegalStateException("Trying to harden cleared Direction: " + direction); | ||||||
|  |         } | ||||||
|  |         this.hardened.add(direction); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void unharden(@NonNull final Direction direction) { | ||||||
|  |         this.hardened.remove(direction); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										194
									
								
								src/main/java/labyrinth/Labyrinth.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								src/main/java/labyrinth/Labyrinth.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,194 @@ | ||||||
|  | package labyrinth; | ||||||
|  | 
 | ||||||
|  | import io.vavr.control.Option; | ||||||
|  | import lombok.NonNull; | ||||||
|  | 
 | ||||||
|  | import java.util.Deque; | ||||||
|  | import java.util.LinkedList; | ||||||
|  | 
 | ||||||
|  | public class Labyrinth { | ||||||
|  |     private final Tile[][] field; | ||||||
|  |     private final int width; | ||||||
|  |     private final int height; | ||||||
|  | 
 | ||||||
|  |     public Labyrinth(final int width, final int height) { | ||||||
|  |         this.width = width; | ||||||
|  |         this.height = height; | ||||||
|  |         this.field = new Tile[width][height]; | ||||||
|  |         this.initField(); | ||||||
|  |         System.out.println(this); | ||||||
|  |         this.generate(); | ||||||
|  |     } | ||||||
|  |     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'; | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public String toString() { | ||||||
|  |         StringBuilder sb = new StringBuilder(); | ||||||
|  |         for (int y = 0; y < this.height; y++) { | ||||||
|  |             // TOP WALL | ||||||
|  |             for (int x = 0; x < this.width; x++) { | ||||||
|  |                 final Tile tile = this.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 < this.width; x++) { | ||||||
|  |                 final Tile tile = this.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 < this.width; x++) { | ||||||
|  |                 final Tile tile = this.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(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private Tile getTileAt(@NonNull final Position position) { | ||||||
|  |         return this.field[position.getX()][position.getY()]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void initField() { | ||||||
|  |         for (int x = 0; x < this.width; x++) { | ||||||
|  |             this.field[x] = new Tile[this.height]; | ||||||
|  |             for (int y = 0; y < this.height; y++) { | ||||||
|  |                 final Tile tile = new Tile(); | ||||||
|  |                 this.hardenWalls(tile, x, y); | ||||||
|  |                 this.field[x][y] = tile; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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) { | ||||||
|  |             tile.preventDiggingToOrFrom(Direction.RIGHT); | ||||||
|  |         } | ||||||
|  |         if (y == 0) { | ||||||
|  |             if (x != 0) { | ||||||
|  |                 tile.preventDiggingToOrFrom(Direction.TOP); | ||||||
|  |             } | ||||||
|  |         } else if (y == height - 1) { | ||||||
|  |             tile.preventDiggingToOrFrom(Direction.BOTTOM); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void generate() { | ||||||
|  |         new Generator(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public class Generator { | ||||||
|  |         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); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void dig() { | ||||||
|  |             while (!this.positions.isEmpty()) { | ||||||
|  |                 final Position currentPosition = this.positions.peek(); | ||||||
|  |                 final Tile currentTile = Labyrinth.this.getTileAt(currentPosition); | ||||||
|  |                 final Option<Direction> directionToDigTo = currentTile.getRandomAvailableDirection(); | ||||||
|  |                 if (directionToDigTo.isDefined()) { | ||||||
|  |                     final Direction digTo = directionToDigTo.get(); | ||||||
|  |                     final Direction digFrom = digTo.getOpposite(); | ||||||
|  |                     final Position neighborPosition = digTo.translate(currentPosition); | ||||||
|  |                     final Tile neighborTile = Labyrinth.this.getTileAt(neighborPosition); | ||||||
|  |                     if (currentTile.digTo(digTo) && neighborTile.digFrom(digFrom)) { | ||||||
|  |                         // all ok! | ||||||
|  |                         this.positions.push(neighborPosition); | ||||||
|  |                     } else { | ||||||
|  |                         // Hm, didn't work. | ||||||
|  |                         currentTile.undigTo(digTo); | ||||||
|  |                         currentTile.preventDiggingToOrFrom(digTo); | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     this.positions.pop(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								src/main/java/labyrinth/Main.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/main/java/labyrinth/Main.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | ||||||
|  | package labyrinth; | ||||||
|  | 
 | ||||||
|  | import lombok.NonNull; | ||||||
|  | 
 | ||||||
|  | public class Main { | ||||||
|  |     public static void main(@NonNull final String[] args) { | ||||||
|  |         int width = 5; | ||||||
|  |         int height = 8; | ||||||
|  |         final Labyrinth labyrinth = new Labyrinth(width, height); | ||||||
|  |         System.out.println(labyrinth); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								src/main/java/labyrinth/Position.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/main/java/labyrinth/Position.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | ||||||
|  | package labyrinth; | ||||||
|  | 
 | ||||||
|  | import lombok.Value; | ||||||
|  | import lombok.With; | ||||||
|  | 
 | ||||||
|  | @Value | ||||||
|  | @With | ||||||
|  | public class Position { | ||||||
|  |     int x; | ||||||
|  |     int y; | ||||||
|  | } | ||||||
							
								
								
									
										49
									
								
								src/main/java/labyrinth/Tile.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/main/java/labyrinth/Tile.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | package 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 Directions walls = new Directions(); | ||||||
|  |     @Getter | ||||||
|  |     boolean visited = false; | ||||||
|  | 
 | ||||||
|  |     public Tile() { | ||||||
|  |         this.walls.setAll(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void preventDiggingToOrFrom(@NonNull final Direction direction) { | ||||||
|  |         this.walls.harden(direction); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void enableDiggingToOrFrom(@NonNull final Direction direction) { | ||||||
|  |         this.walls.unharden(direction); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public boolean digFrom(@NonNull final Direction direction) { | ||||||
|  |         if (this.visited) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         this.visited = true; | ||||||
|  |         return this.walls.clear(direction); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public boolean digTo(@NonNull final Direction direction) { | ||||||
|  |         return this.walls.clear(direction); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void undigTo(@NonNull final Direction direction) { | ||||||
|  |         this.walls.set(direction); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Option<Direction> getRandomAvailableDirection() { | ||||||
|  |         return Stream.ofAll(this.walls.getSet(true)).shuffle().headOption(); | ||||||
|  |     } | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue