-
-
Notifications
You must be signed in to change notification settings - Fork 709
Add MazeGenerator exercise and related tests #2355
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
c88783b
Add MazeGenerator exercise and related tests
1e1765a
Update exercises/practice/mazy-mice/src/main/java/MazeGenerator.java
0b9ef44
Update exercises/practice/mazy-mice/src/main/java/MazeGenerator.java
01bc92e
Update mazy-mice exercise metadata configuration
6cc8087
Merge remote-tracking branch 'origin/mazy-mice' into mazy-mice
7d7348e
Improve grammar in mazy-mice exercise instructions
ccbbcd7
Update placeholder in hints.md
26ecea4
Merge branch 'main' into mazy-mice
11fe1d0
Refactor test method name for clarity
2ef9ca6
Update test method name to reflect logical assertion
4071a73
Add 'mazy-mice' to settings.gradle
d2cfeed
Merge branch 'main' into mazy-mice
f33e267
Add new "mazy-mice" exercise to config.json
88106d6
Refactor maze generation and test methods for readability
4003c58
Merge branch 'main' into mazy-mice
1bd5acf
Add descriptions to auto-generated test cases
d8585eb
Change access modifier for test methods in MazeGeneratorTest
4416e9e
Add additional hints for exercise in hints.md
a25e6b3
Add a new test for seeded maze generator
473e735
Add tests for maze size validation
ca072f3
Add maze dimensions validation to the MazeGenerator class
5b9e2c5
Revise header formatting in instructions.md
54e1580
Remove redundant .meta/tests.toml file
07777d7
delete version file
7be25c0
Remove Dimensions.java as its functionality is redundant
44367ac
Update exercises/practice/mazy-mice/src/test/java/MazeGeneratorTest.java
0d446b8
Update exercises/practice/mazy-mice/src/test/java/MazeGeneratorTest.java
004b60d
Update config.json to include new practices
238f971
Merge branch 'main' into mazy-mice
2f77e5e
Add new game "Mazy Mice" in config.json
f1ca0a9
Merge remote-tracking branch 'origin/mazy-mice' into mazy-mice
4afc2f1
Add Dimensions.java to Mazy Mice example files
0db470a
Remove redundant mazeNotNull test in MazeGeneratorTest class
833a94d
Refactor MazeGeneratorTest for better test organization
69c39aa
Add status field to config.json
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# placeholder | ||
rabestro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Maze generation | ||
|
||
You can use any algorithm to generate a perfect maze. The [recursive backtracker][recursive-backtracker] is a good choice. | ||
|
||
## Box drawing characters | ||
|
||
| Character | Name | Unicode | | ||
|:---------:|:--------------------------------------|:--------| | ||
| ┌ | box drawings light down and right | U+250C | | ||
| ─ | box drawings light horizontal | U+2500 | | ||
| ┬ | box drawings light down and horizontal| U+252C | | ||
| ┐ | box drawings light down and left | U+2510 | | ||
| │ | box drawings light vertical | U+2502 | | ||
| └ | box drawings light up and right | U+2514 | | ||
| ┴ | box drawings light up and horizontal | U+2534 | | ||
| ┘ | box drawings light up and left | U+2518 | | ||
| ├ | box drawings light vertical and right | U+2520 | | ||
| ⇨ | rightwards white arrow | U+21E8 | | ||
|
||
|
||
[recursive-backtracker]: https://en.wikipedia.org/wiki/Maze_generation_algorithm |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
## Instructions | ||
rabestro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Your task is to generate the perfect mazes for Mickey and Minerva — those with only one solution and no isolated sections. | ||
Here's what you need to know: | ||
|
||
- The maze has a rectangular shape with an opening at the start and end. | ||
- The maze has rooms and passages, which intersect at right angles. | ||
- The program should accept two parameters: rows and columns. The maze should be between 5 and 100 cells in size. | ||
rabestro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- A maze which is `x` columns wide and `y` rows high should be `2x + 1` characters wide and `2y + 1` characters high. | ||
- If no seed is provided, generate a random maze. If the same seed is provided multiple times, the resulting maze should be the same each time. | ||
- Use [box-drawing][Box-drawing] characters to draw walls, and an arrow symbol (⇨) for the entrance on the left and exit on the right. | ||
|
||
It's time to create some perfect mazes for these adventurous mice! | ||
|
||
### Examples | ||
|
||
1. The small square maze 5x5 cells (or 11x11 characters) | ||
rabestro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
```text | ||
┌───────┬─┐ | ||
│ │ │ | ||
│ ┌─┬── │ │ | ||
│ │ │ │ ⇨ | ||
│ │ │ ──┤ │ | ||
⇨ │ │ │ │ | ||
┌─┤ └── │ │ | ||
│ │ │ │ | ||
│ │ ────┘ │ | ||
│ │ | ||
└─────────┘ | ||
``` | ||
2. The rectangular maze 6x18 cells | ||
rabestro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
```text | ||
┌───────────┬─────────┬───────────┬─┐ | ||
│ │ │ │ │ | ||
│ ┌───────┐ │ ┌─┐ ──┐ └───┐ ┌───┐ │ │ | ||
│ │ │ │ │ │ │ │ │ │ ⇨ | ||
│ └─┐ ┌─┐ │ │ │ ├── ├───┐ │ │ ──┼── │ | ||
│ │ │ │ │ │ │ │ │ │ │ │ | ||
└── │ │ ├───┴───┤ ┌─┘ ┌─┘ │ ├── │ ──┤ | ||
⇨ │ │ │ │ │ │ │ │ │ | ||
┌─┬─┴─┐ └─┐ ┌─┐ │ └─┐ │ ┌─┘ │ ──┴─┐ │ | ||
│ │ │ │ │ │ │ │ │ │ │ │ | ||
│ │ │ └── │ │ │ └── │ ──┘ ┌─┘ ──┐ │ │ | ||
│ │ │ │ │ │ │ | ||
└───┴───────┴───────┴─────┴─────┴───┘ | ||
``` | ||
|
||
[Box-drawing]: https://en.wikipedia.org/wiki/Box-drawing_character |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Introduction | ||
|
||
Meet Mickey and Minerva, two clever mice who love to navigate their way through a maze to find cheese. They enjoy a good challenge, but with only their tiny mouse brains, they prefer if there is only one correct path to the cheese. |
rabestro marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"authors": [ | ||
"rabestro" | ||
], | ||
"files": { | ||
"solution": [ | ||
"src/main/java/MazeGenerator.java" | ||
], | ||
"test": [ | ||
"src/test/java/MazeGeneratorTest.java" | ||
], | ||
"example": [ | ||
".meta/src/reference/java/MazeGenerator.java" | ||
] | ||
}, | ||
"blurb": "Meet Mickey and Minerva, two clever mice who love to navigate their way through a maze to find cheese. They enjoy a good challenge, but with only their tiny mouse brains, they prefer if there is only one correct path to the cheese.", | ||
"source": "Inspired by the 'Maze Generator' created by Jan Boström at Alance AB.", | ||
"source_url": "https://mazegenerator.net/" | ||
} |
25 changes: 25 additions & 0 deletions
25
exercises/practice/mazy-mice/.meta/src/reference/java/Dimensions.java
rabestro marked this conversation as resolved.
Show resolved
Hide resolved
rabestro marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/** | ||
* Represents the dimensions of a maze. | ||
* <p> | ||
* Dimensions of a grid can be represented in cells or characters. | ||
* Rows and columns are used for cells, while width and height are used for characters. | ||
*/ | ||
public record Dimensions(int rows, int columns) { | ||
/** | ||
* Returns the width of the maze in characters. | ||
* | ||
* @return the width of the maze | ||
*/ | ||
int width() { | ||
return 2 * columns + 1; | ||
} | ||
|
||
/** | ||
* Returns the height of the maze in characters. | ||
* | ||
* @return the height of the maze | ||
*/ | ||
int height() { | ||
return 2 * rows + 1; | ||
} | ||
} |
177 changes: 177 additions & 0 deletions
177
exercises/practice/mazy-mice/.meta/src/reference/java/MazeGenerator.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
import java.util.Random; | ||
import java.util.random.RandomGenerator; | ||
import java.util.BitSet; | ||
import java.util.EnumSet; | ||
import java.util.Set; | ||
|
||
import static java.util.stream.IntStream.range; | ||
|
||
public class MazeGenerator { | ||
|
||
public char[][] generatePerfectMaze(Dimensions dimensions) { | ||
return new Grid(dimensions, RandomGenerator.getDefault()) | ||
.generateMaze() | ||
.placeDoors() | ||
.print(); | ||
} | ||
|
||
public char[][] generatePerfectMaze(Dimensions dimensions, int seed) { | ||
return new Grid(dimensions, new Random(seed)) | ||
.generateMaze() | ||
.placeDoors() | ||
.print(); | ||
} | ||
} | ||
|
||
enum Direction { | ||
NORTH(0, 1), | ||
EAST(1, 0), | ||
SOUTH(0, -1), | ||
WEST(-1, 0); | ||
private final int dx; | ||
private final int dy; | ||
|
||
Direction(int dx, int dy) { | ||
this.dx = dx; | ||
this.dy = dy; | ||
} | ||
|
||
public int dx() { | ||
return dx; | ||
} | ||
|
||
public int dy() { | ||
return dy; | ||
} | ||
} | ||
|
||
final class Grid { | ||
private final Dimensions dimensions; | ||
private final BitSet grid; | ||
private final RandomGenerator randomGenerator; | ||
|
||
Grid(Dimensions dimensions, RandomGenerator randomGenerator) { | ||
this.dimensions = dimensions; | ||
this.grid = new BitSet(dimensions.width() * dimensions.height()); | ||
this.randomGenerator = randomGenerator; | ||
} | ||
|
||
Grid generateMaze() { | ||
generate(new Cell(1, 1)); | ||
return this; | ||
} | ||
|
||
private int random(int bound) { | ||
return randomGenerator.nextInt(bound); | ||
} | ||
|
||
private Direction pickRandomDirection(Set<Direction> directions) { | ||
int size = directions.size(); | ||
int itemIndex = random(size); | ||
var direction = directions.toArray(new Direction[size])[itemIndex]; | ||
directions.remove(direction); | ||
return direction; | ||
} | ||
|
||
Grid placeDoors() { | ||
new Cell(1 + 2 * random(dimensions.rows()), 0).erase(); | ||
new Cell(1 + 2 * random(dimensions.rows()), dimensions.width() - 1).erase(); | ||
return this; | ||
} | ||
|
||
private void generate(Cell cell) { | ||
cell.erase(); | ||
|
||
var directions = EnumSet.allOf(Direction.class); | ||
do { | ||
var direction = pickRandomDirection(directions); | ||
var wall = cell.move(direction); | ||
var next = wall.move(direction); | ||
if (next.isValid() && next.isNotEmpty()) { | ||
wall.erase(); | ||
generate(next); | ||
} | ||
} while (!directions.isEmpty()); | ||
} | ||
|
||
char[][] print() { | ||
return range(0, dimensions.height()) | ||
.mapToObj(this::line) | ||
.toArray(char[][]::new); | ||
} | ||
|
||
private char[] line(int x) { | ||
return range(0, dimensions.width()) | ||
.map(y -> new Cell(x, y).symbol()) | ||
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) | ||
.toString() | ||
.toCharArray(); | ||
} | ||
|
||
private final class Cell { | ||
final int x; | ||
final int y; | ||
|
||
private Cell(int x, int y) { | ||
this.x = x; | ||
this.y = y; | ||
} | ||
|
||
boolean isValid() { | ||
return x > 0 && x < dimensions.height() && y > 0 && y < dimensions.width(); | ||
} | ||
|
||
void erase() { | ||
grid.set(index()); | ||
} | ||
|
||
boolean isNotEmpty() { | ||
return !isEmpty(); | ||
} | ||
|
||
boolean isEmpty() { | ||
return grid.get(index()); | ||
} | ||
|
||
int index() { | ||
return x * dimensions.width() + y; | ||
} | ||
|
||
boolean isDoor() { | ||
return isEmpty() && (y == 0 || y == dimensions.width() - 1); | ||
} | ||
|
||
Cell move(Direction direction) { | ||
return new Cell(x + direction.dx(), y + direction.dy()); | ||
} | ||
|
||
char symbol() { | ||
if (isDoor()) { | ||
return '⇨'; | ||
} | ||
if (isEmpty()) { | ||
return ' '; | ||
} | ||
var n = x > 0 && new Cell(x - 1, y).isNotEmpty() ? 1 : 0; | ||
var e = y < dimensions.width() - 1 && new Cell(x, y + 1).isNotEmpty() ? 1 : 0; | ||
var s = x < dimensions.height() - 1 && new Cell(x + 1, y).isNotEmpty() ? 1 : 0; | ||
var w = y > 0 && new Cell(x, y - 1).isNotEmpty() ? 1 : 0; | ||
var i = n + 2 * e + 4 * s + 8 * w; | ||
return switch (i) { | ||
case 0 -> ' '; | ||
case 1, 5, 4 -> '│'; | ||
case 2, 8, 10 -> '─'; | ||
case 3 -> '└'; | ||
case 6 -> '┌'; | ||
case 7 -> '├'; | ||
case 9 -> '┘'; | ||
case 11 -> '┴'; | ||
case 12 -> '┐'; | ||
case 13 -> '┤'; | ||
case 14 -> '┬'; | ||
case 15 -> '┼'; | ||
default -> throw new IllegalStateException("Unexpected value: " + i); | ||
}; | ||
} | ||
} | ||
} |
rabestro marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
1.0.0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
apply plugin: "java" | ||
apply plugin: "eclipse" | ||
apply plugin: "idea" | ||
|
||
// set default encoding to UTF-8 | ||
compileJava.options.encoding = "UTF-8" | ||
compileTestJava.options.encoding = "UTF-8" | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
dependencies { | ||
testImplementation "junit:junit:4.13" | ||
testImplementation "org.assertj:assertj-core:3.15.0" | ||
} | ||
|
||
test { | ||
testLogging { | ||
exceptionFormat = 'full' | ||
showStandardStreams = true | ||
events = ["passed", "failed", "skipped"] | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
exercises/practice/mazy-mice/src/main/java/Dimensions.java
rabestro marked this conversation as resolved.
Show resolved
Hide resolved
rabestro marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/** | ||
* Represents the dimensions of a maze. | ||
* <p> | ||
* Dimensions of a grid can be represented in cells or characters. | ||
* Rows and columns are used for cells, while width and height are used for characters. | ||
*/ | ||
public record Dimensions(int rows, int columns) { | ||
/** | ||
* Returns the width of the maze in characters. | ||
* | ||
* @return the width of the maze | ||
*/ | ||
int width() { | ||
return 2 * columns + 1; | ||
} | ||
|
||
/** | ||
* Returns the height of the maze in characters. | ||
* | ||
* @return the height of the maze | ||
*/ | ||
int height() { | ||
return 2 * rows + 1; | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
exercises/practice/mazy-mice/src/main/java/MazeGenerator.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
public class MazeGenerator { | ||
|
||
public char[][] generatePerfectMaze(Dimensions dimensions) { | ||
rabestro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return null; | ||
rabestro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
public char[][] generatePerfectMaze(Dimensions dimensions, int seed) { | ||
return null; | ||
rabestro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.