Changed several implementation details and added depth-first-search
This commit is contained in:
parent
481b292d17
commit
82e7e618d7
8
.idea/inspectionProfiles/Project_Default.xml
generated
8
.idea/inspectionProfiles/Project_Default.xml
generated
@ -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>
|
@ -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)
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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();
|
||||||
}
|
}
|
@ -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;
|
||||||
|
|
||||||
|
33
src/search/depthfirstsearch/DepthFirstSearch.java
Normal file
33
src/search/depthfirstsearch/DepthFirstSearch.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
{
|
{
|
20
test/search/SearchTestUtils.java
Normal file
20
test/search/SearchTestUtils.java
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
56
test/search/depthfirstsearch/DepthFirstSearchTest.java
Normal file
56
test/search/depthfirstsearch/DepthFirstSearchTest.java
Normal 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);
|
||||||
|
// }
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user