Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 129 additions & 77 deletions src/main/java/com/thealgorithms/backtracking/MazeRecursion.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,122 +4,174 @@
* 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 {

private MazeRecursion() {
}

/**
* This method solves the maze using the "down -> right -> up -> left"
* movement strategy.
* Solves the maze using the down -> right -> up -> left strategy.
*
* @param map The 2D array representing the maze (walls, paths, etc.)
* @return The solved maze with paths marked, or null if no solution exists.
* @param map The 2D array representing the maze.
* @return The solved maze, or null if no solution exists.
*/
public static int[][] solveMazeUsingFirstStrategy(int[][] map) {
if (setWay(map, 1, 1)) {
return map;
if (map == null || map.length == 0 || map[0] == null || map[0].length == 0) {
return null;
}
return null;

return solveMazeUsingFirstStrategy(
map, 1, 1, map.length - 2, map[0].length - 2
);
}

/**
* This method solves the maze using the "up -> right -> down -> left"
* movement strategy.
* Solves the maze using the up -> right -> down -> left strategy.
*
* @param map The 2D array representing the maze (walls, paths, etc.)
* @return The solved maze with paths marked, or null if no solution exists.
* @param map The 2D array representing the maze.
* @return The solved maze, or null if no solution exists.
*/
public static int[][] solveMazeUsingSecondStrategy(int[][] map) {
if (setWay2(map, 1, 1)) {
return map;
if (map == null || map.length == 0 || map[0] == null || map[0].length == 0) {
return null;
}
return null;

return solveMazeUsingSecondStrategy(
map, 1, 1, map.length - 2, map[0].length - 2
);
}

/**
* Attempts to find a path through the maze using a "down -> right -> up -> left"
* movement strategy. The path is marked with '2' for valid paths and '3' for dead ends.
* Solves the maze using the first strategy with custom coordinates.
*
* @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
* @param map The maze.
* @param startRow Starting row.
* @param startCol Starting column.
* @param destinationRow Destination row.
* @param destinationCol Destination column.
* @return The solved maze, or null if no solution exists.
*/
private static boolean setWay(int[][] map, int i, int j) {
if (map[6][5] == 2) {
return true;
public static int[][] solveMazeUsingFirstStrategy(
int[][] map,
int startRow,
int startCol,
int destinationRow,
int destinationCol) {

int[][] directions = {
{1, 0},
{0, 1},
{-1, 0},
{0, -1}
};

return solveMaze(map, startRow, startCol,
destinationRow, destinationCol, directions);
}

/**
* Solves the maze using the second strategy with custom coordinates.
*
* @param map The maze.
* @param startRow Starting row.
* @param startCol Starting column.
* @param destinationRow Destination row.
* @param destinationCol Destination column.
* @return The solved maze, or null if no solution exists.
*/
public static int[][] solveMazeUsingSecondStrategy(
int[][] map,
int startRow,
int startCol,
int destinationRow,
int destinationCol) {

int[][] directions = {
{-1, 0},
{0, 1},
{1, 0},
{0, -1}
};

return solveMaze(map, startRow, startCol,
destinationRow, destinationCol, directions);
}

/**
* Validates the maze and starts the recursive solving process.
*/
private static int[][] solveMaze(
int[][] map,
int startRow,
int startCol,
int destinationRow,
int destinationCol,
int[][] directions) {

if (map == null || map.length == 0 || map[0] == null || map[0].length == 0) {
return null;
}

// If the current position is unvisited (0), explore it
if (map[i][j] == 0) {
// Mark the current position as '2'
map[i][j] = 2;
int columns = map[0].length;

// Move down
if (setWay(map, i + 1, j)) {
return true;
}
// Move right
else if (setWay(map, i, j + 1)) {
return true;
}
// Move up
else if (setWay(map, i - 1, j)) {
return true;
}
// Move left
else if (setWay(map, i, j - 1)) {
return true;
for (int[] row : map) {
if (row == null || row.length != columns) {
return null;
}
}

map[i][j] = 3; // Mark as dead end (3) if no direction worked
return false;
if (startRow < 0 || startRow >= map.length
|| startCol < 0 || startCol >= columns
|| destinationRow < 0 || destinationRow >= map.length
|| destinationCol < 0 || destinationCol >= columns) {
return null;
}
return false;

if (solveMazeRecursive(map, startRow, startCol,
destinationRow, destinationCol, directions)) {
return map;
}

return null;
}

/**
* 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
* Recursively explores the maze using the provided movement directions.
*/
private static boolean setWay2(int[][] map, int i, int j) {
if (map[6][5] == 2) {
private static boolean solveMazeRecursive(
int[][] map,
int row,
int col,
int destinationRow,
int destinationCol,
int[][] directions) {

if (map[row][col] != 0) {
return false;
}

map[row][col] = 2;

if (row == destinationRow && col == destinationCol) {
return true;
}

if (map[i][j] == 0) {
map[i][j] = 2;
for (int[] direction : directions) {
int nextRow = row + direction[0];
int nextCol = col + direction[1];

// 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;
}
if (nextRow >= 0 && nextRow < map.length
&& nextCol >= 0 && nextCol < map[0].length) {

map[i][j] = 3; // Mark as dead end (3) if no direction worked
return false;
if (solveMazeRecursive(map, nextRow, nextCol,
destinationRow, destinationCol, directions)) {
return true;
}
}
}

map[row][col] = 3;
return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,77 @@
package com.thealgorithms.backtracking;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

import org.junit.jupiter.api.Test;

/**
* @author onglipwei
* @create 2022-08-03 5:17 AM
*/

public class MazeRecursionTest {


@Test
public void testNullMaze() {
int[][] result = MazeRecursion.solveMazeUsingFirstStrategy(null);

assertNull(result);
}

@Test
public void testSmallMazeDoesNotThrowException() {
int[][] map = {
{0, 0},
{0, 0}
};

int[][] result = MazeRecursion.solveMazeUsingFirstStrategy(map);

assertArrayEquals(map, result);
}

@Test
public void testCustomStartAndDestination() {
int[][] map = {
{1, 1, 1, 1, 1},
{1, 0, 0, 0, 1},
{1, 0, 0, 0, 1},
{1, 1, 1, 1, 1}
};

int[][] result = MazeRecursion.solveMazeUsingFirstStrategy(map, 1, 1, 2, 3);

if (result == null) {
throw new AssertionError("Expected a solved maze, but got null");
}

assertEquals(2, result[1][1]);
assertEquals(2, result[2][3]);
}

@Test
public void testInvalidCoordinates() {
int[][] map = {
{1, 1, 1},
{1, 0, 1},
{1, 1, 1}
};

int[][] result = MazeRecursion.solveMazeUsingFirstStrategy(
map, 10, 10, 1, 1
);

assertNull(result);
}

@Test
public void testEmptyMaze() {
int[][] map = new int[0][0];

int[][] result = MazeRecursion.solveMazeUsingFirstStrategy(map);

assertNull(result);
}

@Test
public void testSolveMazeUsingFirstAndSecondStrategy() {
int[][] map = new int[8][7];
Expand Down Expand Up @@ -64,4 +126,5 @@ public void testSolveMazeUsingFirstAndSecondStrategy() {
assertArrayEquals(expectedMap1, solvedMap1);
assertArrayEquals(expectedMap2, solvedMap2);
}

}
Loading