1 package cz.cuni.amis.pogamut.ut2004.agent.module.sensor;
2
3 import java.util.Collection;
4 import java.util.Collections;
5 import java.util.HashMap;
6 import java.util.Map;
7 import java.util.logging.Logger;
8
9 import cz.cuni.amis.pogamut.base.agent.module.SensorModule;
10 import cz.cuni.amis.pogamut.base.communication.worldview.IWorldView;
11 import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
12 import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEventListener;
13 import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectListener;
14 import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectUpdatedEvent;
15 import cz.cuni.amis.pogamut.base.utils.math.DistanceUtils;
16 import cz.cuni.amis.pogamut.base3d.worldview.object.ILocated;
17 import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
18 import cz.cuni.amis.pogamut.ut2004.bot.IUT2004BotController;
19 import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
20 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.GameInfo;
21 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPoint;
22 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Self;
23 import cz.cuni.amis.utils.collections.MyCollections;
24
25 /**
26 * Memory module specialized on getting {@link NavPoint}s from the {@link IWorldView}.
27 *
28 * <h2>Auto updating</h2>
29 *
30 * <p>All {@link NavPoint} objects returned by this memory module are always self-updating
31 * throughout the time, until the associated navPoint leaves the game. This means
32 * that once a valid NavPoint object is obtained, it is not necessary to call any
33 * methods of this memory module to get the object's info updated (e.g. navpoint's
34 * location, visibility, reachability, etc.). The object will autoupdate itself.
35 *
36 * <p>The same principle is applied to all Maps returned by this memory module.
37 * Each returned Map is self-updating throughout the time. Once a specific Map
38 * is obtained (e.g. a map of visible enemies) from this memory module, the Map
39 * will get updated based on actions of the navPoints (e.g. joining or leaving
40 * the game, changing their team, moving around the map, etc.) automatically.
41 *
42 * <p>Note: All Maps returned by this memory module are locked and can not be
43 * modified outside this memory module. If you need to modify a Map returned by
44 * this module (for your own specific purpose), create a duplicate first. Such
45 * duplicates, however and of course, will not get updated.
46 *
47 * <p><b>WARNING:</b>It is totally unclear what UT2004 means by reachable!!!
48 *
49 * <p><p>
50 * It is designed to be initialized inside {@link IUT2004BotController#prepareBot(UT2004Bot)} method call
51 * and may be used since {@link IUT2004BotController#botInitialized(cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.GameInfo, cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.ConfigChange, cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.InitedMessage)}
52 * is called.
53 *
54 * @author Jimmy
55 */
56 public class NavPoints extends SensorModule<UT2004Bot> {
57
58 /**
59 * Retreives last known info about given navPoint.
60 *
61 * <p>Note: The returned NavPoint object is self updating throughout time.
62 * Once you have a valid NavPoint object, you do not have to call this
63 * method to get updated info about that navPoint.
64 *
65 * <p>Note: If you have the string name only, you should use {@link #getNavPoint(String)} instead.
66 *
67 * @param UnrealId NavPoint UnrealId to be retreived.
68 * @return Last known navPoint info; or null upon none.
69 *
70 * @see getNavPoint(nam)
71 * @see getVisibleNavPoint(UnrealId)
72 * @see getReachableNavPoint(UnrealId)
73 */
74 public NavPoint getNavPoint(UnrealId UnrealId)
75 {
76 // retreive from map of all navPoints
77 return navPoints.all.get(UnrealId);
78 }
79
80 /**
81 * Retreives last known info about given navPoint.
82 *
83 * <p>Note: The returned NavPoint object is self updating throughout time.
84 * Once you have a valid NavPoint object, you do not have to call this
85 * method to get updated info about that navPoint.
86 *
87 * @param String NavPoint name to be retreived, must not need to be prefixed with the map name
88 * @return Last known navPoint info; or null upon none.
89 *
90 * @see getVisibleNavPoint(UnrealId)
91 * @see getReachableNavPoint(UnrealId)
92 */
93 public NavPoint getNavPoint(String name) {
94 NavPoint result = navPoints.all.get(UnrealId.get(name));
95 if (result != null) return result;
96 GameInfo info = worldView.getSingle(GameInfo.class);
97 if (info == null) return null;
98 return navPoints.all.get(UnrealId.get(info.getLevel() + "." + name));
99 }
100
101 /**
102 * Returns {@link NavPoint} that is the nearest to the bot.
103 *
104 * @return
105 */
106 public NavPoint getNearestNavPoint() {
107 if (lastSelf == null) return null;
108 return DistanceUtils.getNearest(navPoints.all.values(), lastSelf.getLocation());
109 }
110
111
112 /**
113 * Returns random navigation point.
114 * @return
115 */
116 public NavPoint getRandomNavPoint() {
117 return MyCollections.getRandom(navPoints.all.values());
118 }
119
120 /**
121 * Returns nearest {@link NavPoint} to the 'location'.
122 * @param location
123 * @return
124 */
125 public NavPoint getNearestNavPoint(ILocated location) {
126 if (location == null || location.getLocation() == null) return null;
127 return DistanceUtils.getNearest(navPoints.all.values(), location);
128 }
129
130 /**
131 * Retrieves info about given navPoint, but only it the navPoint is visible.
132 *
133 * <p>Note: The returned NavPoint object is self updating throughout time.
134 * Once you have a valid NavPoint object, you do not have to call this
135 * method to get updated info about visibility of that navPoint.
136 *
137 * @param UnrealId NavPoint UnrealId to be retrieved.
138 * @return NavPoint info; or null upon none or not visible.
139 *
140 * @see getNavPoint(UnrealId)
141 * @see getReachableNavPoint(UnrealId)
142 */
143 public NavPoint getVisibleNavPoint(UnrealId UnrealId)
144 {
145 // retreive from map of all visible navPoints
146 return navPoints.visible.get(UnrealId);
147 }
148
149 /*========================================================================*/
150
151 /**
152 * Retreives a Map of all navPoints.
153 *
154 * <p>Note: The returned Map is unmodifiable and self updating throughout
155 * time. Once you obtain a specific Map of navPoints from this memory module,
156 * the Map will get updated based on actions of the navPoints (e.g. joining
157 * or leaving the game, changing their status, etc.).
158 *
159 * @return Map of all navPoints, using their UnrealIds as keys.
160 *
161 * @see getEnemies()
162 * @see getFriends()
163 * @see getVisibleNavPoints()
164 * @see getReachableNavPoints()
165 */
166 public Map<UnrealId, NavPoint> getNavPoints()
167 {
168 // publish map of all navPoints
169 return Collections.unmodifiableMap(navPoints.all);
170 }
171
172 /*========================================================================*/
173
174 /**
175 * Retrieves a Map of all visible navPoints.
176 *
177 * <p>Note: The returned Map is unmodifiable and self updating throughout
178 * time. Once you obtain a specific Map of navPoints from this memory module,
179 * the Map will get updated based on actions of the navPoints (e.g. joining
180 * or leaving the game, or changing their visibility, etc.).
181 *
182 * @return Map of all visible navPoints, using their UnrealIds as keys.
183 *
184 * @see getNavPoints()
185 * @see getVisibleEnemies()
186 * @see getVisibleFriends()
187 * @see canSeeNavPoints()
188 */
189 public Map<UnrealId, NavPoint> getVisibleNavPoints()
190 {
191 // publish map of all visible navPoints
192 return Collections.unmodifiableMap(navPoints.visible);
193 }
194
195 /*========================================================================*/
196
197 /**
198 * Returns nearest navPoint that is visible or that was 'recently' visible. If no such navPoint exists, returns null.
199 *
200 * @param recently how long the navPoint may be non-visible. IN MILISECONDS!
201 * @return nearest visible or 'recentlyVisibleTime' visible navPoint
202 */
203 public NavPoint getRecentlyVisibleNavPoint(double recently) {
204 NavPoint nearest = null;
205 double distance = Double.MAX_VALUE;
206 for (NavPoint plr : navPoints.all.values()) {
207 if (plr.isVisible() || lastSelf.getSimTime() - plr.getSimTime() <= recently) {
208 double d = lastSelf.getLocation().getDistance(plr.getLocation());
209 if (d < distance) {
210 distance = d;
211 nearest = plr;
212 }
213 }
214 }
215 return nearest;
216 }
217
218 /**
219 * Returns nearest-visible navPoint - if no if no navPoint is visible returns null.
220 *
221 * @return nearest visible navPoint
222 */
223 public NavPoint getNearestVisibleNavPoint() {
224 return DistanceUtils.getNearest(navPoints.visible.values(), lastSelf.getLocation());
225 }
226
227 /**
228 * Returns nearest-visible navPoint to the bot from the collection of 'navPoints' - if no navPoint
229 * is visible returns null.
230 *
231 * @param navPoints collection to go through
232 * @return nearest visible navPoint from the collection
233 */
234 public NavPoint getNearestVisibleNavPoint(Collection<NavPoint> navPoints) {
235 return DistanceUtils.getNearestVisible(navPoints, lastSelf.getLocation());
236 }
237
238 /**
239 * Returns random visible navPoint - if no if no navPoint is visible returns null.
240 *
241 * @return random visible navPoint
242 */
243 public NavPoint getRandomVisibleNavPoint() {
244 return MyCollections.getRandom(navPoints.visible.values());
245 }
246
247 /*========================================================================*/
248
249 /**
250 * Tells, whether the agent sees any other navPoints.
251 *
252 * @return True, if at least one other navPoint is visible; false otherwise.
253 *
254 * @see getVisibleNavPoints()
255 */
256 public boolean canSeeNavPoints()
257 {
258 // search map of all visible navPoints
259 return (navPoints.visible.size() > 0);
260 }
261
262 /*========================================================================*/
263
264 /**
265 * Maps of navPoints of specific type.
266 */
267 private class NavPointMaps
268 {
269 /** Map of all navPoints of the specific type. */
270 private HashMap<UnrealId, NavPoint> all = new HashMap<UnrealId, NavPoint> ();
271 /** Map of visible navPoints of the specific type. */
272 private HashMap<UnrealId, NavPoint> visible = new HashMap<UnrealId, NavPoint> ();
273
274 /**
275 * Processes events.
276 * @param navPoint NavPoint to process.
277 */
278 private void notify(NavPoint navPoint)
279 {
280 UnrealId uid = navPoint.getId();
281
282 // be sure to be within all
283 if (!all.containsKey(uid))
284 all.put(uid, navPoint);
285
286 // previous visibility
287 boolean wasVisible = visible.containsKey(uid);
288 boolean isVisible = navPoint.isVisible();
289
290 // refresh visible
291 if (isVisible && !wasVisible)
292 {
293 // add to visibles
294 visible.put(uid, navPoint);
295 }
296 else if (!isVisible && wasVisible)
297 {
298 // remove from visibles
299 visible.remove(uid);
300 }
301
302 }
303
304 /**
305 * Removes navPoint from all maps.
306 * @param uid UnrealId of navPoint to be removed.
307 */
308 private void remove(UnrealId uid)
309 {
310 // remove from all maps
311 all.remove(uid);
312 visible.remove(uid);
313 }
314
315 private void clear() {
316 all.clear();
317 visible.clear();
318 }
319 }
320
321 /** Maps of all navPoints. */
322 private NavPointMaps navPoints = new NavPointMaps ();
323
324 /*========================================================================*/
325
326 /**
327 * NavPoint listener.
328 */
329 private class NavPointListener implements IWorldObjectEventListener<NavPoint, WorldObjectUpdatedEvent<NavPoint>>
330 {
331 @Override
332 public void notify(WorldObjectUpdatedEvent<NavPoint> event)
333 {
334 NavPoint navPoint = event.getObject();
335 // do the job in map of navPoints
336 navPoints.notify(navPoint);
337 }
338
339 /**
340 * Constructor. Registers itself on the given WorldView object.
341 * @param worldView WorldView object to listent to.
342 */
343 public NavPointListener(IWorldView worldView)
344 {
345 worldView.addObjectListener(NavPoint.class, WorldObjectUpdatedEvent.class, this);
346 }
347 }
348
349 /** NavPoint listener */
350 NavPointListener navPointListener;
351
352 /*========================================================================*/
353
354 /**
355 * Self listener.
356 */
357 private class SelfListener implements IWorldObjectListener<Self>
358 {
359 @Override
360 public void notify(IWorldObjectEvent<Self> event)
361 {
362 if (lastSelf == null || lastSelf.getTeam() != event.getObject().getTeam()) {
363 lastSelf = event.getObject();
364 } else {
365 lastSelf = event.getObject();
366 }
367 }
368
369 /**
370 * Constructor. Registers itself on the given WorldView object.
371 * @param worldView WorldView object to listent to.
372 */
373 public SelfListener(IWorldView worldView)
374 {
375 worldView.addObjectListener(Self.class, this);
376 }
377 }
378
379 /** Self listener */
380 SelfListener selfListener;
381
382 Self lastSelf = null;
383
384 /**
385 * Constructor. Setups the memory module based on bot's world view.
386 * @param bot owner of the module that is using it
387 */
388 public NavPoints(UT2004Bot bot)
389 {
390 this(bot, null);
391 }
392
393 /**
394 * Constructor. Setups the memory module based on bot's world view.
395 * @param bot owner of the module that is using it
396 * @param log Logger to be used for logging runtime/debug info. If <i>null</i>, module creates its own logger.
397 */
398 public NavPoints(UT2004Bot bot, Logger log)
399 {
400 super(bot, log);
401
402 // create listeners
403 navPointListener = new NavPointListener(worldView);
404 selfListener = new SelfListener(worldView);
405
406 cleanUp();
407 }
408
409 @Override
410 protected void cleanUp() {
411 super.cleanUp();
412 lastSelf = null;
413 navPoints.clear();
414 }
415
416 }