diff --git a/src/main/java/com/thealgorithms/backtracking/MazeRecursion.java b/src/main/java/com/thealgorithms/backtracking/MazeRecursion.java index 8247172e7ee0..edddec8b7ada 100644 --- a/src/main/java/com/thealgorithms/backtracking/MazeRecursion.java +++ b/src/main/java/com/thealgorithms/backtracking/MazeRecursion.java @@ -4,9 +4,6 @@ * 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 { @@ -14,112 +11,167 @@ 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; } } diff --git a/src/test/java/com/thealgorithms/backtracking/MazeRecursionTest.java b/src/test/java/com/thealgorithms/backtracking/MazeRecursionTest.java index b8e77fb38bad..cacace23bb36 100644 --- a/src/test/java/com/thealgorithms/backtracking/MazeRecursionTest.java +++ b/src/test/java/com/thealgorithms/backtracking/MazeRecursionTest.java @@ -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]; @@ -64,4 +126,5 @@ public void testSolveMazeUsingFirstAndSecondStrategy() { assertArrayEquals(expectedMap1, solvedMap1); assertArrayEquals(expectedMap2, solvedMap2); } + }