-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
560 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package lib.maze; | ||
|
||
import lombok.Getter; | ||
import lombok.NonNull; | ||
import org.springframework.lang.Nullable; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
@Getter | ||
public class Node<T> implements Comparable<Node<T>> { | ||
|
||
@NonNull | ||
private final T state; // DFS only | ||
@NonNull | ||
private final Optional<Node<T>> parent; // DFS only | ||
private final double cost; // used in A* not in DFS | ||
private final double heuristic;// used in A* not in DFS | ||
|
||
// DFS Node | ||
private Node(@NonNull final T state, @Nullable final Node<T> parent) { | ||
this.state = state; | ||
this.parent = Optional.ofNullable(parent); | ||
this.cost = 0; | ||
this.heuristic = 0; | ||
} | ||
|
||
// A* Node | ||
private Node(@NonNull final T state, @Nullable final Node<T> parent, final double cost, final double heuristic) { | ||
this.state = state; | ||
this.parent = Optional.ofNullable(parent); | ||
this.cost = cost; | ||
this.heuristic = heuristic; | ||
} | ||
|
||
public static <T> Node<T> astarStartNode( | ||
@NonNull final T state, | ||
final double heuristic | ||
) { | ||
return new Node<>(state, null, 0, heuristic); | ||
} | ||
|
||
public static <T> Node<T> astarNode( | ||
@NonNull final T state, | ||
@Nullable final Node<T> parent, | ||
final double cost, | ||
final double heuristic | ||
) { | ||
return new Node<>(state, parent, cost, heuristic); | ||
} | ||
|
||
public static <T> Node<T> dfsStartNode(@NonNull final T state) { | ||
return new Node<>(state, null); | ||
} | ||
|
||
public static <T> Node<T> dfsNode( | ||
@NonNull final T state, | ||
@NonNull final Node<T> parent | ||
) { | ||
return new Node<>(state, parent); | ||
} | ||
|
||
@NonNull | ||
public static <T> List<T> nodeToPath(@NonNull final Node<T> node) { | ||
final List<T> path = new ArrayList<>(); | ||
path.add(node.getState()); | ||
|
||
// work backwards from end to front | ||
Node<T> currentNode = node; | ||
while (currentNode.getParent().isPresent()) { | ||
currentNode = currentNode.getParent().get(); | ||
path.add(0, currentNode.getState()); | ||
} | ||
|
||
return path; | ||
} | ||
|
||
@Override | ||
public int compareTo(@NonNull final Node<T> other) { | ||
final Double mine = cost + heuristic; | ||
final Double theirs = other.cost + other.heuristic; | ||
return mine.compareTo(theirs); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package lib.maze.searchstrategy; | ||
|
||
import lib.maze.Node; | ||
import lombok.NonNull; | ||
|
||
import java.util.*; | ||
import java.util.function.Function; | ||
import java.util.function.Predicate; | ||
import java.util.function.ToDoubleFunction; | ||
|
||
/** | ||
* A* Search | ||
*/ | ||
public class AstarStrategy { | ||
|
||
@NonNull | ||
public <T> Optional<Node<T>> search( | ||
@NonNull final T initialState, | ||
@NonNull final Predicate<T> goalTest, | ||
@NonNull final Function<T, List<T>> successors, | ||
@NonNull final ToDoubleFunction<T> heuristic | ||
) { | ||
// frontier is where we've yet to go | ||
final PriorityQueue<Node<T>> frontier = new PriorityQueue<>(); | ||
frontier.offer(Node.astarStartNode(initialState, heuristic.applyAsDouble(initialState))); | ||
|
||
// explored is where we've already been | ||
final Map<T, Double> explored = new HashMap<>(); | ||
explored.put(initialState, 0.0); | ||
|
||
// keep going while there is more to explore | ||
while (!frontier.isEmpty()) { | ||
final Node<T> currentNode = frontier.poll(); | ||
final T currentState = currentNode.getState(); | ||
|
||
// if we found the goal, we're done | ||
if (goalTest.test(currentState)) { | ||
return Optional.of(currentNode); | ||
} | ||
|
||
// check where we can go next and haven't explored | ||
for (final T child : successors.apply(currentState)) { | ||
// cost=1 here assumes a simple grid, need a cost function for more sophisticated apps | ||
final double newCost = currentNode.getCost() + 1; | ||
if (!explored.containsKey(child) || explored.get(child) > newCost) { | ||
explored.put(child, newCost); | ||
frontier.offer(Node.astarNode(child, currentNode, newCost, heuristic.applyAsDouble(child))); | ||
} | ||
} | ||
} | ||
|
||
return Optional.empty(); // never found the goal | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package lib.maze.searchstrategy; | ||
|
||
import lib.maze.Node; | ||
import lombok.NonNull; | ||
|
||
import java.util.*; | ||
import java.util.function.Function; | ||
import java.util.function.Predicate; | ||
|
||
/** | ||
* Breadth First Search | ||
*/ | ||
public class BSFStrategy implements GoalSearchableStrategy { | ||
|
||
@NonNull | ||
public <T> Optional<Node<T>> search( | ||
@NonNull final T initialState, | ||
@NonNull final Predicate<T> goalTest, | ||
@NonNull final Function<T, List<T>> successors | ||
) { | ||
// frontier is where we've yet to go | ||
final Queue<Node<T>> frontier = new LinkedList<>(); | ||
frontier.offer(Node.dfsStartNode(initialState)); | ||
|
||
// explored is where we've already been | ||
final Set<T> explored = new HashSet<>(); | ||
explored.add(initialState); | ||
|
||
// keep going while there is more to explore | ||
while (!frontier.isEmpty()) { | ||
final Node<T> currentNode = frontier.poll(); | ||
final T currentState = currentNode.getState(); | ||
|
||
// if we found the goal, we're done | ||
if (goalTest.test(currentState)) { | ||
return Optional.of(currentNode); | ||
} | ||
|
||
// check where we can go next and haven't explored | ||
for (final T child : successors.apply(currentState)) { | ||
if (explored.contains(child)) { | ||
continue; // skip children we already explored | ||
} | ||
explored.add(child); | ||
frontier.offer(Node.dfsNode(child, currentNode)); | ||
} | ||
} | ||
|
||
return Optional.empty(); // never found the goal | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package lib.maze.searchstrategy; | ||
|
||
import lib.maze.Node; | ||
import lombok.NonNull; | ||
|
||
import java.util.*; | ||
import java.util.function.Function; | ||
import java.util.function.Predicate; | ||
|
||
/** | ||
* Depth First Search | ||
*/ | ||
public class DSFStrategy implements GoalSearchableStrategy { | ||
|
||
@NonNull | ||
public <T> Optional<Node<T>> search( | ||
@NonNull final T initialState, | ||
@NonNull final Predicate<T> goalTest, | ||
@NonNull final Function<T, List<T>> successors | ||
) { | ||
// frontier is where we've yet to go | ||
final Stack<Node<T>> frontier = new Stack<>(); | ||
frontier.push(Node.dfsStartNode(initialState)); | ||
|
||
// explored is where we've already been | ||
final Set<T> explored = new HashSet<>(); | ||
explored.add(initialState); | ||
|
||
// keep going while there is more to explore | ||
while (!frontier.isEmpty()) { | ||
final Node<T> currentNode = frontier.pop(); | ||
final T currentState = currentNode.getState(); | ||
|
||
// if we found the goal, we're done | ||
if (goalTest.test(currentState)) { | ||
return Optional.of(currentNode); | ||
} | ||
|
||
// check where we can go next and haven't explored | ||
for (final T child : successors.apply(currentState)) { | ||
if (explored.contains(child)) { | ||
continue; // skip children we already explored | ||
} | ||
explored.add(child); | ||
frontier.push(Node.dfsNode(child, currentNode)); | ||
} | ||
} | ||
|
||
return Optional.empty(); // never found the goal | ||
} | ||
|
||
} |
20 changes: 20 additions & 0 deletions
20
src/main/java/lib/maze/searchstrategy/GoalSearchableStrategy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package lib.maze.searchstrategy; | ||
|
||
import lib.maze.Node; | ||
import lombok.NonNull; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.function.Function; | ||
import java.util.function.Predicate; | ||
|
||
public interface GoalSearchableStrategy { | ||
|
||
@NonNull | ||
<T> Optional<Node<T>> search( | ||
@NonNull final T initialState, | ||
@NonNull final Predicate<T> goalTest, | ||
@NonNull final Function<T, List<T>> successors | ||
); | ||
|
||
} |
Oops, something went wrong.