diff --git a/src/search/breadthfirstsearch/EightPuzzleNode.java b/src/search/breadthfirstsearch/EightPuzzleNode.java new file mode 100644 index 0000000..06c14f9 --- /dev/null +++ b/src/search/breadthfirstsearch/EightPuzzleNode.java @@ -0,0 +1,62 @@ +package search.breadthfirstsearch; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.IntStream; + +public class EightPuzzleNode implements Node +{ + private int[][] state; + + public EightPuzzleNode(int[][] state) { + if (!isValidState(state)) + { + throw new IllegalArgumentException("Allowed numbers are only 0-8 and they must exist uniquely."); + } + + this.state = state; + } + + @Override + public boolean isTargetReached(Node target) { + return Arrays.equals(this.state, ((EightPuzzleNode) target).state); + } + + @Override + public List generateSuccessors() { + ArrayList successors = new ArrayList<>(); + + successors.add(new EightPuzzleNode(null)); + + return successors; + } + + private boolean isValidState(int[][] state) + { + int[] numbers = Arrays.stream(state).flatMapToInt(IntStream::of).toArray(); + int[] countOfNumbers = new int[9]; + + for (int i : numbers) + { + try + { + countOfNumbers[i]++; + } + catch (ArrayIndexOutOfBoundsException e) + { + return false; + } + } + + for (int i : countOfNumbers) + { + if (i == 0 || i > 1) + { + return false; + } + } + + return true; + } +} diff --git a/test/search/breadthfirstsearch/EightPuzzleNodeTest.java b/test/search/breadthfirstsearch/EightPuzzleNodeTest.java new file mode 100644 index 0000000..036a5e0 --- /dev/null +++ b/test/search/breadthfirstsearch/EightPuzzleNodeTest.java @@ -0,0 +1,87 @@ +package search.breadthfirstsearch; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class EightPuzzleNodeTest +{ + @Test + public void shouldThrowExceptionWhileStateHasDuplicateNumbers() + { + int[][] state = { + {1, 1, 3}, + {4, 5, 6}, + {7, 8, 0} + }; + + Assertions.assertThrows(IllegalArgumentException.class, () -> new EightPuzzleNode(state)); + } + + @Test + public void shouldThrowExceptionWhileStateHasNumbersOutOfRange() + { + int[][] state = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9} + }; + + Assertions.assertThrows(IllegalArgumentException.class, () -> new EightPuzzleNode(state)); + } + + @Test + public void shouldReturnTrueWhenTargetReached() + { + int[][] state = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 0} + }; + + var node = new EightPuzzleNode(state); + var target = new EightPuzzleNode(state); + + Assertions.assertTrue(node.isTargetReached(target)); + } + + @Test + public void shouldReturnFalseWhenTargetNotReached() + { + int[][] actualState = { + {7, 1, 6}, + {0, 4, 2}, + {3, 5, 8} + }; + + int[][] targetState = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 0} + }; + + var node = new EightPuzzleNode(actualState); + var target = new EightPuzzleNode(targetState); + + Assertions.assertFalse(node.isTargetReached(target)); + } + + @Test + public void shouldReturnNonEmptyListOfSuccessors() + { + int[][] state = { + {7, 1, 6}, + {0, 4, 2}, + {3, 5, 8} + }; + + var node = new EightPuzzleNode(state); + var successors = node.generateSuccessors(); + + Assertions.assertFalse(successors.isEmpty()); + } + +} \ No newline at end of file