/* * Project Info: http://jcae.sourceforge.net * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * (C) Copyright 2014, by EADS France */ package org.jcae.mesh.amibe.util; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; /** * Implementation of A* algorithm * http://en.wikipedia.org/wiki/A*_search_algorithm * @author Jerome Robert */ public abstract class AStar<Node> { private static class Score<Node> implements Comparable<Score<Node>> { public Node node; /** the actual shortest distance traveled from initial node to current node */ public double g; /** the estimated (or "heuristic") distance from current node to goal */ public double h; /** the sum of g(x) and h(x) */ public double f; @Override public int compareTo(Score<Node> o) { if (f == o.f) { int thisVal = System.identityHashCode(this); int anotherVal = System.identityHashCode(o); return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1)); } else return Double.compare(f, o.f); } public Score(Node node) { this.node = node; } public Score(Node node, double g, double h, double f) { this.node = node; this.g = g; this.h = h; this.f = f; } @Override public String toString() { return "{ " +node.toString() + " g=" + g + " h=" + h + " f=" + f +" }"; } } protected abstract double heuristicDistance(Node n1, Node n2); /** Distance between 2 neighboor nodes */ protected abstract double distance(Node n1, Node n2); protected abstract Iterable<Node> neighborNodes(Node n); private double pathLength = Double.NaN; private List<Node> reconstructPath(Map<Node, Node> cameFrom, Node current) { ArrayList<Node> toReturn = new ArrayList<Node>(cameFrom.size()+1); while(current != null) { toReturn.add(current); current = cameFrom.get(current); } Collections.reverse(toReturn); return toReturn; } public List<Node> find(Node start, Node goal) { return find(start, goal, Integer.MAX_VALUE); } public List<Node> find(Node start, Node goal, int maxIter) { //The set of nodes already evaluated. Set<Node> closetSet = new HashSet<Node>(); //The set of tentative nodes to be evaluated. SortedSet<Score<Node>> openSet = new TreeSet<Score<Node>>(); Map<Node, Score<Node>> openMap = new HashMap<Node, Score<Node>>(); Map<Node, Node> cameFrom = new HashMap<Node, Node>(); double d = heuristicDistance(start, goal); Score<Node> ss = new Score<Node>(start, 0, d, d); openSet.add(ss); openMap.put(start, ss); int it = 0; Score<Node> x = null; while(!openSet.isEmpty()) { x = openSet.first(); if(x.node.equals(goal) || it > maxIter) { pathLength = x.g; return reconstructPath(cameFrom, x.node); } it++; openSet.remove(x); openMap.remove(x.node); closetSet.add(x.node); int cpt = 0; for(Node y:neighborNodes(x.node)) { cpt++; if(closetSet.contains(y)) continue; double tentativeGScore=x.g + distance(x.node, y); boolean tentativeIsBetter = false; Score<Node> sy = openMap.get(y); if(sy == null) { sy = new Score<Node>(y); openSet.add(sy); openMap.put(y, sy); sy.h = heuristicDistance(y, goal); tentativeIsBetter = true; } else if(tentativeGScore < sy.g) tentativeIsBetter = true; if(tentativeIsBetter) { cameFrom.put(y, x.node); sy.g = tentativeGScore; openSet.remove(sy); sy.f = sy.g + sy.h; openSet.add(sy); } } } if(goal == null) { pathLength = x.g; return reconstructPath(cameFrom, x.node); } else { pathLength = Double.NaN; return null; } } public double getPathLength() { return pathLength; } }