From b70a50d611d815c5db9e42579a7d538e6277c59a Mon Sep 17 00:00:00 2001 From: HumaGitGud Date: Thu, 3 Apr 2025 16:00:13 -0700 Subject: [PATCH 1/3] Completed project --- src/SalamanderSearch.java | 72 +++++++ src/SalamanderSearchTest.java | 394 +++++++++++++++++----------------- 2 files changed, 269 insertions(+), 197 deletions(-) diff --git a/src/SalamanderSearch.java b/src/SalamanderSearch.java index 688cda7..8bbcf73 100644 --- a/src/SalamanderSearch.java +++ b/src/SalamanderSearch.java @@ -43,6 +43,78 @@ public static void main(String[] args) { * @return whether the salamander can reach the food */ public static boolean canReach(char[][] enclosure) { + int[] start = salamanderLocation(enclosure); + boolean[][] visited = new boolean[enclosure.length][enclosure[0].length]; + return canReach(enclosure, start, visited); + } + + public static boolean canReach(char[][] enclosure, int[] current, boolean[][] visited) { + // base cases + int currRow = current[0]; + int currCol = current[1]; + + if (visited[currRow][currCol]) return false; + if (enclosure[currRow][currCol] == 'f') return true; + + // avoid cycles + visited[currRow][currCol] = true; + + // recurse and logic + List moves = possibleMoves(enclosure, current); + for(int[] move : moves) { + if(canReach(enclosure, move, visited)) return true; + } + return false; } + + public static List possibleMoves(char[][] enclosure, int[] current) { + int currRow = current[0]; + int currCol = current[1]; + + List moves = new ArrayList<>(); + + // UP + int newRow = currRow - 1; + int newCol = currCol; + if (newRow >= 0 && enclosure[newRow][newCol] != 'W') { + moves.add(new int[]{newRow, newCol}); + } + + // DOWN + newRow = currRow + 1; + newCol = currCol; + if (newRow < enclosure.length && enclosure[newRow][newCol] != 'W') { + moves.add(new int[]{newRow, newCol}); + } + + // LEFT + newRow = currRow; + newCol = currCol - 1; + if (newCol >= 0 && enclosure[newRow][newCol] != 'W') { + moves.add(new int[]{newRow, newCol}); + + } + + // RIGHT + newRow = currRow; + newCol = currCol + 1; + if (newCol < enclosure[0].length && enclosure[newRow][newCol] != 'W') { + moves.add(new int[]{newRow, newCol}); + } + + return moves; + } + + public static int[] salamanderLocation(char[][] enclosure) { + for(int r = 0; r < enclosure.length; r++) { + for(int c = 0; c < enclosure[0].length; c++) { + if (enclosure[r][c] == 's') { + return new int[]{r,c}; + } + } + } + + throw new IllegalArgumentException("No salamander present"); + } } diff --git a/src/SalamanderSearchTest.java b/src/SalamanderSearchTest.java index ddad9de..a8b5af2 100644 --- a/src/SalamanderSearchTest.java +++ b/src/SalamanderSearchTest.java @@ -34,203 +34,203 @@ public void canReach_NoPath() { assertFalse(actual); } - // @Test - // public void testSalamanderLocation_centerOfGrid() { - // char[][] enclosure = { - // {'.', '.', '.'}, - // {'.', 's', '.'}, - // {'.', '.', '.'} - // }; - // int[] expected = {1, 1}; - // assertArrayEquals(expected, SalamanderSearch.salamanderLocation(enclosure)); - // } - - // @Test - // public void testSalamanderLocation_topLeftCorner() { - // char[][] enclosure = { - // {'s', '.', '.'}, - // {'.', '.', '.'}, - // {'.', '.', '.'} - // }; - // int[] expected = {0, 0}; - // assertArrayEquals(expected, SalamanderSearch.salamanderLocation(enclosure)); - // } - - // @Test - // public void testSalamanderLocation_notFound_throwsException() { - // char[][] enclosure = { - // {'.', '.', '.'}, - // {'.', '.', '.'}, - // {'.', '.', '.'} - // }; - // Exception exception = assertThrows(IllegalArgumentException.class, () -> { - // SalamanderSearch.salamanderLocation(enclosure); - // }); - // assertEquals("No salamander present", exception.getMessage()); - // } - - - // @Test - // public void testSalamanderLocation_at_1_2() { - // char[][] enclosure = { - // {'.', '.', '.'}, - // {'.', '.', 's'}, - // {'.', '.', '.'} - // }; - // int[] expected = {1, 2}; - // assertArrayEquals(expected, SalamanderSearch.salamanderLocation(enclosure)); - // } - - // @Test - // public void testPossibleMoves_allDirectionsOpen() { - // char[][] enclosure = { - // {'.', '.', '.'}, - // {'.', 's', '.'}, - // {'.', '.', '.'} - // }; - // int[] location = {1, 1}; - // List moves = SalamanderSearch.possibleMoves(enclosure, location); - // Set moveSet = toSet(moves); - - // assertEquals(4, moves.size()); - // assertTrue(moveSet.contains("0,1")); // up - // assertTrue(moveSet.contains("2,1")); // down - // assertTrue(moveSet.contains("1,0")); // left - // assertTrue(moveSet.contains("1,2")); // right - // } - - // @Test - // public void testPossibleMoves_allDirectionsBlockedByWalls() { - // char[][] enclosure = { - // {'W', 'W', 'W'}, - // {'W', 's', 'W'}, - // {'W', 'W', 'W'} - // }; - // int[] location = {1, 1}; - // List moves = SalamanderSearch.possibleMoves(enclosure, location); - // assertTrue(moves.isEmpty()); - // } - - // @Test - // public void testPossibleMoves_partialEdge() { - // char[][] enclosure = { - // {'s', '.', '.'} - // }; - // int[] location = {0, 0}; - // List moves = SalamanderSearch.possibleMoves(enclosure, location); - // Set moveSet = toSet(moves); - - // assertEquals(1, moves.size()); - // assertTrue(moveSet.contains("0,1")); // only right - // } - - // @Test - // public void testPossibleMoves_oneOpen_twoWalls_oneEdge() { - // // Salamander at (1, 1) - // // Up is W, down is wall, left is edge (0), right is open - // char[][] enclosure = { - // {'W', 'W', 'W'}, - // {'.', 's', '.'}, - // {'W', 'W', 'W'} - // }; - // int[] location = {1, 1}; - // List moves = SalamanderSearch.possibleMoves(enclosure, location); - // Set moveSet = toSet(moves); - - // assertEquals(2, moves.size()); - // assertTrue(moveSet.contains("1,2")); - // assertTrue(moveSet.contains("1,0")); - // } - - // @Test - // public void testPossibleMoves_blockedAboveByWall() { - // char[][] enclosure = { - // {'.', 'W', '.'}, - // {'.', 's', '.'}, - // {'.', '.', '.'} - // }; - // int[] location = {1, 1}; - // List moves = SalamanderSearch.possibleMoves(enclosure, location); - // Set moveSet = toSet(moves); - - // assertFalse(moveSet.contains("0,1")); // up blocked - // assertTrue(moveSet.contains("2,1")); // down open - // assertTrue(moveSet.contains("1,0")); // left open - // assertTrue(moveSet.contains("1,2")); // right open - // } - - // @Test - // public void testPossibleMoves_blockedBelowByWall() { - // char[][] enclosure = { - // {'.', '.', '.'}, - // {'.', 's', '.'}, - // {'.', 'W', '.'} - // }; - // int[] location = {1, 1}; - // Set moveSet = toSet(SalamanderSearch.possibleMoves(enclosure, location)); - - // assertTrue(moveSet.contains("0,1")); // up open - // assertFalse(moveSet.contains("2,1")); // down blocked - // assertTrue(moveSet.contains("1,0")); // left open - // assertTrue(moveSet.contains("1,2")); // right open - // } - - // @Test - // public void testPossibleMoves_blockedLeftByWall() { - // char[][] enclosure = { - // {'.', '.', '.'}, - // {'W', 's', '.'}, - // {'.', '.', '.'} - // }; - // int[] location = {1, 1}; - // Set moveSet = toSet(SalamanderSearch.possibleMoves(enclosure, location)); - - // assertTrue(moveSet.contains("0,1")); // up open - // assertTrue(moveSet.contains("2,1")); // down open - // assertFalse(moveSet.contains("1,0")); // left blocked - // assertTrue(moveSet.contains("1,2")); // right open - // } - - // @Test - // public void testPossibleMoves_blockedRightByWall() { - // char[][] enclosure = { - // {'.', '.', '.'}, - // {'.', 's', 'W'}, - // {'.', '.', '.'} - // }; - // int[] location = {1, 1}; - // Set moveSet = toSet(SalamanderSearch.possibleMoves(enclosure, location)); - - // assertTrue(moveSet.contains("0,1")); // up open - // assertTrue(moveSet.contains("2,1")); // down open - // assertTrue(moveSet.contains("1,0")); // left open - // assertFalse(moveSet.contains("1,2")); // right blocked - // } - - // @Test - // public void testPossibleMoves_topLeftCornerWithOneOpen() { - // char[][] enclosure = { - // {'s', 'W'}, - // {'W', '.'} - // }; - // int[] location = {0, 0}; - // List moves = SalamanderSearch.possibleMoves(enclosure, location); - - // assertTrue(moves.isEmpty()); // surrounded by walls/edges - // } - - // @Test - // public void testPossibleMoves_rightEdgeWithOpenLeft() { - // char[][] enclosure = { - // {'.', '.', 's'} - // }; - // int[] location = {0, 2}; - // List moves = SalamanderSearch.possibleMoves(enclosure, location); - // Set moveSet = toSet(moves); - - // assertEquals(1, moves.size()); - // assertTrue(moveSet.contains("0,1")); - // } + @Test + public void testSalamanderLocation_centerOfGrid() { + char[][] enclosure = { + {'.', '.', '.'}, + {'.', 's', '.'}, + {'.', '.', '.'} + }; + int[] expected = {1, 1}; + assertArrayEquals(expected, SalamanderSearch.salamanderLocation(enclosure)); + } + + @Test + public void testSalamanderLocation_topLeftCorner() { + char[][] enclosure = { + {'s', '.', '.'}, + {'.', '.', '.'}, + {'.', '.', '.'} + }; + int[] expected = {0, 0}; + assertArrayEquals(expected, SalamanderSearch.salamanderLocation(enclosure)); + } + + @Test + public void testSalamanderLocation_notFound_throwsException() { + char[][] enclosure = { + {'.', '.', '.'}, + {'.', '.', '.'}, + {'.', '.', '.'} + }; + Exception exception = assertThrows(IllegalArgumentException.class, () -> { + SalamanderSearch.salamanderLocation(enclosure); + }); + assertEquals("No salamander present", exception.getMessage()); + } + + + @Test + public void testSalamanderLocation_at_1_2() { + char[][] enclosure = { + {'.', '.', '.'}, + {'.', '.', 's'}, + {'.', '.', '.'} + }; + int[] expected = {1, 2}; + assertArrayEquals(expected, SalamanderSearch.salamanderLocation(enclosure)); + } + + @Test + public void testPossibleMoves_allDirectionsOpen() { + char[][] enclosure = { + {'.', '.', '.'}, + {'.', 's', '.'}, + {'.', '.', '.'} + }; + int[] location = {1, 1}; + List moves = SalamanderSearch.possibleMoves(enclosure, location); + Set moveSet = toSet(moves); + + assertEquals(4, moves.size()); + assertTrue(moveSet.contains("0,1")); // up + assertTrue(moveSet.contains("2,1")); // down + assertTrue(moveSet.contains("1,0")); // left + assertTrue(moveSet.contains("1,2")); // right + } + + @Test + public void testPossibleMoves_allDirectionsBlockedByWalls() { + char[][] enclosure = { + {'W', 'W', 'W'}, + {'W', 's', 'W'}, + {'W', 'W', 'W'} + }; + int[] location = {1, 1}; + List moves = SalamanderSearch.possibleMoves(enclosure, location); + assertTrue(moves.isEmpty()); + } + + @Test + public void testPossibleMoves_partialEdge() { + char[][] enclosure = { + {'s', '.', '.'} + }; + int[] location = {0, 0}; + List moves = SalamanderSearch.possibleMoves(enclosure, location); + Set moveSet = toSet(moves); + + assertEquals(1, moves.size()); + assertTrue(moveSet.contains("0,1")); // only right + } + + @Test + public void testPossibleMoves_oneOpen_twoWalls_oneEdge() { + // Salamander at (1, 1) + // Up is W, down is wall, left is edge (0), right is open + char[][] enclosure = { + {'W', 'W', 'W'}, + {'.', 's', '.'}, + {'W', 'W', 'W'} + }; + int[] location = {1, 1}; + List moves = SalamanderSearch.possibleMoves(enclosure, location); + Set moveSet = toSet(moves); + + assertEquals(2, moves.size()); + assertTrue(moveSet.contains("1,2")); + assertTrue(moveSet.contains("1,0")); + } + + @Test + public void testPossibleMoves_blockedAboveByWall() { + char[][] enclosure = { + {'.', 'W', '.'}, + {'.', 's', '.'}, + {'.', '.', '.'} + }; + int[] location = {1, 1}; + List moves = SalamanderSearch.possibleMoves(enclosure, location); + Set moveSet = toSet(moves); + + assertFalse(moveSet.contains("0,1")); // up blocked + assertTrue(moveSet.contains("2,1")); // down open + assertTrue(moveSet.contains("1,0")); // left open + assertTrue(moveSet.contains("1,2")); // right open + } + + @Test + public void testPossibleMoves_blockedBelowByWall() { + char[][] enclosure = { + {'.', '.', '.'}, + {'.', 's', '.'}, + {'.', 'W', '.'} + }; + int[] location = {1, 1}; + Set moveSet = toSet(SalamanderSearch.possibleMoves(enclosure, location)); + + assertTrue(moveSet.contains("0,1")); // up open + assertFalse(moveSet.contains("2,1")); // down blocked + assertTrue(moveSet.contains("1,0")); // left open + assertTrue(moveSet.contains("1,2")); // right open + } + + @Test + public void testPossibleMoves_blockedLeftByWall() { + char[][] enclosure = { + {'.', '.', '.'}, + {'W', 's', '.'}, + {'.', '.', '.'} + }; + int[] location = {1, 1}; + Set moveSet = toSet(SalamanderSearch.possibleMoves(enclosure, location)); + + assertTrue(moveSet.contains("0,1")); // up open + assertTrue(moveSet.contains("2,1")); // down open + assertFalse(moveSet.contains("1,0")); // left blocked + assertTrue(moveSet.contains("1,2")); // right open + } + + @Test + public void testPossibleMoves_blockedRightByWall() { + char[][] enclosure = { + {'.', '.', '.'}, + {'.', 's', 'W'}, + {'.', '.', '.'} + }; + int[] location = {1, 1}; + Set moveSet = toSet(SalamanderSearch.possibleMoves(enclosure, location)); + + assertTrue(moveSet.contains("0,1")); // up open + assertTrue(moveSet.contains("2,1")); // down open + assertTrue(moveSet.contains("1,0")); // left open + assertFalse(moveSet.contains("1,2")); // right blocked + } + + @Test + public void testPossibleMoves_topLeftCornerWithOneOpen() { + char[][] enclosure = { + {'s', 'W'}, + {'W', '.'} + }; + int[] location = {0, 0}; + List moves = SalamanderSearch.possibleMoves(enclosure, location); + + assertTrue(moves.isEmpty()); // surrounded by walls/edges + } + + @Test + public void testPossibleMoves_rightEdgeWithOpenLeft() { + char[][] enclosure = { + {'.', '.', 's'} + }; + int[] location = {0, 2}; + List moves = SalamanderSearch.possibleMoves(enclosure, location); + Set moveSet = toSet(moves); + + assertEquals(1, moves.size()); + assertTrue(moveSet.contains("0,1")); + } private Set toSet(List list) { From faf0e4f4ef5c82f178641aeb2620d30d566bfb43 Mon Sep 17 00:00:00 2001 From: HumaGitGud Date: Tue, 8 Apr 2025 14:25:59 -0700 Subject: [PATCH 2/3] Optimized possibleMoves method --- src/SalamanderSearch.java | 82 +++++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/src/SalamanderSearch.java b/src/SalamanderSearch.java index 8bbcf73..f6afbac 100644 --- a/src/SalamanderSearch.java +++ b/src/SalamanderSearch.java @@ -1,5 +1,7 @@ import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; public class SalamanderSearch { public static void main(String[] args) { @@ -18,6 +20,17 @@ public static void main(String[] args) { {'f','W','.','.','W','.'}, {'W','.','W','.','.','.'}, }; + + Set coordinateSet = new HashSet<>(); + int[] coord1 = new int[]{1, 5}; + int[] coord2 = new int[]{3, 7}; + int[] coord3 = new int[]{3, 7}; + + coordinateSet.add(coord1); + coordinateSet.add(coord2); + coordinateSet.add(coord3); + + System.out.println(coordinateSet.size()); } /** @@ -74,36 +87,55 @@ public static List possibleMoves(char[][] enclosure, int[] current) { List moves = new ArrayList<>(); - // UP - int newRow = currRow - 1; - int newCol = currCol; - if (newRow >= 0 && enclosure[newRow][newCol] != 'W') { - moves.add(new int[]{newRow, newCol}); - } - - // DOWN - newRow = currRow + 1; - newCol = currCol; - if (newRow < enclosure.length && enclosure[newRow][newCol] != 'W') { - moves.add(new int[]{newRow, newCol}); - } + int[][] directions = new int[][]{ + {-1, 0}, + {1, 0}, + {0, -1}, + {0, 1} + }; - // LEFT - newRow = currRow; - newCol = currCol - 1; - if (newCol >= 0 && enclosure[newRow][newCol] != 'W') { - moves.add(new int[]{newRow, newCol}); + for(int[] direction : directions) { + int newRow = currRow + direction[0]; + int newCol = currCol + direction[1]; - } - - // RIGHT - newRow = currRow; - newCol = currCol + 1; - if (newCol < enclosure[0].length && enclosure[newRow][newCol] != 'W') { - moves.add(new int[]{newRow, newCol}); + if (newRow >= 0 && newRow < enclosure.length && + newCol >= 0 && newCol < enclosure[0].length && + enclosure[newRow][newCol] != 'W') { + moves.add(new int[]{newRow, newCol}); + } } return moves; + + // Long and dirty implementation + // // UP + // int newRow = currRow - 1; + // int newCol = currCol; + // if (newRow >= 0 && enclosure[newRow][newCol] != 'W') { + // moves.add(new int[]{newRow, newCol}); + // } + + // // DOWN + // newRow = currRow + 1; + // newCol = currCol; + // if (newRow < enclosure.length && enclosure[newRow][newCol] != 'W') { + // moves.add(new int[]{newRow, newCol}); + // } + + // // LEFT + // newRow = currRow; + // newCol = currCol - 1; + // if (newCol >= 0 && enclosure[newRow][newCol] != 'W') { + // moves.add(new int[]{newRow, newCol}); + + // } + + // // RIGHT + // newRow = currRow; + // newCol = currCol + 1; + // if (newCol < enclosure[0].length && enclosure[newRow][newCol] != 'W') { + // moves.add(new int[]{newRow, newCol}); + // } } public static int[] salamanderLocation(char[][] enclosure) { From c8074f36a0d7723b22d0a83c142b0b5a460e7824 Mon Sep 17 00:00:00 2001 From: HumaGitGud Date: Tue, 8 Apr 2025 14:49:08 -0700 Subject: [PATCH 3/3] Added a record --- src/Position.java | 1 + src/SalamanderSearch.java | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 src/Position.java diff --git a/src/Position.java b/src/Position.java new file mode 100644 index 0000000..0bb2282 --- /dev/null +++ b/src/Position.java @@ -0,0 +1 @@ +public record Position(int row, int col) {} \ No newline at end of file diff --git a/src/SalamanderSearch.java b/src/SalamanderSearch.java index f6afbac..815a46a 100644 --- a/src/SalamanderSearch.java +++ b/src/SalamanderSearch.java @@ -21,16 +21,20 @@ public static void main(String[] args) { {'W','.','W','.','.','.'}, }; - Set coordinateSet = new HashSet<>(); - int[] coord1 = new int[]{1, 5}; - int[] coord2 = new int[]{3, 7}; - int[] coord3 = new int[]{3, 7}; + + // Set coordinateSet = new HashSet<>(); + // int[] coord1 = new int[]{1, 5}; + // int[] coord2 = new int[]{3, 7}; + // int[] coord3 = new int[]{3, 7}; + + // coordinateSet.add(coord1); + // coordinateSet.add(coord2); + // coordinateSet.add(coord3); - coordinateSet.add(coord1); - coordinateSet.add(coord2); - coordinateSet.add(coord3); + // System.out.println(coordinateSet.size()); - System.out.println(coordinateSet.size()); + Position pos = new Position(3, 5); + System.out.println(pos); } /** @@ -106,7 +110,7 @@ public static List possibleMoves(char[][] enclosure, int[] current) { } return moves; - + // Long and dirty implementation // // UP // int newRow = currRow - 1;