Add some fun stuff
This commit is contained in:
parent
e4fcb06949
commit
3c43233d96
93
src/search/ChessKnightNode.java
Executable file
93
src/search/ChessKnightNode.java
Executable file
@ -0,0 +1,93 @@
|
||||
package search;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* For a Kata on codewars.com.
|
||||
* How many moves need a knight (Springer) to get from a given start to a given end on the chessboard,
|
||||
* e.g. a3 -> b5, the knight needs 1 move;
|
||||
* a1 -> f1, the knight needs 3 moves
|
||||
*/
|
||||
public class ChessKnightNode extends Node<ChessKnightNode.KnightPair> {
|
||||
public ChessKnightNode(final KnightPair state) {
|
||||
super(state);
|
||||
}
|
||||
|
||||
private ChessKnightNode(final KnightPair value, final Node<KnightPair> parent, final int heuristicCosts) {
|
||||
super(value, parent, heuristicCosts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTargetReached(final Node<KnightPair> target) {
|
||||
return this.valueEquals(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node<KnightPair>> generateSuccessors() {
|
||||
final var successors = new ArrayList<Node<KnightPair>>();
|
||||
|
||||
for (final var dir : ChessDirections.values()) {
|
||||
try {
|
||||
final char newCol = (char) (super.value.col() + dir.getColChange());
|
||||
final char newRow = (char) (super.value.row() + dir.getRowChange());
|
||||
|
||||
final var newState = new KnightPair(newCol, newRow);
|
||||
|
||||
final var successor = new ChessKnightNode(newState, this, super.heuristicCosts + 1);
|
||||
|
||||
if (!successor.valueEquals(this) && !successor.valueEquals(super.getParent())) {
|
||||
successors.add(successor);
|
||||
}
|
||||
} catch (final ChessKnightException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return successors;
|
||||
}
|
||||
|
||||
private boolean valueEquals(final Node<KnightPair> node) {
|
||||
return node != null && super.value.equals(node.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "%c%c".formatted(super.value.col, super.value.row);
|
||||
}
|
||||
|
||||
protected record KnightPair(char col, char row) {
|
||||
public KnightPair {
|
||||
if (col < 'a' || col > 'h') throw new ChessKnightException("Row out of bounds at index %c".formatted(col));
|
||||
if (row < '1' || row > '8') throw new ChessKnightException("Column out of bounds at index %c".formatted(row));
|
||||
}
|
||||
}
|
||||
|
||||
protected enum ChessDirections {
|
||||
TOP_LEFT(-1, 2), TOP_RIGHT(1, 2),
|
||||
RIGHT_TOP(2, 1), RIGHT_BOTTOM(2, -1),
|
||||
DOWN_RIGHT(1 ,-2), DOWN_LEFT(-1,-2),
|
||||
LEFT_BOTTOM(-2,-1), LEFT_TOP(-2,1);
|
||||
|
||||
public final int colChange;
|
||||
public final int rowChange;
|
||||
|
||||
ChessDirections( final int colChange, final int rowChange) {
|
||||
this.colChange = colChange;
|
||||
this.rowChange = rowChange;
|
||||
}
|
||||
|
||||
public int getColChange() {
|
||||
return colChange;
|
||||
}
|
||||
|
||||
public int getRowChange() {
|
||||
return rowChange;
|
||||
}
|
||||
}
|
||||
|
||||
protected static class ChessKnightException extends IndexOutOfBoundsException {
|
||||
public ChessKnightException(final String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
}
|
107
test/search/ChessKnightNodeTest.java
Executable file
107
test/search/ChessKnightNodeTest.java
Executable file
@ -0,0 +1,107 @@
|
||||
package search;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ChessKnightNodeTest
|
||||
{
|
||||
@Test
|
||||
public void shouldReturnTrueWhenTargetReached()
|
||||
{
|
||||
final var state = new ChessKnightNode.KnightPair('a', '1');
|
||||
|
||||
final var node = new ChessKnightNode(state);
|
||||
final var target = new ChessKnightNode(state);
|
||||
|
||||
Assertions.assertTrue(node.isTargetReached(target));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnFalseWhenTargetNotReached()
|
||||
{
|
||||
final var actualState = new ChessKnightNode.KnightPair('a', '1');
|
||||
|
||||
final var targetState = new ChessKnightNode.KnightPair('h', '8');
|
||||
|
||||
final var node = new ChessKnightNode(actualState);
|
||||
final var target = new ChessKnightNode(targetState);
|
||||
|
||||
Assertions.assertFalse(node.isTargetReached(target));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnNonEmptyListOfSuccessors()
|
||||
{
|
||||
final var state = new ChessKnightNode.KnightPair('a', '1');
|
||||
|
||||
final var node = new ChessKnightNode(state);
|
||||
final var successors = node.generateSuccessors();
|
||||
|
||||
Assertions.assertFalse(successors.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnCorrectSuccessorsWithMaxPossibleSuccessors()
|
||||
{
|
||||
final var state = new ChessKnightNode.KnightPair('f', '3');
|
||||
|
||||
final var node = new ChessKnightNode(state);
|
||||
final var successors = node.generateSuccessors();
|
||||
|
||||
Assertions.assertEquals(8, successors.size());
|
||||
|
||||
final var newState1 = new ChessKnightNode.KnightPair('e', '5');
|
||||
final var newState2 = new ChessKnightNode.KnightPair('g', '5');
|
||||
final var newState3 = new ChessKnightNode.KnightPair('h', '4');
|
||||
final var newState4 = new ChessKnightNode.KnightPair('h', '2');
|
||||
final var newState5 = new ChessKnightNode.KnightPair('g', '1');
|
||||
final var newState6 = new ChessKnightNode.KnightPair('e', '1');
|
||||
final var newState7 = new ChessKnightNode.KnightPair('d', '2');
|
||||
final var newState8 = new ChessKnightNode.KnightPair('d', '4');
|
||||
|
||||
|
||||
Assertions.assertEquals(newState1, successors.get(0).getValue());
|
||||
Assertions.assertEquals(newState2, successors.get(1).getValue());
|
||||
Assertions.assertEquals(newState3, successors.get(2).getValue());
|
||||
Assertions.assertEquals(newState4, successors.get(3).getValue());
|
||||
Assertions.assertEquals(newState5, successors.get(4).getValue());
|
||||
Assertions.assertEquals(newState6, successors.get(5).getValue());
|
||||
Assertions.assertEquals(newState7, successors.get(6).getValue());
|
||||
Assertions.assertEquals(newState8, successors.get(7).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnCorrectSuccessorsOnEdge()
|
||||
{
|
||||
final var state = new ChessKnightNode.KnightPair('a', '1');
|
||||
|
||||
final var node = new ChessKnightNode(state);
|
||||
final var successors = node.generateSuccessors();
|
||||
|
||||
Assertions.assertEquals(2, successors.size());
|
||||
|
||||
Assertions.assertThrows(ChessKnightNode.ChessKnightException.class,
|
||||
() -> new ChessKnightNode.KnightPair((char) ('a' - 1), '3'));
|
||||
|
||||
final var newState1 = new ChessKnightNode.KnightPair('b', '3');
|
||||
final var newState2 = new ChessKnightNode.KnightPair('c', '2');
|
||||
|
||||
Assertions.assertThrows(ChessKnightNode.ChessKnightException.class,
|
||||
() -> new ChessKnightNode.KnightPair('c', '0'));
|
||||
|
||||
Assertions.assertThrows(ChessKnightNode.ChessKnightException.class,
|
||||
() -> new ChessKnightNode.KnightPair('c', (char) ('0' - 1)));
|
||||
|
||||
Assertions.assertThrows(ChessKnightNode.ChessKnightException.class,
|
||||
() -> new ChessKnightNode.KnightPair((char) ('a' - 1), (char) ('0' - 1)));
|
||||
|
||||
Assertions.assertThrows(ChessKnightNode.ChessKnightException.class,
|
||||
() -> new ChessKnightNode.KnightPair((char) ('a' - 2), '0'));
|
||||
|
||||
Assertions.assertThrows(ChessKnightNode.ChessKnightException.class,
|
||||
() -> new ChessKnightNode.KnightPair((char) ('a' - 2), '3'));
|
||||
|
||||
Assertions.assertEquals(newState1, successors.get(0).getValue());
|
||||
Assertions.assertEquals(newState2, successors.get(1).getValue());
|
||||
}
|
||||
}
|
51
test/search/ChessKnightTest.java
Executable file
51
test/search/ChessKnightTest.java
Executable file
@ -0,0 +1,51 @@
|
||||
package search;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import search.uninformed.breadthfirstsearch.BreadthFirstSearch;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static search.SearchTestUtils.countNodes;
|
||||
import static search.SearchTestUtils.printSolution;
|
||||
|
||||
public class ChessKnightTest
|
||||
{
|
||||
@Test
|
||||
public void shouldReturnOne()
|
||||
{
|
||||
final var state = new ChessKnightNode.KnightPair('a', '3');
|
||||
|
||||
final var root = new ChessKnightNode(state);
|
||||
|
||||
final var targetState = new ChessKnightNode.KnightPair('b', '5');
|
||||
final var expected = new ChessKnightNode(targetState);
|
||||
|
||||
final var actual = new BreadthFirstSearch().breadthFirstSearch(List.of(root), expected);
|
||||
|
||||
printSolution(actual);
|
||||
var nodeCount = countNodes(actual);
|
||||
var edgeCount = nodeCount - 1;
|
||||
|
||||
Assertions.assertEquals(1, edgeCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnThree()
|
||||
{
|
||||
final var state = new ChessKnightNode.KnightPair('a', '1');
|
||||
|
||||
final var root = new ChessKnightNode(state);
|
||||
|
||||
final var targetState = new ChessKnightNode.KnightPair('f', '1');
|
||||
final var expected = new ChessKnightNode(targetState);
|
||||
|
||||
final var actual = new BreadthFirstSearch().breadthFirstSearch(List.of(root), expected);
|
||||
|
||||
printSolution(actual);
|
||||
var nodeCount = countNodes(actual);
|
||||
var edgeCount = nodeCount - 1;
|
||||
|
||||
Assertions.assertEquals(3, edgeCount);
|
||||
}
|
||||
}
|
@ -11,9 +11,9 @@ public class GameHelperTest
|
||||
void shouldReturnCorrectTargetWartales()
|
||||
{
|
||||
final int[][] state = {
|
||||
{5, 8, 2},
|
||||
{1, 7, 3},
|
||||
{4, 0, 6}
|
||||
{1, 8, 6},
|
||||
{3, 2, 7},
|
||||
{5, 4, 0}
|
||||
};
|
||||
final var root = new EightPuzzleNode(state);
|
||||
|
||||
|
@ -17,4 +17,18 @@ public class SearchTestUtils
|
||||
|
||||
System.out.println("START");
|
||||
}
|
||||
|
||||
public static <T> int countNodes(final Node<T> targetNode)
|
||||
{
|
||||
var node = targetNode;
|
||||
|
||||
int nodeCount = 0;
|
||||
while (node != null)
|
||||
{
|
||||
nodeCount++;
|
||||
node = node.getParent();
|
||||
}
|
||||
|
||||
return nodeCount;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user