mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-07-07 01:35:16 +08:00
Add class documentation, improve comments in MazeRecursion.java
(#5576)
This commit is contained in:
@ -1,152 +1,125 @@
|
|||||||
package com.thealgorithms.backtracking;
|
package com.thealgorithms.backtracking;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class contains methods to solve a maze using recursive backtracking.
|
||||||
|
* The maze is represented as a 2D array where walls, paths, and visited/dead
|
||||||
|
* ends are marked with different integers.
|
||||||
|
*
|
||||||
|
* The goal is to find a path from a starting position to the target position
|
||||||
|
* (map[6][5]) while navigating through the maze.
|
||||||
|
*/
|
||||||
public final class MazeRecursion {
|
public final class MazeRecursion {
|
||||||
|
|
||||||
private MazeRecursion() {
|
private MazeRecursion() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void mazeRecursion() {
|
/**
|
||||||
// First create a 2 dimensions array to mimic a maze map
|
* This method solves the maze using the "down -> right -> up -> left"
|
||||||
int[][] map = new int[8][7];
|
* movement strategy.
|
||||||
int[][] map2 = new int[8][7];
|
*
|
||||||
|
* @param map The 2D array representing the maze (walls, paths, etc.)
|
||||||
// We use 1 to indicate wall
|
* @return The solved maze with paths marked, or null if no solution exists.
|
||||||
// Set the ceiling and floor to 1
|
*/
|
||||||
for (int i = 0; i < 7; i++) {
|
public static int[][] solveMazeUsingFirstStrategy(int[][] map) {
|
||||||
map[0][i] = 1;
|
if (setWay(map, 1, 1)) {
|
||||||
map[7][i] = 1;
|
return map;
|
||||||
}
|
|
||||||
|
|
||||||
// Then we set the left and right wall to 1
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
map[i][0] = 1;
|
|
||||||
map[i][6] = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we have created a maze with its wall initialized
|
|
||||||
|
|
||||||
// Here we set the obstacle
|
|
||||||
map[3][1] = 1;
|
|
||||||
map[3][2] = 1;
|
|
||||||
|
|
||||||
// Print the current map
|
|
||||||
System.out.println("The condition of the map: ");
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
for (int j = 0; j < 7; j++) {
|
|
||||||
System.out.print(map[i][j] + " ");
|
|
||||||
}
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
|
|
||||||
// clone another map for setWay2 method
|
|
||||||
for (int i = 0; i < map.length; i++) {
|
|
||||||
System.arraycopy(map[i], 0, map2[i], 0, map[i].length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// By using recursive backtracking to let your ball(target) find its way in the
|
|
||||||
// maze
|
|
||||||
// The first parameter is the map
|
|
||||||
// Second parameter is x coordinate of your target
|
|
||||||
// Third parameter is the y coordinate of your target
|
|
||||||
setWay(map, 1, 1);
|
|
||||||
setWay2(map2, 1, 1);
|
|
||||||
|
|
||||||
// Print out the new map1, with the ball footprint
|
|
||||||
System.out.println("After the ball goes through the map1,show the current map1 condition");
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
for (int j = 0; j < 7; j++) {
|
|
||||||
System.out.print(map[i][j] + " ");
|
|
||||||
}
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print out the new map2, with the ball footprint
|
|
||||||
System.out.println("After the ball goes through the map2,show the current map2 condition");
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
for (int j = 0; j < 7; j++) {
|
|
||||||
System.out.print(map2[i][j] + " ");
|
|
||||||
}
|
|
||||||
System.out.println();
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Using recursive path finding to help the ball find its way in the maze
|
* This method solves the maze using the "up -> right -> down -> left"
|
||||||
* Description:
|
* movement strategy.
|
||||||
* 1. map (means the maze)
|
|
||||||
* 2. i, j (means the initial coordinate of the ball in the maze)
|
|
||||||
* 3. if the ball can reach the end of maze, that is position of map[6][5],
|
|
||||||
* means the we have found a path for the ball
|
|
||||||
* 4. Additional Information: 0 in the map[i][j] means the ball has not gone
|
|
||||||
* through this position, 1 means the wall, 2 means the path is feasible, 3
|
|
||||||
* means the ball has gone through the path but this path is dead end
|
|
||||||
* 5. We will need strategy for the ball to pass through the maze for example:
|
|
||||||
* Down -> Right -> Up -> Left, if the path doesn't work, then backtrack
|
|
||||||
*
|
*
|
||||||
* @author OngLipWei
|
* @param map The 2D array representing the maze (walls, paths, etc.)
|
||||||
* @version Jun 23, 2021 11:36:14 AM
|
* @return The solved maze with paths marked, or null if no solution exists.
|
||||||
* @param map The maze
|
|
||||||
* @param i x coordinate of your ball(target)
|
|
||||||
* @param j y coordinate of your ball(target)
|
|
||||||
* @return If we did find a path for the ball,return true,else false
|
|
||||||
*/
|
*/
|
||||||
public static boolean setWay(int[][] map, int i, int j) {
|
public static int[][] solveMazeUsingSecondStrategy(int[][] map) {
|
||||||
if (map[6][5] == 2) { // means the ball find its path, ending condition
|
if (setWay2(map, 1, 1)) {
|
||||||
return true;
|
return map;
|
||||||
}
|
|
||||||
if (map[i][j] == 0) { // if the ball haven't gone through this point
|
|
||||||
// then the ball follows the move strategy : down -> right -> up -> left
|
|
||||||
map[i][j] = 2; // we assume that this path is feasible first, set the current point to 2
|
|
||||||
// first。
|
|
||||||
if (setWay(map, i + 1, j)) { // go down
|
|
||||||
return true;
|
|
||||||
} else if (setWay(map, i, j + 1)) { // go right
|
|
||||||
return true;
|
|
||||||
} else if (setWay(map, i - 1, j)) { // go up
|
|
||||||
return true;
|
|
||||||
} else if (setWay(map, i, j - 1)) { // go left
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
// means that the current point is the dead end, the ball cannot proceed, set
|
|
||||||
// the current point to 3 and return false, the backtracking will start, it will
|
|
||||||
// go to the previous step and check for feasible path again
|
|
||||||
map[i][j] = 3;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else { // if the map[i][j] != 0 , it will probably be 1,2,3, return false because the
|
|
||||||
// ball cannot hit the wall, cannot go to the path that has gone though before,
|
|
||||||
// and cannot head to deadened.
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here is another move strategy for the ball: up->right->down->left
|
/**
|
||||||
public static boolean setWay2(int[][] map, int i, int j) {
|
* Attempts to find a path through the maze using a "down -> right -> up -> left"
|
||||||
if (map[6][5] == 2) { // means the ball find its path, ending condition
|
* movement strategy. The path is marked with '2' for valid paths and '3' for dead ends.
|
||||||
|
*
|
||||||
|
* @param map The 2D array representing the maze (walls, paths, etc.)
|
||||||
|
* @param i The current x-coordinate of the ball (row index)
|
||||||
|
* @param j The current y-coordinate of the ball (column index)
|
||||||
|
* @return True if a path is found to (6,5), otherwise false
|
||||||
|
*/
|
||||||
|
private static boolean setWay(int[][] map, int i, int j) {
|
||||||
|
if (map[6][5] == 2) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (map[i][j] == 0) { // if the ball haven't gone through this point
|
|
||||||
// then the ball follows the move strategy : up->right->down->left
|
// If the current position is unvisited (0), explore it
|
||||||
map[i][j] = 2; // we assume that this path is feasible first, set the current point to 2
|
if (map[i][j] == 0) {
|
||||||
// first。
|
// Mark the current position as '2'
|
||||||
if (setWay2(map, i - 1, j)) { // go up
|
map[i][j] = 2;
|
||||||
|
|
||||||
|
// Move down
|
||||||
|
if (setWay(map, i + 1, j)) {
|
||||||
return true;
|
return true;
|
||||||
} else if (setWay2(map, i, j + 1)) { // go right
|
}
|
||||||
|
// Move right
|
||||||
|
else if (setWay(map, i, j + 1)) {
|
||||||
return true;
|
return true;
|
||||||
} else if (setWay2(map, i + 1, j)) { // go down
|
}
|
||||||
|
// Move up
|
||||||
|
else if (setWay(map, i - 1, j)) {
|
||||||
return true;
|
return true;
|
||||||
} else if (setWay2(map, i, j - 1)) { // go left
|
}
|
||||||
|
// Move left
|
||||||
|
else if (setWay(map, i, j - 1)) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
}
|
||||||
// means that the current point is the dead end, the ball cannot proceed, set
|
|
||||||
// the current point to 3 and return false, the backtracking will start, it will
|
map[i][j] = 3; // Mark as dead end (3) if no direction worked
|
||||||
// go to the previous step and check for feasible path again
|
return false;
|
||||||
map[i][j] = 3;
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to find a path through the maze using an alternative movement
|
||||||
|
* strategy "up -> right -> down -> left".
|
||||||
|
*
|
||||||
|
* @param map The 2D array representing the maze (walls, paths, etc.)
|
||||||
|
* @param i The current x-coordinate of the ball (row index)
|
||||||
|
* @param j The current y-coordinate of the ball (column index)
|
||||||
|
* @return True if a path is found to (6,5), otherwise false
|
||||||
|
*/
|
||||||
|
private static boolean setWay2(int[][] map, int i, int j) {
|
||||||
|
if (map[6][5] == 2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map[i][j] == 0) {
|
||||||
|
map[i][j] = 2;
|
||||||
|
|
||||||
|
// Move up
|
||||||
|
if (setWay2(map, i - 1, j)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Move right
|
||||||
|
else if (setWay2(map, i, j + 1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Move down
|
||||||
|
else if (setWay2(map, i + 1, j)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Move left
|
||||||
|
else if (setWay2(map, i, j - 1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
map[i][j] = 3; // Mark as dead end (3) if no direction worked
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else { // if the map[i][j] != 0 , it will probably be 1,2,3, return false because the
|
|
||||||
// ball cannot hit the wall, cannot go to the path that has gone through before,
|
|
||||||
// and cannot head to deadend.
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -11,41 +11,35 @@ import org.junit.jupiter.api.Test;
|
|||||||
public class MazeRecursionTest {
|
public class MazeRecursionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMaze() {
|
public void testSolveMazeUsingFirstAndSecondStrategy() {
|
||||||
// First create a 2 dimensions array to mimic a maze map
|
|
||||||
int[][] map = new int[8][7];
|
int[][] map = new int[8][7];
|
||||||
int[][] map2 = new int[8][7];
|
int[][] map2 = new int[8][7];
|
||||||
|
|
||||||
// We use 1 to indicate wall
|
// We use 1 to indicate walls
|
||||||
// Set the ceiling and floor to 1
|
// Set the ceiling and floor to 1
|
||||||
for (int i = 0; i < 7; i++) {
|
for (int i = 0; i < 7; i++) {
|
||||||
map[0][i] = 1;
|
map[0][i] = 1;
|
||||||
map[7][i] = 1;
|
map[7][i] = 1;
|
||||||
}
|
}
|
||||||
|
// Set the left and right wall to 1
|
||||||
// Then we set the left and right wall to 1
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
map[i][0] = 1;
|
map[i][0] = 1;
|
||||||
map[i][6] = 1;
|
map[i][6] = 1;
|
||||||
}
|
}
|
||||||
|
// Set obstacles
|
||||||
// Now we have created a maze with its wall initialized
|
|
||||||
|
|
||||||
// Here we set the obstacle
|
|
||||||
map[3][1] = 1;
|
map[3][1] = 1;
|
||||||
map[3][2] = 1;
|
map[3][2] = 1;
|
||||||
|
|
||||||
// clone another map for setWay2 method
|
// Clone the original map for the second pathfinding strategy
|
||||||
for (int i = 0; i < map.length; i++) {
|
for (int i = 0; i < map.length; i++) {
|
||||||
for (int j = 0; j < map[i].length; j++) {
|
System.arraycopy(map[i], 0, map2[i], 0, map[i].length);
|
||||||
map2[i][j] = map[i][j];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MazeRecursion.setWay(map, 1, 1);
|
// Solve the maze using the first strategy
|
||||||
MazeRecursion.setWay2(map2, 1, 1);
|
int[][] solvedMap1 = MazeRecursion.solveMazeUsingFirstStrategy(map);
|
||||||
|
// Solve the maze using the second strategy
|
||||||
int[][] expectedMap = new int[][] {
|
int[][] solvedMap2 = MazeRecursion.solveMazeUsingSecondStrategy(map2);
|
||||||
|
int[][] expectedMap1 = new int[][] {
|
||||||
{1, 1, 1, 1, 1, 1, 1},
|
{1, 1, 1, 1, 1, 1, 1},
|
||||||
{1, 2, 0, 0, 0, 0, 1},
|
{1, 2, 0, 0, 0, 0, 1},
|
||||||
{1, 2, 2, 2, 0, 0, 1},
|
{1, 2, 2, 2, 0, 0, 1},
|
||||||
@ -55,7 +49,6 @@ public class MazeRecursionTest {
|
|||||||
{1, 0, 0, 2, 2, 2, 1},
|
{1, 0, 0, 2, 2, 2, 1},
|
||||||
{1, 1, 1, 1, 1, 1, 1},
|
{1, 1, 1, 1, 1, 1, 1},
|
||||||
};
|
};
|
||||||
|
|
||||||
int[][] expectedMap2 = new int[][] {
|
int[][] expectedMap2 = new int[][] {
|
||||||
{1, 1, 1, 1, 1, 1, 1},
|
{1, 1, 1, 1, 1, 1, 1},
|
||||||
{1, 2, 2, 2, 2, 2, 1},
|
{1, 2, 2, 2, 2, 2, 1},
|
||||||
@ -67,7 +60,8 @@ public class MazeRecursionTest {
|
|||||||
{1, 1, 1, 1, 1, 1, 1},
|
{1, 1, 1, 1, 1, 1, 1},
|
||||||
};
|
};
|
||||||
|
|
||||||
assertArrayEquals(map, expectedMap);
|
// Assert the results
|
||||||
assertArrayEquals(map2, expectedMap2);
|
assertArrayEquals(expectedMap1, solvedMap1);
|
||||||
|
assertArrayEquals(expectedMap2, solvedMap2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user