1
\$\begingroup\$

Intro

I have this GitHub repository containing some iterative deepening pathfinding algorithms.

Code

io.github.coderodde.libid.impl.BidirectionalIterativeDeepeningDepthFirstSearch.java::

package io.github.coderodde.libid.impl;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import io.github.coderodde.libid.NodeExpander;
public final class BidirectionalIterativeDeepeningDepthFirstSearch<N> {
 private final N source;
 private final Deque<N> backwardSearchStack;
 private final Set<N> frontier;
 private final Set<N> visitedForward;
 private final Set<N> visitedBackward;
 private final NodeExpander<N> forwardExpander;
 private final NodeExpander<N> backwardExpander;
 private int previousVisitedSizeForward;
 private int previousVisitedSizeBackward;
 public BidirectionalIterativeDeepeningDepthFirstSearch() {
 this.source = null;
 this.backwardSearchStack = null;
 this.frontier = null;
 this.visitedForward = null;
 this.visitedBackward = null;
 this.forwardExpander = null;
 this.backwardExpander = null;
 }
 private BidirectionalIterativeDeepeningDepthFirstSearch(
 N source,
 NodeExpander<N> forwardExpander,
 NodeExpander<N> backwardExpander) {
 this.source = source;
 this.backwardSearchStack = new ArrayDeque<>();
 this.frontier = new HashSet<>();
 this.visitedForward = new HashSet<>();
 this.visitedBackward = new HashSet<>();
 this.forwardExpander = forwardExpander;
 this.backwardExpander = backwardExpander;
 this.previousVisitedSizeForward = 0;
 this.previousVisitedSizeBackward = 0;
 }
 public List<N> search(N source, 
 N target, 
 NodeExpander<N> forwardExpander,
 NodeExpander<N> backwardExpander) {
 // Handle the easy case. We need this in order to terminate the 
 // recursion in buildPath.
 if (source.equals(target)) {
 return new ArrayList<>(Arrays.asList(source));
 }
 BidirectionalIterativeDeepeningDepthFirstSearch<N> state = 
 new BidirectionalIterativeDeepeningDepthFirstSearch<>(
 source,
 forwardExpander,
 backwardExpander);
 for (int forwardDepth = 0;; ++forwardDepth) {
 state.visitedForward.clear();
 
 // Do a depth limited search in forward direction. Put all nodes at 
 // depth == 0 to the frontier.
 state.depthLimitedSearchForward(source, forwardDepth);
 
 if (state.visitedForward.size() == 
 state.previousVisitedSizeForward) {
 return null;
 }
 
 state.previousVisitedSizeForward = state.visitedForward.size();
 state.visitedForward.clear();
 
 for (int backwardDepth = forwardDepth;
 backwardDepth < forwardDepth + 2;
 backwardDepth++) {
 
 N meetingNode = state.depthLimitedSearchBackward(target,
 backwardDepth);
 if (meetingNode != null) {
 return state.buildPath(meetingNode);
 }
 if (state.visitedForward.size() == 
 state.previousVisitedSizeForward) {
 return null;
 }
 state.previousVisitedSizeBackward =
 state.visitedBackward.size();
 
 state.visitedBackward.clear();
 }
 
 state.frontier.clear();
 }
 }
 private void depthLimitedSearchForward(N node, int depth) {
 if (visitedForward.contains(node)) {
 return;
 }
 
 visitedForward.add(node);
 
 if (depth == 0) {
 frontier.add(node);
 return;
 }
 for (N child : forwardExpander.expand(node)) {
 depthLimitedSearchForward(child, depth - 1);
 }
 }
 private N depthLimitedSearchBackward(N node, int depth) {
 if (visitedBackward.contains(node)) {
 return null;
 }
 
 visitedBackward.add(node); 
 backwardSearchStack.addFirst(node);
 if (depth == 0) {
 if (frontier.contains(node)) {
 return node;
 }
 backwardSearchStack.removeFirst();
 return null;
 }
 for (N parent : backwardExpander.expand(node)) {
 N meetingNode = depthLimitedSearchBackward(parent, depth - 1);
 if (meetingNode != null) {
 return meetingNode;
 } 
 }
 backwardSearchStack.removeFirst();
 return null;
 }
 private List<N> buildPath(N meetingNode) {
 List<N> path = new ArrayList<>();
 List<N> prefixPath = 
 new BidirectionalIterativeDeepeningDepthFirstSearch<N>()
 .search(source, 
 meetingNode, 
 forwardExpander, 
 backwardExpander);
 path.addAll(prefixPath);
 path.remove(path.size() - 1);
 path.addAll(backwardSearchStack);
 return path;
 }
}

io.github.coderodde.libid.impl.IterativeDeepeningAStar.java::

package io.github.coderodde.libid.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import io.github.coderodde.libid.IntHeuristicFunction;
import io.github.coderodde.libid.NodeExpander;
public final class IterativeDeepeningAStar<N> {
 private static final int RUNNING = 0;
 private static final int FOUND = 1;
 
 private final N target;
 private final IntHeuristicFunction<N> heuristicFunction;
 private final NodeExpander<N> expander;
 private final List<N> path;
 
 private int status = RUNNING;
 
 public IterativeDeepeningAStar() {
 this.target = null;
 this.heuristicFunction = null;
 this.expander = null;
 this.path = null;
 }
 
 private IterativeDeepeningAStar(
 N target,
 IntHeuristicFunction<N> heuristicFunction,
 NodeExpander<N> expander) {
 this.target = target;
 this.heuristicFunction = heuristicFunction;
 this.expander = expander;
 this.path = new ArrayList<>();
 }
 
 public List<N> search(N source, 
 N target, 
 NodeExpander<N> expander, 
 IntHeuristicFunction<N> heuristicFunction) {
 IterativeDeepeningAStar<N> state = 
 new IterativeDeepeningAStar<>(target,
 heuristicFunction, 
 expander);
 
 int bound = heuristicFunction.estimate(source, target);
 
 while (true) {
 int t = state.search(source, 0, bound);
 
 if (state.status == FOUND) {
 Collections.<N>reverse(state.path);
 return state.path;
 }
 
 if (t == Integer.MAX_VALUE) {
 throw new IllegalStateException("Target not reachable");
 }
 
 bound = t;
 }
 }
 
 private int search(N node, int g, int bound) {
 int f = g + heuristicFunction.estimate(node, target);
 
 if (f > bound) {
 return f;
 }
 
 if (node.equals(target)) {
 path.add(target);
 status = FOUND;
 return 0;
 }
 
 int min = Integer.MAX_VALUE;
 
 for (N child : expander.expand(node)) {
 int t = search(child, g + 1, bound);
 
 if (status == FOUND) {
 path.add(node);
 return 0;
 }
 
 min = Math.min(min, t);
 }
 
 return min;
 }
}

io.github.coderodde.libid.impl.IterativeDeepeningDepthFirstSearch.java::

package io.github.coderodde.libid.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import io.github.coderodde.libid.NodeExpander;
import io.github.coderodde.libid.demo.SlidingTilePuzzleNode;
public final class IterativeDeepeningDepthFirstSearch<N> {
 private final N target;
 private final List<N> path;
 private final NodeExpander<N> expander;
 private boolean found = false;
 
 public IterativeDeepeningDepthFirstSearch() {
 this.target = null;
 this.path = null;
 this.expander = null;
 }
 
 private IterativeDeepeningDepthFirstSearch(N target, 
 NodeExpander<N> expander) {
 this.target = Objects.requireNonNull(target, "target is null");
 this.path = new ArrayList<>();
 this.expander = expander;
 }
 
 public List<N> search(N source, N target, NodeExpander<N> expander) {
 IterativeDeepeningDepthFirstSearch state = 
 new IterativeDeepeningDepthFirstSearch(target, expander);
 
 for (int depth = 0;; ++depth) {
 state.depthLimitedSearch(source, depth);
 
 if (state.found) {
 Collections.<SlidingTilePuzzleNode>reverse(state.path);
 return state.path;
 }
 }
 }
 
 private void depthLimitedSearch(N node, int depth) {
 if (depth == 0 && node.equals(target)) {
 found = true;
 path.add(node);
 return;
 }
 
 if (depth > 0) {
 for (N child : expander.expand(node)) {
 depthLimitedSearch(child, depth - 1);
 
 if (found) {
 path.add(node);
 return;
 }
 }
 }
 }
}

Outro

BidirectionalIterativeDeepeningDepthFirstSearch performs best on most benchmark batteries.

Critique request

As always, I am eager to hear any constructive commentary on my work.

asked Jun 21 at 5:57
\$\endgroup\$
1
  • \$\begingroup\$ buildPath() method creates a new instance with null fields and calls search() on it, which will cause infinite recursion or null pointer exceptions. I suggest you to fix that. \$\endgroup\$ Commented Jun 21 at 23:00

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.