Route Finding Code
>> Tuesday, July 27, 2010
I uploaded the full source-code for the Route Finding Problem and the Touring Problem.
Source Code (box.net)
I uploaded the full source-code for the Route Finding Problem and the Touring Problem.
Source Code (box.net)
Today I'm going to look at implementing a simple problem-solving agent using Java and Groovy. This is based on chapter 3 in my old A.I. textbook, "Solving Problems By Searching." I am going to tackle the route-finding problem which takes a number of cities in Romania (p 68 on Google Books) with paths between them with the goal of having to find the shortest route between any two cities.
Let's get started. First, I created a few simple Java classes to represent a Node (a city), an Action (go from one node to another), a Path (list of Actions that have gotten me where I am now), and a State (what Node am I at and how did I get here). These classes are not that interesting, but here are the basics of each one without the implementations:
Node:
public class Node {
public Node(String nodeName, List availableActions) { }
public String getNodeName() { }
public List getAvailableActions() { }
}
public class Action {
public Action(String actionName, int stepCost) { }
public Node resultOfAction(List availableNodes) {
//if I execute this action, where am I?
}
public int getStepCost() { }
}
public class Path {
public void addAction(Action action) { }
public int getPathCost() { }
public List<action> getActions() { }
public Path clone() { }
}
public class State {
public State(Node currentNode, Path currentPath, List closedNodes){ }
public Node getCurrentNode() { }
public Path getCurrentPath() { }
public List getClosedNodes() { }
}
public Path solve(final Node initialNode, final Node goalNode) {
if (initialNode.getNodeName().equalsIgnoreCase(goalNode.getNodeName())) {
resultPath = new Path(new Action("Stay" + initialNode.getNodeName(), 0));
} else {
resultPath = null;
lowestPathCost = Integer.MAX_VALUE;
// find the paths
doSearch(initialNode, goalNode);
}
return resultPath;
}
private void doSearch(final Node initialNode, final Node goalNode) {
stateDataStructure = new SimpleStack<State>();
// create the first state
stateDataStructure
.put(new State(initialNode, new Path(), new ArrayList<node>()));
while (!stateDataStructure.isEmpty()) {
if (goalTest(stateDataStructure.peek(), goalNode)) {
handleGoalReached(stateDataStructure.get());
} else {
successorFunction(stateDataStructure.get(), goalNode);
}
}
}
private void successorFunction(final State currentState, final Node goalNode) {
// make a copy of the closed list for each sub state
final List<Node> cloneClosedList = cloneClosedList(currentState
.getClosedNodes());
cloneClosedList.add(currentState.getCurrentNode());
// for each action after this, add it to the queue
for (Action nextAction : currentState.getCurrentNode()
.getAvailableActions()) {
final Node nextNode = nextAction.resultOfAction(nodes);
// make sure I haven't already checked this node (circular)
if (!cloneClosedList.contains(nextNode)) {
final Path clonePath = currentState.getCurrentPath().clone();
clonePath.addAction(nextAction);
// only add the next state if the current path is
// lower than any previous paths
if (clonePath.getPathCost() < lowestPathCost) {
stateDataStructure.put(new State(nextNode, clonePath,
cloneClosedList));
}
}
}
}
createActionList = {actionMap ->
List<Action> actions = new ArrayList<Action>();
actionMap.each {place,cost -> actions.add(new Action("Go(" + place + ")", cost)) };
return actions;
}
createNode = {nodeName, actionMap ->
return new Node("In(" + nodeName + ")", createActionList(actionMap));
}
findNode = {nodeList, nodeToFind ->
for(nextNode in nodeList){
if(nextNode != null && nextNode.getNodeName().equalsIgnoreCase("In(" + nodeToFind + ")")){
return nextNode;
}
}
}
Update:See this post about defining Segments instead of Nodes.
nodes = [
createNode("Arad", ["Zerind":75, "Sibiu":140, "Timisoara":118]),
createNode("Bucharest", ["Pitesti":101, "Fagaras":211, "Giurgiu":90, "Urziemi":85]),
createNode("Craiova", ["Drobeta":120, "Rimnieu Vilcea":146, "Pitesti":138]),
createNode("Drobeta", ["Mehadia":75, "Craiova":120]),
createNode("Eforie", ["Hirsova":86]),
createNode("Fagaras", ["Sibiu":99, "Bucharest":211]),
createNode("Giurgiu", ["Bucharest":90]),
createNode("Hirsova", ["Urziemi":98, "Eforie":86]),
createNode("Iasi", ["Vaslui":92, "Neamt":87]),
createNode("Lugoj", ["Timisoara":111, "Mehadia":70]),
createNode("Mehadia", ["Lugoj":70, "Drobeta":75]),
createNode("Neamt", ["Iasi":87]),
createNode("Oradea", ["Zerind":71, "Sibiu":151]),
createNode("Pitesti", ["Rimnieu Vilcea":97, "Craiova":138, "Bucharest":101]),
createNode("Rimnieu Vilcea", ["Sibiu":80, "Craiova":146, "Pitesti":97]),
createNode("Sibiu", ["Oradea":151, "Arad": 140, "Fagaras":999, "Rimnieu Vilcea":80]),
createNode("Timisoara", ["Arad":118, "Lugoj":70]),
createNode("Urziemi", ["Bucharest":85, "Vaslui":142, "Hirsova":98]),
createNode("Vaslui", ["Urziemi":142, "Iasi":92]),
createNode("Zerind", ["Oradea":71, "Arad":75])
];
RouteFindingProblem problem = new RouteFindingProblem(nodes);
//same initial/goal
assert 0 == (problem.solve(findNode(nodes, "Arad"), findNode(nodes, "Arad"))).getPathCost();
//neighbors
assert 75 == (problem.solve(findNode(nodes, "Arad"), findNode(nodes, "Zerind"))).getPathCost();
assert 75 == (problem.solve(findNode(nodes, "Zerind"), findNode(nodes, "Arad"))).getPathCost();
//2 hops
assert 215 == (problem.solve(findNode(nodes, "Zerind"), findNode(nodes, "Sibiu"))).getPathCost();
//one end to the other
assert 698 == (problem.solve(findNode(nodes, "Oradea"), findNode(nodes, "Eforie"))).getPathCost();
Path resultPath = problem.solve(findNode(nodes, "Oradea"), findNode(nodes, "Eforie"));
//display the results
println("Shortest path from " + findNode(nodes, "Oradea").toString() + " to " + findNode(nodes, "Eforie").toString());
for (Action nextAction : resultPath.getActions()) {
println(nextAction.toString());
}
Shortest path from In(Oradea) to In(Eforie)
Go(Sibiu)(151)
Go(Rimnieu Vilcea)(80)
Go(Pitesti)(97)
Go(Bucharest)(101)
Go(Urziemi)(85)
Go(Hirsova)(98)
Go(Eforie)(86)
![]() | I am a software developer working on the server-side (groovy/grails) of a cool platform that will change the world! Email me: geekmattblog@gmail.com |
© Blogger template Webnolia by Ourblogtemplates.com 2009
Back to TOP