diff --git a/src/Search.java b/src/Search.java index cebb278..4687c38 100644 --- a/src/Search.java +++ b/src/Search.java @@ -1,7 +1,13 @@ +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + public class Search { - /** + /** * Finds the location of the nearest reachable cheese from the rat's position. - * Returns a 2-element int array: [row, col] of the closest 'c'. If there are multiple + * Returns a 2-element int array: [row, col] of the closest 'c'. If there are + * multiple * cheeses that are tied for the same shortest distance to reach, return * any one of them. * @@ -24,11 +30,85 @@ public class Search { * * @param maze 2D char array representing the maze * @return int[] location of the closest cheese in row, column format - * @throws EscapedRatException if there is no rat in the maze + * @throws EscapedRatException if there is no rat in the maze * @throws CrowdedMazeException if there is more than one rat in the maze - * @throws HungryRatException if there is no reachable cheese + * @throws HungryRatException if there is no reachable cheese */ public static int[] nearestCheese(char[][] maze) throws EscapedRatException, CrowdedMazeException, HungryRatException { - return null; + int[] start = ratLocation(maze); + + Queue queue = new LinkedList<>(); + + queue.add(start); + + boolean[][] visited = new boolean[maze.length][maze[0].length]; + + while (!queue.isEmpty()) { + int[] current = queue.poll(); + int curR = current[0]; + int curC = current[1]; + + if (maze[curR][curC] == 'c') return current; + + if (visited[curR][curC]) continue; + + visited[curR][curC] = true; + + List nextMoves = possibleMoves(maze, current); + + queue.addAll(nextMoves); + } + + throw new HungryRatException(); + } + + public static List possibleMoves(char[][] maze, int[] current) { + int curR = current[0]; + int curC = current[1]; + + List moves = new ArrayList<>(); + + int[][] directions = new int[][] { + { -1, 0 }, + { 1, 0 }, + { 0, -1 }, + { 0, 1 } + }; + + for (int[] direction : directions) { + int newR = curR + direction[0]; + int newC = curC + direction[1]; + + if (newR >= 0 && newR < maze.length && newC >= 0 && newC < maze[0].length && maze[newR][newC] != 'w') { + moves.add(new int[] { newR, newC }); + } + } + + return moves; + } + + public static int[] ratLocation(char[][] maze) throws CrowdedMazeException, EscapedRatException { + int ratCount = 0; + int[] ratLocation = null; + + for (int r = 0; r < maze.length; r++) { + for (int c = 0; c < maze[0].length; c++) { + if (maze[r][c] == 'R') { + ratLocation = new int[] { r, c }; + ratCount++; + } + } + } + + if (ratCount > 1) + throw new CrowdedMazeException(); + if (ratCount == 0) + throw new EscapedRatException(); + + return ratLocation; + } + + public static void main(String[] args) { + } } \ No newline at end of file diff --git a/src/SearchTest.java b/src/SearchTest.java index 70b01de..130598e 100644 --- a/src/SearchTest.java +++ b/src/SearchTest.java @@ -1,3 +1,81 @@ +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.Test; + public class SearchTest { - -} + @Test + public void testFindsNearestCheese() throws Exception { + char[][] maze = { + {'o', 'o', 'o', 'o', 'c', 'w', 'c', 'o'}, + {'w', 'o', 'o', 'w', 'w', 'c', 'w', 'o'}, + {'o', 'o', 'o', 'o', 'R', 'w', 'o', 'o'}, + {'o', 'o', 'w', 'w', 'w', 'o', 'o', 'o'}, + {'o', 'o', 'o', 'o', 'c', 'o', 'o', 'o'} + }; + + int[] result = Search.nearestCheese(maze); + assertArrayEquals(new int[]{0, 4}, result); + } + + @Test + public void testNoRatThrowsEscapedRatException() { + char[][] maze = { + {'o', 'o', 'o'}, + {'c', 'o', 'c'}, + {'o', 'o', 'o'} + }; + + assertThrows(EscapedRatException.class, () -> Search.nearestCheese(maze)); + } + + @Test + public void testMultipleRatsThrowsCrowdedMazeException() { + char[][] maze = { + {'R', 'o', 'R'}, + {'o', 'c', 'o'}, + {'o', 'o', 'o'} + }; + + assertThrows(CrowdedMazeException.class, () -> Search.nearestCheese(maze)); + } + + @Test + public void testNoReachableCheeseThrowsHungryRatException() { + char[][] maze = { + {'R', 'w', 'c'}, + {'w', 'w', 'w'}, + {'c', 'w', 'c'} + }; + + assertThrows(HungryRatException.class, () -> Search.nearestCheese(maze)); + } + + @Test + public void testRatAlreadyNextToCheese() throws Exception { + char[][] maze = { + {'R', 'c', 'o'}, + {'o', 'o', 'o'}, + {'o', 'o', 'o'} + }; + + int[] result = Search.nearestCheese(maze); + assertArrayEquals(new int[]{0, 1}, result); + } + + @Test + public void testMultipleCheesesSameDistance() throws Exception { + char[][] maze = { + {'o', 'c', 'o'}, + {'R', 'o', 'c'}, + {'o', 'o', 'o'} + }; + + int[] result = Search.nearestCheese(maze); + + // It could either go to (0,1) or (1,2) + boolean isValid = (result[0] == 0 && result[1] == 1) || (result[0] == 1 && result[1] == 2); + assertTrue(isValid, "Result should be either (0,1) or (1,2)"); + } +} \ No newline at end of file