package sample;
import rescuecore2.worldmodel.EntityID;
import rescuecore2.worldmodel.Entity;
import rescuecore2.misc.collections.LazyMap;
import java.util.List;
import java.util.LinkedList;
import java.util.Arrays;
import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
import java.util.Collection;
import rescuecore2.standard.entities.StandardWorldModel;
import rescuecore2.standard.entities.Area;
/**
A sample search class that uses a connection graph to look up neighbours.
*/
public final class SampleSearch {
private Map<EntityID, Set<EntityID>> graph;
/**
Construct a new SampleSearch.
@param world The world model to construct the neighbourhood graph from.
*/
public SampleSearch(StandardWorldModel world) {
Map<EntityID, Set<EntityID>> neighbours = new LazyMap<EntityID, Set<EntityID>>() {
@Override
public Set<EntityID> createValue() {
return new HashSet<EntityID>();
}
};
for (Entity next : world) {
if (next instanceof Area) {
Collection<EntityID> areaNeighbours = ((Area)next).getNeighbours();
neighbours.get(next.getID()).addAll(areaNeighbours);
}
}
setGraph(neighbours);
}
/**
Construct a new ConnectionGraphSearch.
@param graph The connection graph in the form of a map from EntityID to the set of neighbouring EntityIDs.
*/
public SampleSearch(Map<EntityID, Set<EntityID>> graph) {
setGraph(graph);
}
/**
Set the neighbourhood graph.
@param newGraph The new neighbourhood graph.
*/
public void setGraph(Map<EntityID, Set<EntityID>> newGraph) {
this.graph = newGraph;
}
/**
Get the neighbourhood graph.
@return The neighbourhood graph.
*/
public Map<EntityID, Set<EntityID>> getGraph() {
return graph;
}
/**
Do a breadth first search from one location to the closest (in terms of number of nodes) of a set of goals.
@param start The location we start at.
@param goals The set of possible goals.
@return The path from start to one of the goals, or null if no path can be found.
*/
public List<EntityID> breadthFirstSearch(EntityID start, EntityID... goals) {
return breadthFirstSearch(start, Arrays.asList(goals));
}
/**
Do a breadth first search from one location to the closest (in terms of number of nodes) of a set of goals.
@param start The location we start at.
@param goals The set of possible goals.
@return The path from start to one of the goals, or null if no path can be found.
*/
public List<EntityID> breadthFirstSearch(EntityID start, Collection<EntityID> goals) {
List<EntityID> open = new LinkedList<EntityID>();
Map<EntityID, EntityID> ancestors = new HashMap<EntityID, EntityID>();
open.add(start);
EntityID next = null;
boolean found = false;
ancestors.put(start, start);
do {
next = open.remove(0);
if (isGoal(next, goals)) {
found = true;
break;
}
Collection<EntityID> neighbours = graph.get(next);
if (neighbours.isEmpty()) {
continue;
}
for (EntityID neighbour : neighbours) {
if (isGoal(neighbour, goals)) {
ancestors.put(neighbour, next);
next = neighbour;
found = true;
break;
}
else {
if (!ancestors.containsKey(neighbour)) {
open.add(neighbour);
ancestors.put(neighbour, next);
}
}
}
} while (!found && !open.isEmpty());
if (!found) {
// No path
return null;
}
// Walk back from goal to start
EntityID current = next;
List<EntityID> path = new LinkedList<EntityID>();
do {
path.add(0, current);
current = ancestors.get(current);
if (current == null) {
throw new RuntimeException("Found a node with no ancestor! Something is broken.");
}
} while (current != start);
return path;
}
private boolean isGoal(EntityID e, Collection<EntityID> test) {
return test.contains(e);
}
}