View Javadoc

1   package nl.tudelft.goal.ut2004.floydwarshall;
2   
3   
4   import java.util.ArrayList;
5   import java.util.HashMap;
6   import java.util.List;
7   import java.util.Map;
8   import java.util.logging.Level;
9   import java.util.logging.Logger;
10  
11  import javax.vecmath.Point3d;
12  
13  import cz.cuni.amis.pogamut.base.agent.navigation.IPathFuture;
14  import cz.cuni.amis.pogamut.base.agent.navigation.IPathPlanner;
15  import cz.cuni.amis.pogamut.base.agent.navigation.impl.PrecomputedPathFuture;
16  import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
17  import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.NavigationGraphBuilder;
18  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPoint;
19  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPointNeighbourLink;
20  import cz.cuni.amis.pogamut.ut2004.communication.translator.shared.events.MapPointListObtained;
21  import cz.cuni.amis.pogamut.ut2004.utils.LinkFlag;
22  import cz.cuni.amis.utils.collections.MyCollections;
23  
24  /**
25   * Private map using Floyd-Warshall for path-finding.
26   * <p><p>
27   * It should be initialized upon receiving {@link MapPointListObtained} event.
28   * It precomputes all the paths inside the environment using Floyd-Warshall
29   * algorithm (O(n^3)). Use getReachable(), getDistance(), getPath() to obtain
30   * the info about the path.
31   * <p><p>
32   * If needed you may call {@link FloydWarshallMap#refreshPathMatrix()} to recompute Floyd-Warshall. Especially useful
33   * if you're using {@link NavigationGraphBuilder} to modify the underlying navpoints/edges.
34   * <p><p>
35   * Based upon the implementation from Martin Krulis with his kind consent -
36   * Thank you!
37   * <p><p>
38   * NOTE: requires O(navpoints.size^3) of memory ~ which is 10000^3 at max for
39   * UT2004 (usually the biggest maps have 3000 navpoints max, but small maps, e.g., DM-1on1-Albatross has 200 navpoints).
40   * <p><p>
41   * 
42   * @author Martin Krulis
43   * @author Jimmy
44   * @author mpkorstanje
45   */
46  public class FloydWarshallMap implements IPathPlanner<NavPoint> {
47  
48  	public static class PathMatrixNode {
49  
50  		private float distance = Float.POSITIVE_INFINITY;
51  		private Integer viaNode = null;
52  		private List<NavPoint> path = null;
53  
54  		/**
55  		 * Doesn't leading anywhere
56  		 */
57  		public PathMatrixNode() {
58  		}
59  
60  		public PathMatrixNode(float distance) {
61  			this.distance = distance;
62  		}
63  
64  		public float getDistance() {
65  			return distance;
66  		}
67  
68  		public void setDistance(float distance) {
69  			this.distance = distance;
70  		}
71  
72  		/**
73  		 * Returns indice.
74  		 * 
75  		 * @return
76  		 */
77  		public Integer getViaNode() {
78  			return viaNode;
79  		}
80  
81  		public void setViaNode(Integer indice) {
82  			this.viaNode = indice;
83  		}
84  
85  		public List<NavPoint> getPath() {
86  			return path;
87  		}
88  
89  		public void setPath(List<NavPoint> path) {
90  			this.path = path;
91  		}
92  
93  	}
94  
95  	/**
96  	 * Flag mask representing unusable edge.
97  	 */
98  	public static final int BAD_EDGE_FLAG = LinkFlag.FLY.get()
99  			| LinkFlag.LADDER.get() | LinkFlag.PROSCRIBED.get()
100 			| LinkFlag.SWIM.get() | LinkFlag.PLAYERONLY.get();
101 	
102 	public static boolean isWalkable(int flag) {
103 		return ((flag & BAD_EDGE_FLAG) == 0) && ((flag & LinkFlag.SPECIAL.get()) == 0); 
104 	}
105 
106 	/**
107 	 * Prohibited edges.
108 	 */
109 	protected int badEdgeFlag = 0;
110 
111 	/**
112 	 * Hash table converting navPoint IDs to our own indices.
113 	 */
114 	protected Map<UnrealId, Integer> navPointIndices;
115 
116 	/**
117 	 * Mapping indices to nav points.
118 	 * <p>
119 	 * <p>
120 	 * WILL BE NULL AFTER CONSTRUCTION! SERVES AS A TEMPORARY "GLOBAL VARIABLE"
121 	 * DURING FLOYD-WARSHALL COMPUTATION AND PATH RECONSTRUCTION.
122 	 */
123 	protected Map<Integer, NavPoint> indicesNavPoints;
124 
125 	// Warshall's matrix of distances.
126 	protected PathMatrixNode[][] pathMatrix;
127 	
128 	/**
129 	 * Synchronizing access to object with respect to {@link FloydWarshallMap#enabled}.
130 	 */
131 	protected Object mutex = new Object();
132 
133 	/**
134 	 * Log for logging
135 	 */
136 	private Logger log;
137 
138 	public FloydWarshallMap(Logger log) {
139 		this(BAD_EDGE_FLAG, log);
140 	}
141 
142 	public FloydWarshallMap(int badEdgeFlag, Logger log) {
143 		this.badEdgeFlag = badEdgeFlag;
144 		this.log = log;
145 	}
146 	
147 	
148 	/**
149 	 * Force FloydWarshall to run again, useful if you modify navpoints using {@link NavigationGraphBuilder}.
150 	 */
151 	public void refreshPathMatrix(List<NavPoint> navPoints) {
152 		synchronized(mutex) {
153 			if (log.isLoggable(Level.FINE)) log.fine(this + ": Refreshing path matrix...");
154 			performFloydWarshall(navPoints);
155 			if (log.isLoggable(Level.WARNING)) log.warning(this + ": Path matrix refreshed!");
156 		}		
157 	}
158 	
159 	protected void performFloydWarshall(MapPointListObtained map) {
160 		List<NavPoint> navPoints = MyCollections.asList(map.getNavPoints().values());
161 		performFloydWarshall(navPoints);
162 	}
163 
164 	protected void performFloydWarshall(List<NavPoint> navPoints) {
165 		if (log.isLoggable(Level.FINE)) log.fine(this + ": Beginning Floyd-Warshall algorithm...");
166 		long start = System.currentTimeMillis();
167 
168 		// prepares data structures
169 		int size = navPoints.size();
170 		navPointIndices = new HashMap<UnrealId, Integer>(size);
171 		indicesNavPoints = new HashMap<Integer, NavPoint>(size);
172 		pathMatrix = new PathMatrixNode[size][size];
173 
174 		// Initialize navPoint indices mapping.
175 		for (int i = 0; i < navPoints.size(); ++i) {
176 			navPointIndices.put(navPoints.get(i).getId(), i);
177 			indicesNavPoints.put(i, navPoints.get(i));
178 		}
179 
180 		// Initialize distance matrix.
181 		for (int i = 0; i < size; i++) {
182 			for (int j = 0; j < size; j++) {
183 				pathMatrix[i][j] = new PathMatrixNode((i == j) ? 0.0f
184 						: Float.POSITIVE_INFINITY);
185 			}
186 		}
187 
188 		// Set edge lengths into distance matrix.
189 		for (int i = 0; i < size; i++) {
190 			Point3d location = navPoints.get(i).getLocation().getPoint3d();
191 			for (NavPointNeighbourLink link : navPoints.get(i)
192 					.getOutgoingEdges().values()) {
193 				if (checkLink(link)) {
194 					pathMatrix[i][navPointIndices.get(link.getToNavPoint()
195 							.getId())].setDistance((float) location
196 							.distance(link.getToNavPoint().getLocation()
197 									.getPoint3d()));
198 				}
199 			}
200 		}
201 
202 		// Perform Floyd-Warshall.
203 		for (int k = 0; k < size; k++) {
204 			for (int i = 0; i < size; i++) {
205 				for (int j = 0; j < size; j++) {
206 					float newLen = pathMatrix[i][k].getDistance()
207 							+ pathMatrix[k][j].getDistance();
208 					if (pathMatrix[i][j].getDistance() > newLen) {
209 						pathMatrix[i][j].setDistance(newLen);
210 						pathMatrix[i][j].setViaNode(k);
211 					}
212 				}
213 			}
214 		}
215 
216 		// Construct paths + log unreachable paths.
217 		int count = 0;
218 		for (int i = 0; i < size; i++) {
219 			for (int j = 0; j < size; j++) {
220 				if (pathMatrix[i][j].getDistance() == Float.POSITIVE_INFINITY) {
221 					if (log.isLoggable(Level.FINER)) log.finer("Unreachable path from " + navPoints.get(i).getId().getStringId()
222 							+ " -> " + navPoints.get(j).getId().getStringId());
223 					count++;
224 				} else {
225 					// path exists ... retrieve it
226 					pathMatrix[i][j].setPath(retrievePath(i, j));
227 				}
228 			}
229 		}
230 
231 		if (count > 0) {
232 			if (log.isLoggable(Level.WARNING)) log.warning(this + ": There are " + count + " unreachable nav point pairs (if you wish to see more, set logging to Level.FINER).");
233 		}
234 
235 		if (log.isLoggable(Level.INFO)) log.info(this + ": computation for " + size + " navpoints took " + (System.currentTimeMillis() - start) + " millis");
236 
237 		// null unneeded field to free some memory
238 		indicesNavPoints = null;
239 	}
240 
241 	/**
242 	 * Checks whether the edge is usable.
243 	 * 
244 	 * @param from
245 	 *            Starting nav point.
246 	 * @param edge
247 	 *            NeighNav object representing the edge.
248 	 * @return boolean
249 	 */
250 	public boolean checkLink(NavPointNeighbourLink edge) {
251 		// Bad flags (prohibited edges, swimming, flying...).
252 		if ((edge.getFlags() & badEdgeFlag) != 0)
253 			return false;
254 
255 		// Lift flags.
256 		if ((edge.getFlags() & LinkFlag.SPECIAL.get()) != 0)
257 			return true;
258 
259 		// Check whether the climbing angle is not so steep.
260 //		if ((edge.getFromNavPoint().getLocation().getPoint3d().distance(
261 //				edge.getToNavPoint().getLocation().getPoint3d()) < (edge
262 //				.getToNavPoint().getLocation().z - edge.getFromNavPoint()
263 //				.getLocation().z))
264 //				&& (edge.getFromNavPoint().getLocation().getPoint3d().distance(
265 //						edge.getToNavPoint().getLocation().getPoint3d()) > 100)) {
266 //			return false;
267 //		}
268 
269 		// Check whether the jump is not so high.
270 //		if (((edge.getFlags() & LinkFlag.JUMP.get()) != 0)
271 //				&& (edge.getToNavPoint().getLocation().z
272 //						- edge.getFromNavPoint().getLocation().z > 80)) {
273 //			return false;
274 //		}
275         //Check whether there is NeededJump attribute set - this means the bot has to 
276         //provide the jump himself - if the Z of the jump is too high it means he
277         //needs to rocket jump or ShieldGun jump - we will erase those links
278         //as our bots are not capable of this
279         if (edge.getNeededJump() != null && edge.getNeededJump().z > 680) {
280         	return false;
281         }
282         
283         //This is a navpoint that requires lift jump - our bots can't do this - banning the link!
284         if (edge.getToNavPoint().isLiftJumpExit()) {
285         	return false;        	        
286         }
287                 
288 		return true;
289 	}
290 
291 	/**
292 	 * Sub-routine of retrievePath - do not use! ... Well you may, it returns
293 	 * path without 'from', 'to' or null if such path dosn't exist.
294 	 * <p>
295 	 * <p>
296 	 * DO NOT USE OUTSIDE CONSTRUCTOR (relies on indicesNavPoints).
297 	 * 
298 	 * @param from
299 	 * @param to
300 	 * @return
301 	 */
302 	private List<NavPoint> retrievePathInner(Integer from, Integer to) {
303 		PathMatrixNode node = pathMatrix[from][to];
304 		if (node.getDistance() == Float.POSITIVE_INFINITY)
305 			return null;
306 		if (node.getViaNode() == null) {
307 			return new ArrayList<NavPoint>(0);
308 		}
309 		if (node.getViaNode() == null)
310 			return new ArrayList<NavPoint>(0);
311 
312 		List<NavPoint> path = new ArrayList<NavPoint>();
313 		path.addAll(retrievePathInner(from, node.getViaNode()));
314 		path.add(indicesNavPoints.get(node.getViaNode()));
315 		path.addAll(retrievePathInner(node.getViaNode(), to));
316 
317 		return path;
318 	}
319 
320 	/**
321 	 * Returns path between from-to or null if path doesn't exist. Path begins
322 	 * with 'from' and ends with 'to'.
323 	 * <p>
324 	 * <p>
325 	 * DO NOT USE OUTSIDE CONSTRUCTOR (relies on indicesNavPoints).
326 	 * 
327 	 * @param from
328 	 * @param to
329 	 * @return
330 	 */
331 	private List<NavPoint> retrievePath(Integer from, Integer to) {
332 		List<NavPoint> path = new ArrayList<NavPoint>();
333 		path.add(indicesNavPoints.get(from));
334 		path.addAll(retrievePathInner(from, to));
335 		path.add(indicesNavPoints.get(to));
336 		return path;
337 	}
338 
339 	protected PathMatrixNode getPathMatrixNode(NavPoint np1, NavPoint np2) {
340 		return pathMatrix[navPointIndices.get(np1.getId())][navPointIndices
341 				.get(np2.getId())];
342 	}
343 
344 	/**
345 	 * Whether navpoint 'to' is reachable from the navpoint 'from'.
346 	 * <p><p>
347 	 * Throws exception if object is disabled, see {@link FloydWarshallMap#setEnabled(boolean)}. Note that the object
348 	 * is enabled by default.
349 	 * 
350 	 * @param from
351 	 * @param to
352 	 * @return
353 	 */
354 	public boolean reachable(NavPoint from, NavPoint to) {
355 		if ((from == null) || (to == null))
356 			return false;
357 		return getPathMatrixNode(from, to).getDistance() != Float.POSITIVE_INFINITY;
358 	}
359 
360 	/**
361 	 * Calculate's distance between two nav points (using pathfinding).
362 	 * <p><p>
363 	 * Throws exception if object is disabled, see {@link FloydWarshallMap#setEnabled(boolean)}. Note that the object
364 	 * is enabled by default.
365 	 * 
366 	 * @return Distance or POSITIVE_INFINITY if there's no path.
367 	 */
368 	public float getDistance(NavPoint from, NavPoint to) {
369 		if ((from == null) || (to == null))
370 			return Float.POSITIVE_INFINITY;
371 		return getPathMatrixNode(from, to).getDistance();
372 	}
373 
374 	/**
375 	 * Returns path between navpoints 'from' -> 'to'. The path begins with
376 	 * 'from' and ends with 'to'. If such path doesn't exist, returns null.
377 	 * <p><p>
378 	 * Throws exception if object is disabled, see {@link FloydWarshallMap#setEnabled(boolean)}. Note that the object
379 	 * is enabled by default.
380 	 * 
381 	 * @param from
382 	 * @param to
383 	 * @return
384 	 */
385 	public List<NavPoint> getPath(NavPoint from, NavPoint to) {
386 		synchronized(mutex) {
387 			if ((from == null) || (to == null))
388 				return null;
389 			if (log.isLoggable(Level.FINE)) log.fine("Retrieving path: " + from.getId().getStringId() + "[" + from.getLocation() + "] -> " + to.getId().getStringId() + "[" + to.getLocation() + "]");
390 			List<NavPoint> path = getPathMatrixNode(from, to).getPath();
391 			if (path == null) {
392 				if (log.isLoggable(Level.WARNING)) log.warning("PATH NOT EXIST: " + from.getId().getStringId() + "[" + from.getLocation() + "] -> " + to.getId().getStringId() + "[" + to.getLocation() + "]");
393 			} else {
394 				if (log.isLoggable(Level.FINE)) log.fine("Path exists - " + path.size() + " navpoints.");
395 			}
396 			return path;
397 		}
398 	}
399 
400 	protected void cleanUp() {
401 		pathMatrix = null;
402 		navPointIndices = null;
403 	}
404 	
405 	@Override
406 	public String toString() {
407 		return "FloydWarshallMap";
408 	}
409 
410 	/**
411 	 * Returns path between navpoints 'from' -> 'to'. The path begins with
412 	 * 'from' and ends with 'to'. If such path does not exist, it returns zero-sized path.
413 	 * <p><p>
414 	 * Throws exception if object is disabled, see {@link FloydWarshallMap#setEnabled(boolean)}. Note that the object
415 	 * is enabled by default.
416 	 * 
417 	 * @param from
418 	 * @param to
419 	 * @return
420 	 */
421 	@Override
422 	public IPathFuture<NavPoint> computePath(NavPoint from, NavPoint to) {
423 		return new PrecomputedPathFuture<NavPoint>(from, to, getPath(from, to));
424 	}
425 	
426     
427 }