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">
<profile version="1.0">
<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>
</component>

View File

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

View File

@ -1,4 +1,4 @@
package search.breadthfirstsearch;
package search;
import java.util.ArrayList;
import java.util.Arrays;
@ -21,18 +21,7 @@ public class EightPuzzleNode extends Node<int[][]>
@Override
public boolean isTargetReached(Node<int[][]> target)
{
for (int row = 0; row < super.value.length; row++)
{
for (int col = 0; col < super.value[row].length; col++)
{
if (super.value[row][col] != target.value[row][col])
{
return false;
}
}
}
return true;
return valueEquals(target);
}
@Override
@ -40,8 +29,8 @@ public class EightPuzzleNode extends Node<int[][]>
{
var successors = new ArrayList<Node<int[][]>>();
var emptyPosition = Objects.requireNonNull(detectEmptyPosition());
int x = emptyPosition.getRight();
int y = emptyPosition.getLeft();
int x = emptyPosition.getX();
int y = emptyPosition.getY();
for (Direction dir : Direction.values())
{
@ -49,34 +38,20 @@ public class EightPuzzleNode extends Node<int[][]>
try
{
int tmp;
switch (dir)
var posToSwap = 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:
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;
successors.add(successor);
}
}
catch (ArrayIndexOutOfBoundsException ignored)
{
@ -87,13 +62,47 @@ public class EightPuzzleNode extends Node<int[][]>
}
@Override
protected boolean isValidValue(int[][] state)
protected boolean isValidParameterValue(int[][] state)
{
var numbers = Arrays.stream(state).flatMapToInt(IntStream::of).toArray();
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)
{
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)
{
return new IntPair(row, col);
return new IntPair(col, row);
}
}
}
@ -132,38 +141,39 @@ public class EightPuzzleNode extends Node<int[][]>
return copy;
}
@Override
public String toString()
private EightPuzzleNode swapStateField(int[][] newState, IntPair emptyPos, IntPair posToSwap)
{
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)
{
builder.append(Arrays.toString(row)).append("\n");
}
tmp = newState[postToSwapY][posToSwapX];
newState[postToSwapY][posToSwapX] = newState[emptyY][emptyX];
newState[emptyY][emptyX] = tmp;
return builder.toString();
return new EightPuzzleNode(newState, this);
}
private class IntPair
{
private final int left;
private final int x;
private final int y;
private final int right;
public IntPair(int left, int right)
public IntPair(int x, int y)
{
this.left = left;
this.right = right;
this.x = x;
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;
@ -14,7 +14,7 @@ public abstract class Node<T>
protected Node(T value, Node<T> parent)
{
if (!isValidValue(value))
if (!isValidParameterValue(value))
{
throw new IllegalArgumentException("Illegal node value");
}
@ -33,13 +33,7 @@ public abstract class Node<T>
return this.parent;
}
@Override
public String toString()
{
return this.value.toString();
}
protected abstract boolean isValidValue(T value);
protected abstract boolean isValidParameterValue(T value);
public abstract boolean isTargetReached(Node<T> target);
public abstract List<Node<T>> generateSuccessors();
}

View File

@ -1,5 +1,7 @@
package search.breadthfirstsearch;
import search.Node;
import java.util.ArrayList;
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.Test;
import search.EightPuzzleNode;
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;
import org.junit.jupiter.api.Test;
import search.EightPuzzleNode;
import java.util.Arrays;
import java.util.List;
import static search.SearchTestUtils.printSolution;
class BreadthFirstSearchTest
{
@Test
@ -26,12 +28,28 @@ class BreadthFirstSearchTest
var actual = new BreadthFirstSearch().breadthFirstSearch(List.of(root), expected);
System.out.println("Target: " + Arrays.deepToString(targetState));
System.out.println("Actual:\n" + actual);
printSolution(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);
// }
}