diff --git a/src/search/EightPuzzleNode.java b/src/search/EightPuzzleNode.java index 9bac00a..eb8d235 100644 --- a/src/search/EightPuzzleNode.java +++ b/src/search/EightPuzzleNode.java @@ -28,10 +28,10 @@ public class EightPuzzleNode extends Node { final var successors = new ArrayList>(); final var emptyPosition = Objects.requireNonNull(detectEmptyPosition()); - final int x = emptyPosition.getX(); - final int y = emptyPosition.getY(); + final var x = emptyPosition.getX(); + final var y = emptyPosition.getY(); - for (final Direction dir : Direction.values()) + for (final var dir : Direction.values()) { final var newState = copyOfState(); @@ -50,7 +50,6 @@ public class EightPuzzleNode extends Node { successors.add(successor); } - } catch (final ArrayIndexOutOfBoundsException ignored) { @@ -63,9 +62,9 @@ public class EightPuzzleNode extends Node @Override public String toString() { - final StringBuilder builder = new StringBuilder(); + final var builder = new StringBuilder(); - for (final int[] row : super.value) + for (final var row : super.value) { builder.append(Arrays.toString(row)).append("\n"); } @@ -80,9 +79,9 @@ public class EightPuzzleNode extends Node return false; } - for (int row = 0; row < super.value.length; row++) + for (var row = 0; row < super.value.length; row++) { - for (int col = 0; col < super.value[row].length; col++) + for (var col = 0; col < super.value[row].length; col++) { if (super.value[row][col] != node.value[row][col]) { @@ -96,9 +95,9 @@ public class EightPuzzleNode extends Node private IntPair detectEmptyPosition() { - for (int row = 0; row < super.value.length; row++) + for (var row = 0; row < super.value.length; row++) { - for (int col = 0; col < super.value[row].length; col++) + for (var col = 0; col < super.value[row].length; col++) { if (super.value[row][col] == 0) { @@ -114,7 +113,7 @@ public class EightPuzzleNode extends Node { final var copy = new int[3][3]; - for (int y = 0; y < copy.length; y++) + for (var y = 0; y < copy.length; y++) { System.arraycopy(super.value[y], 0, copy[y], 0, copy.length); } @@ -124,15 +123,9 @@ public class EightPuzzleNode extends Node private EightPuzzleNode swapStateField(final int[][] newState, final IntPair emptyPos, final IntPair posToSwap) { - final int posToSwapX = posToSwap.getX(); - final int postToSwapY = posToSwap.getY(); - final int emptyX = emptyPos.getX(); - final int emptyY = emptyPos.getY(); - final int tmp; - - tmp = newState[postToSwapY][posToSwapX]; - newState[postToSwapY][posToSwapX] = newState[emptyY][emptyX]; - newState[emptyY][emptyX] = tmp; + final var tmp = newState[posToSwap.getY()][posToSwap.getX()]; + newState[posToSwap.getY()][posToSwap.getX()] = newState[emptyPos.getY()][emptyPos.getX()]; + newState[emptyPos.getY()][emptyPos.getX()] = tmp; return new EightPuzzleNode(newState, this); } @@ -162,6 +155,5 @@ public class EightPuzzleNode extends Node private enum Direction { TOP, RIGHT, DOWN, LEFT - } } diff --git a/src/search/Node.java b/src/search/Node.java index fb97094..295fff1 100644 --- a/src/search/Node.java +++ b/src/search/Node.java @@ -1,6 +1,9 @@ package search; +import search.heuristic.Heuristic; + import java.util.List; +import java.util.Objects; public abstract class Node { @@ -14,7 +17,7 @@ public abstract class Node protected Node(final T value, final Node parent) { - this.value = value; + this.value = Objects.requireNonNull(value); this.parent = parent; } @@ -28,6 +31,6 @@ public abstract class Node return this.parent; } - public abstract boolean isTargetReached(Node target); + public abstract boolean isTargetReached(final Node target); public abstract List> generateSuccessors(); } diff --git a/src/search/heuristic/AStar.java b/src/search/heuristic/AStar.java index 8ca5914..ec2cc4a 100644 --- a/src/search/heuristic/AStar.java +++ b/src/search/heuristic/AStar.java @@ -1,5 +1,39 @@ package search.heuristic; -public class AStar +import search.Node; + +import java.util.Comparator; +import java.util.PriorityQueue; + +public class AStar { + private final Heuristic heuristicFunction; + + public AStar(final Heuristic heuristicFunction) + { + this.heuristicFunction = heuristicFunction; + } + + public Node heuristicSearch(final Node start, final Node target) + { + final var nodes = new PriorityQueue>(Comparator.comparingInt(node -> heuristicFunction.heuristic(node, target))); + nodes.add(start); + + while (true) + { + if (nodes.isEmpty()) + { + return null; + } + + final var node = nodes.poll(); + + if (node.isTargetReached(target)) + { + return node; + } + + nodes.addAll(node.generateSuccessors()); + } + } } diff --git a/src/search/heuristic/Heuristic.java b/src/search/heuristic/Heuristic.java new file mode 100644 index 0000000..8d040e1 --- /dev/null +++ b/src/search/heuristic/Heuristic.java @@ -0,0 +1,8 @@ +package search.heuristic; + +import search.Node; + +public interface Heuristic +{ + int heuristic(Node node, Node target); +} diff --git a/src/search/uninformed/breadthfirstsearch/BreadthFirstSearch.java b/src/search/uninformed/breadthfirstsearch/BreadthFirstSearch.java index 12aaff5..983fcc9 100644 --- a/src/search/uninformed/breadthfirstsearch/BreadthFirstSearch.java +++ b/src/search/uninformed/breadthfirstsearch/BreadthFirstSearch.java @@ -11,7 +11,7 @@ public class BreadthFirstSearch { final var newNodes = new ArrayList>(); - for (final Node node : nodes) + for (final var node : nodes) { if (node.isTargetReached(target)) { diff --git a/src/search/uninformed/iterativedeepening/IterativeDeepening.java b/src/search/uninformed/iterativedeepening/IterativeDeepening.java index 70cde89..67e67f2 100644 --- a/src/search/uninformed/iterativedeepening/IterativeDeepening.java +++ b/src/search/uninformed/iterativedeepening/IterativeDeepening.java @@ -6,7 +6,7 @@ public class IterativeDeepening { public Node iterativeDeepening(final Node node, final Node target) { - int lowBarrier = 0; + var lowBarrier = 0; Node resultNode; diff --git a/test/search/heuristic/AStarTest.java b/test/search/heuristic/AStarTest.java index 071c42e..70e38af 100644 --- a/test/search/heuristic/AStarTest.java +++ b/test/search/heuristic/AStarTest.java @@ -1,8 +1,80 @@ package search.heuristic; +import org.junit.jupiter.api.Test; +import search.EightPuzzleNode; +import search.Node; + import static org.junit.jupiter.api.Assertions.*; +import static search.SearchTestUtils.printSolution; class AStarTest { + @Test + void shouldReturnCorrectTargetCubekNodeHeuristik1() + { + final int[][] state = { + {3, 5, 0}, + {1, 2, 6}, + {4, 7, 8} + }; + final var root = new EightPuzzleNode(state); + final int[][] targetState = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 0} + }; + final var expected = new EightPuzzleNode(targetState); + + final var actual = new AStar<>(this::h1).heuristicSearch(root, expected); + + printSolution(actual); + } + + @Test + void shouldReturnCorrectTargetCubekNodeHeuristic2() + { + final int[][] state = { + {2, 0, 4}, + {6, 7, 1}, + {8, 5, 3} + }; + final var root = new EightPuzzleNode(state); + + final int[][] targetState = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 0} + }; + final var expected = new EightPuzzleNode(targetState); + + final var actual = new AStar<>(this::h2).heuristicSearch(root, expected); + + printSolution(actual); + } + + private int h1(final Node node, final Node target) + { + final var value = node.getValue(); + final var targetValue = target.getValue(); + var counter = 0; + + for (var row = 0; row < value.length; row++) + { + for (var col = 0; col < value[row].length; col++) + { + if (value[row][col] != targetValue[row][col]) + { + counter++; + } + } + } + + return counter; + } + + private int h2(final Node node, final Node target) + { + return 0; + } } \ No newline at end of file