Changed several implementation details and added depth-first-search

This commit is contained in:
Niklas Birk 2019-03-27 19:59:28 +01:00
parent 481b292d17
commit 82e7e618d7
10 changed files with 216 additions and 78 deletions

View File

@ -1,6 +1,12 @@
<component name="InspectionProjectProfileManager"> <component name="InspectionProjectProfileManager">
<profile version="1.0"> <profile version="1.0">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="OptionalGetWithoutIsPresent" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="SuspiciousNameCombination" enabled="false" level="WARNING" enabled_by_default="false">
<group names="x,width,left,right" />
<group names="y,height,top,bottom" />
<ignored>
<option name="METHOD_MATCHER_CONFIG" value="java.io.PrintStream,println,java.io.PrintWriter,println,java.lang.System,identityHashCode,java.sql.PreparedStatement,set.*,java.sql.ResultSet,update.*,java.sql.SQLOutput,write.*,java.lang.Integer,compare.*,java.lang.Long,compare.*,java.lang.Short,compare,java.lang.Byte,compare,java.lang.Character,compare,java.lang.Boolean,compare,java.lang.Math,.*,java.lang.StrictMath,.*" />
</ignored>
</inspection_tool>
</profile> </profile>
</component> </component>

View File

@ -1,5 +1,3 @@
package search.breadthfirstsearch;
public class Main public class Main
{ {
public static void main(String[] args) public static void main(String[] args)

View File

@ -1,4 +1,4 @@
package search.breadthfirstsearch; package search;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -21,18 +21,7 @@ public class EightPuzzleNode extends Node<int[][]>
@Override @Override
public boolean isTargetReached(Node<int[][]> target) public boolean isTargetReached(Node<int[][]> target)
{ {
for (int row = 0; row < super.value.length; row++) return valueEquals(target);
{
for (int col = 0; col < super.value[row].length; col++)
{
if (super.value[row][col] != target.value[row][col])
{
return false;
}
}
}
return true;
} }
@Override @Override
@ -40,8 +29,8 @@ public class EightPuzzleNode extends Node<int[][]>
{ {
var successors = new ArrayList<Node<int[][]>>(); var successors = new ArrayList<Node<int[][]>>();
var emptyPosition = Objects.requireNonNull(detectEmptyPosition()); var emptyPosition = Objects.requireNonNull(detectEmptyPosition());
int x = emptyPosition.getRight(); int x = emptyPosition.getX();
int y = emptyPosition.getLeft(); int y = emptyPosition.getY();
for (Direction dir : Direction.values()) for (Direction dir : Direction.values())
{ {
@ -49,34 +38,20 @@ public class EightPuzzleNode extends Node<int[][]>
try try
{ {
int tmp; var posToSwap = switch (dir) {
switch (dir) case TOP -> new IntPair(x, y-1);
case RIGHT -> new IntPair(x+1, y);
case DOWN -> new IntPair(x, y+1);
case LEFT -> new IntPair(x-1, y);
};
var successor = this.swapStateField(newState, emptyPosition, posToSwap);
if (!successor.valueEquals(this) && !successor.valueEquals(super.getParent()))
{ {
case TOP: successors.add(successor);
tmp = newState[y-1][x];
newState[y-1][x] = newState[y][x];
newState[y][x] = tmp;
successors.add(new EightPuzzleNode(newState, this));
break;
case RIGHT:
tmp = newState[y][x+1];
newState[y][x+1] = newState[y][x];
newState[y][x] = tmp;
successors.add(new EightPuzzleNode(newState, this));
break;
case DOWN:
tmp = newState[y+1][x];
newState[y+1][x] = newState[y][x];
newState[y][x] = tmp;
successors.add(new EightPuzzleNode(newState, this));
break;
case LEFT:
tmp = newState[y][x-1];
newState[y][x-1] = newState[y][x];
newState[y][x] = tmp;
successors.add(new EightPuzzleNode(newState, this));
break;
} }
} }
catch (ArrayIndexOutOfBoundsException ignored) catch (ArrayIndexOutOfBoundsException ignored)
{ {
@ -87,13 +62,47 @@ public class EightPuzzleNode extends Node<int[][]>
} }
@Override @Override
protected boolean isValidValue(int[][] state) protected boolean isValidParameterValue(int[][] state)
{ {
var numbers = Arrays.stream(state).flatMapToInt(IntStream::of).toArray(); var numbers = Arrays.stream(state).flatMapToInt(IntStream::of).toArray();
return numbersAreInAllowedRange(numbers) && numbersAreUnique(numbers); return numbersAreInAllowedRange(numbers) && numbersAreUnique(numbers);
} }
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
for (int[] row : super.value)
{
builder.append(Arrays.toString(row)).append("\n");
}
return builder.toString();
}
private boolean valueEquals(Node<int[][]> node)
{
if (node == null)
{
return false;
}
for (int row = 0; row < super.value.length; row++)
{
for (int col = 0; col < super.value[row].length; col++)
{
if (super.value[row][col] != node.value[row][col])
{
return false;
}
}
}
return true;
}
private boolean numbersAreInAllowedRange(int[] numbers) private boolean numbersAreInAllowedRange(int[] numbers)
{ {
return Arrays.stream(numbers).min().getAsInt() == 0 && Arrays.stream(numbers).max().getAsInt() == 8; return Arrays.stream(numbers).min().getAsInt() == 0 && Arrays.stream(numbers).max().getAsInt() == 8;
@ -112,7 +121,7 @@ public class EightPuzzleNode extends Node<int[][]>
{ {
if (super.value[row][col] == 0) if (super.value[row][col] == 0)
{ {
return new IntPair(row, col); return new IntPair(col, row);
} }
} }
} }
@ -132,38 +141,39 @@ public class EightPuzzleNode extends Node<int[][]>
return copy; return copy;
} }
@Override private EightPuzzleNode swapStateField(int[][] newState, IntPair emptyPos, IntPair posToSwap)
public String toString()
{ {
StringBuilder builder = new StringBuilder("Node:\n"); int posToSwapX = posToSwap.getX();
int postToSwapY = posToSwap.getY();
int emptyX = emptyPos.getX();
int emptyY = emptyPos.getY();
int tmp;
for (int[] row : super.value) tmp = newState[postToSwapY][posToSwapX];
{ newState[postToSwapY][posToSwapX] = newState[emptyY][emptyX];
builder.append(Arrays.toString(row)).append("\n"); newState[emptyY][emptyX] = tmp;
}
return builder.toString(); return new EightPuzzleNode(newState, this);
} }
private class IntPair private class IntPair
{ {
private final int left; private final int x;
private final int y;
private final int right; public IntPair(int x, int y)
public IntPair(int left, int right)
{ {
this.left = left; this.x = x;
this.right = right; this.y = y;
} }
public int getLeft() public int getX()
{ {
return left; return x;
} }
public int getRight() public int getY()
{ {
return right; return y;
} }
} }

View File

@ -1,4 +1,4 @@
package search.breadthfirstsearch; package search;
import java.util.List; import java.util.List;
@ -14,7 +14,7 @@ public abstract class Node<T>
protected Node(T value, Node<T> parent) protected Node(T value, Node<T> parent)
{ {
if (!isValidValue(value)) if (!isValidParameterValue(value))
{ {
throw new IllegalArgumentException("Illegal node value"); throw new IllegalArgumentException("Illegal node value");
} }
@ -33,13 +33,7 @@ public abstract class Node<T>
return this.parent; return this.parent;
} }
@Override protected abstract boolean isValidParameterValue(T value);
public String toString()
{
return this.value.toString();
}
protected abstract boolean isValidValue(T value);
public abstract boolean isTargetReached(Node<T> target); public abstract boolean isTargetReached(Node<T> target);
public abstract List<Node<T>> generateSuccessors(); public abstract List<Node<T>> generateSuccessors();
} }

View File

@ -1,5 +1,7 @@
package search.breadthfirstsearch; package search.breadthfirstsearch;
import search.Node;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View File

@ -0,0 +1,33 @@
package search.depthfirstsearch;
import search.Node;
import java.util.ArrayList;
import java.util.List;
public class DepthFirstSearch
{
public <T> Node<T> depthFirstSearch(Node<T> node, Node<T> target)
{
if (node.isTargetReached(target))
{
return node;
}
var newNodes = node.generateSuccessors();
while (!newNodes.isEmpty())
{
var resultNode = depthFirstSearch(newNodes.get(0), target);
if (resultNode != null)
{
return resultNode;
}
newNodes.remove(0);
}
return null;
}
}

View File

@ -1,7 +1,8 @@
package search.breadthfirstsearch; package search;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import search.EightPuzzleNode;
class EightPuzzleNodeTest class EightPuzzleNodeTest
{ {

View File

@ -0,0 +1,20 @@
package search;
public class SearchTestUtils
{
public static <T> void printSolution(Node<T> targetNode)
{
var node = targetNode;
System.out.println("Read from down to top!");
System.out.println("END");
while (node != null)
{
System.out.println(node);
node = node.getParent();
}
System.out.println("START");
}
}

View File

@ -1,10 +1,12 @@
package search.breadthfirstsearch; package search.breadthfirstsearch;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import search.EightPuzzleNode;
import java.util.Arrays;
import java.util.List; import java.util.List;
import static search.SearchTestUtils.printSolution;
class BreadthFirstSearchTest class BreadthFirstSearchTest
{ {
@Test @Test
@ -26,12 +28,28 @@ class BreadthFirstSearchTest
var actual = new BreadthFirstSearch().breadthFirstSearch(List.of(root), expected); var actual = new BreadthFirstSearch().breadthFirstSearch(List.of(root), expected);
System.out.println("Target: " + Arrays.deepToString(targetState)); printSolution(actual);
System.out.println("Actual:\n" + actual);
} }
private void printSolution(Node<int[][]> targetNode) @Test
void shouldReturnCorrectTargetChubekNode()
{ {
int[][] state = {
{2, 0, 4},
{6, 7, 1},
{8, 5, 3}
};
var root = new EightPuzzleNode(state);
int[][] targetState = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 0}
};
var expected = new EightPuzzleNode(targetState);
var actual = new BreadthFirstSearch().breadthFirstSearch(List.of(root), expected);
printSolution(actual);
} }
} }

View File

@ -0,0 +1,56 @@
package search.depthfirstsearch;
import org.junit.jupiter.api.Test;
import search.EightPuzzleNode;
import search.breadthfirstsearch.BreadthFirstSearch;
import java.util.List;
import static search.SearchTestUtils.printSolution;
class DepthFirstSearchTest
{
@Test
void shouldReturnCorrectTarget()
{
int[][] state = {
{1, 2, 3},
{4, 5, 6},
{7, 0, 8}
};
var root = new EightPuzzleNode(state);
int[][] targetState = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 0}
};
var expected = new EightPuzzleNode(targetState);
var actual = new DepthFirstSearch().depthFirstSearch(root, expected);
printSolution(actual);
}
// @Test
// void shouldReturnCorrectTargetChubekNode()
// {
// int[][] state = {
// {2, 0, 4},
// {6, 7, 1},
// {8, 5, 3}
// };
// var root = new EightPuzzleNode(state);
//
// int[][] targetState = {
// {1, 2, 3},
// {4, 5, 6},
// {7, 8, 0}
// };
// var expected = new EightPuzzleNode(targetState);
//
// var actual = new DepthFirstSearch().depthFirstSearch(root, expected);
//
// printSolution(actual);
// }
}