View Javadoc

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.event.IWorldEventListener;
12  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
13  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEventListener;
14  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectListener;
15  import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectUpdatedEvent;
16  import cz.cuni.amis.pogamut.base.utils.math.DistanceUtils;
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.Player;
21  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.PlayerLeft;
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 whereabouts of other players.
27   *
28   * <h2>Auto updating</h2>
29   *
30   * <p>All Player objects returned by this memory module are always self-updating
31   * throughout the time, until the associated player leaves the game. This means
32   * that once a valid Player 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. player'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 players (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>If you need to get info about players' deaths use {@link Senses} module.
48   * 
49   * <p><b>WARNING:</b>It is totally unclear what UT2004 means by reachable!!!
50   * 
51   * <p><p>
52   * It is designed to be initialized inside {@link IUT2004BotController#prepareBot(UT2004Bot)} method call
53   * 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)}
54   * is called.
55   *
56   *
57   * @author Juraj 'Loque' Simlovic
58   * @author Jimmy
59   */
60  public class Players extends SensorModule<UT2004Bot>
61  {
62  	/**
63  	 * Retreives last known info about given player.
64  	 *
65  	 * <p>Note: The returned Player object is self updating throughout time.
66  	 * Once you have a valid Player object, you do not have to call this
67  	 * method to get updated info about that player.
68  	 *
69  	 * @param UnrealId Player UnrealId to be retreived.
70  	 * @return Last known player info; or null upon none.
71  	 *
72  	 * @see getVisiblePlayer(UnrealId)
73  	 * @see getReachablePlayer(UnrealId)
74  	 */
75  	public Player getPlayer(UnrealId UnrealId)
76  	{
77  		// retreive from map of all players
78  		return players.all.get(UnrealId);
79  	}
80  
81  	/**
82  	 * Retreives info about given player, but only it the player is visible.
83  	 *
84  	 * <p>Note: The returned Player object is self updating throughout time.
85  	 * Once you have a valid Player object, you do not have to call this
86  	 * method to get updated info about visibility of that player.
87  	 *
88  	 * @param UnrealId Player UnrealId to be retrieved.
89  	 * @return Player info; or null upon none or not visible.
90  	 *
91  	 * @see getPlayer(UnrealId)
92  	 * @see getReachablePlayer(UnrealId)
93  	 */
94  	public Player getVisiblePlayer(UnrealId UnrealId)
95  	{
96  		// retreive from map of all visible players
97  		return players.visible.get(UnrealId);
98  	}
99  
100 	/*========================================================================*/
101 
102 	/**
103 	 * Retreives a Map of all players.
104 	 *
105 	 * <p>Note: The returned Map is unmodifiable and self updating throughout
106 	 * time. Once you obtain a specific Map of players from this memory module,
107 	 * the Map will get updated based on actions of the players (e.g. joining
108 	 * or leaving the game, changing their status, etc.).
109 	 *
110 	 * @return Map of all players, using their UnrealIds as keys.
111 	 *
112 	 * @see getEnemies()
113 	 * @see getFriends()
114 	 * @see getVisiblePlayers()
115 	 * @see getReachablePlayers()
116 	 */
117 	public Map<UnrealId, Player> getPlayers()
118 	{
119 		// publish map of all players
120 		return Collections.unmodifiableMap(players.all);
121 	}
122 
123 	/**
124 	 * Retreives a Map of all enemies.
125 	 *
126 	 * <p>Note: The returned Map is unmodifiable and self updating throughout
127 	 * time. Once you obtain a specific Map of enemies from this memory module,
128 	 * the Map will get updated based on actions of the players (e.g. joining
129 	 * or leaving the game, changing their team or status, etc.).
130 	 *
131 	 * @return Map of all enemies, using their UnrealIds as keys.
132 	 *
133 	 * @see getPlayers()
134 	 * @see getFriends()
135 	 * @see getVisibleEnemies()
136 	 * @see getReachableEnemies()
137 	 */
138 	public Map<UnrealId, Player> getEnemies()
139 	{
140 		// publish map of all enemies
141 		return Collections.unmodifiableMap(enemies.all);
142 	}
143 
144 	/**
145 	 * Retreives a Map of all friends.
146 	 *
147 	 * <p>Note: The returned Map is unmodifiable and self updating throughout
148 	 * time. Once you obtain a specific Map of friends from this memory module,
149 	 * the Map will get updated based on actions of the players (e.g. joining
150 	 * or leaving the game, changing their team or status, etc.).
151 	 *
152 	 * @return Map of all friends, using their UnrealIds as keys.
153 	 *
154 	 * @see getPlayers()
155 	 * @see getEnemies()
156 	 * @see getVisibleFriends()
157 	 * @see getReachableFriends()
158 	 */
159 	public Map<UnrealId, Player> getFriends()
160 	{
161 		// publish map of all friends
162 		return Collections.unmodifiableMap(friends.all);
163 	}
164 
165 	/*========================================================================*/
166 
167 	/**
168 	 * Retreives a Map of all visible players.
169 	 *
170 	 * <p>Note: The returned Map is unmodifiable and self updating throughout
171 	 * time. Once you obtain a specific Map of players from this memory module,
172 	 * the Map will get updated based on actions of the players (e.g. joining
173 	 * or leaving the game, or changing their visibility, etc.).
174 	 *
175 	 * @return Map of all visible players, using their UnrealIds as keys.
176 	 *
177 	 * @see getPlayers()
178 	 * @see getVisibleEnemies()
179 	 * @see getVisibleFriends()
180 	 * @see canSeePlayers()
181 	 */
182 	public Map<UnrealId, Player> getVisiblePlayers()
183 	{
184 		// publish map of all visible players
185 		return Collections.unmodifiableMap(players.visible);
186 	}
187 
188 	/**
189 	 * Retreives a Map of all visible enemies.
190 	 *
191 	 * <p>Note: The returned Map is unmodifiable and self updating throughout
192 	 * time. Once you obtain a specific Map of enemies from this memory module,
193 	 * the Map will get updated based on actions of the players (e.g. joining
194 	 * or leaving the game, changing their team, status or visibility, etc.).
195 	 *
196 	 * @return Map of all visible enemies, using their UnrealIds as keys.
197 	 *
198 	 * @see getEnemies()
199 	 * @see getVisiblePlayers()
200 	 * @see getVisibleFriends()
201 	 * @see canSeeEnemies()
202 	 */
203 	public Map<UnrealId, Player> getVisibleEnemies()
204 	{
205 		// publish map of all visible enemies
206 		return Collections.unmodifiableMap(enemies.visible);
207 	}
208 
209 	/**
210 	 * Retreives a Map of all visible friends.
211 	 *
212 	 * <p>Note: The returned Map is unmodifiable and self updating throughout
213 	 * time. Once you obtain a specific Map of friends from this memory module,
214 	 * the Map will get updated based on actions of the players (e.g. joining
215 	 * or leaving the game, changing their team, status or visibility, etc.).
216 	 *
217 	 * @return Map of all visible friends, using their UnrealIds as keys.
218 	 *
219 	 * @see getFriends()
220 	 * @see getVisiblePlayers()
221 	 * @see getVisibleEnemies()
222 	 * @see canSeeFriends()
223 	 */
224 	public Map<UnrealId, Player> getVisibleFriends()
225 	{
226 		// publish map of all visible friends
227 		return Collections.unmodifiableMap(friends.visible);
228 	}
229 
230 	/*========================================================================*/
231 
232 	/**
233 	 * Returns nearest player that is visible or that was 'recently' visible. If no such player exists, returns null.
234 	 * 
235 	 * @param recently how long the player may be non-visible. IN MILISECONDS!
236 	 * @return nearest visible or 'recentlyVisibleTime' visible player
237 	 */
238 	public Player getNearestPlayer(double recently) {	
239 		Player nearest = null;
240 		double distance = Double.MAX_VALUE;
241 		for (Player plr : players.all.values()) {
242 			if (plr.isVisible() || lastSelf.getSimTime() - plr.getSimTime() <= recently) {
243 				double d = lastSelf.getLocation().getDistance(plr.getLocation());
244 				if (d < distance) {
245 					distance = d;
246 					nearest = plr;
247 				}
248 			}
249 		}
250 		return nearest;
251 	}
252 	
253 	/**
254 	 * Returns nearest enemy that is visible or that was 'recently' visible. If no such enemy exists, returns null.
255 	 * 
256 	 * @param recentlyVisibleTime how long the player may be non-visible IN MILISECONDS!
257 	 * @return nearest visible or 'recently' visible enemy
258 	 */
259 	public Player getNearestEnemy(double recentlyVisibleTime) {	
260 		Player nearest = null;
261 		double distance = Double.MAX_VALUE;
262 		for (Player plr : enemies.all.values()) {
263 			if (plr.isVisible() || lastSelf.getSimTime() - plr.getSimTime() <= recentlyVisibleTime) {
264 				double d = lastSelf.getLocation().getDistance(plr.getLocation());
265 				if (d < distance) {
266 					distance = d;
267 					nearest = plr;
268 				}
269 			}
270 		}
271 		return nearest;
272 	}
273 	
274 	/**
275 	 * Returns nearest friend that is visible or that was 'recently' visible. If no such friend exists, returns null.
276 	 * 
277 	 * @param recentlyVisibleTime how long the player may be non-visible IN MILISECONDS!
278 	 * @return nearest visible or 'recently' visible friend
279 	 */
280 	public Player getNearestFriend(double recentlyVisibleTime) {	
281 		Player nearest = null;
282 		double distance = Double.MAX_VALUE;
283 		for (Player plr : friends.all.values()) {
284 			if (plr.isVisible() || lastSelf.getSimTime() - plr.getSimTime() <= recentlyVisibleTime) {
285 				double d = lastSelf.getLocation().getDistance(plr.getLocation());
286 				if (d < distance) {
287 					distance = d;
288 					nearest = plr;
289 				}
290 			}
291 		}
292 		return nearest;
293 	}
294 	
295 	/**
296 	 * Returns nearest-visible player - if no if no player is visible returns null.
297 	 * 
298 	 * @return nearest visible player
299 	 */
300 	public Player getNearestVisiblePlayer() {		
301         return DistanceUtils.getNearest(players.visible.values(), lastSelf.getLocation());
302 	}
303 	
304 	/**
305 	 * Returns nearest-visible enemy - if no enemy is visible returns null.
306 	 * 
307 	 * @return nearest visible enemy
308 	 */
309 	public Player getNearestVisibleEnemy() {		
310         return DistanceUtils.getNearest(enemies.visible.values(), lastSelf.getLocation());
311 	}
312 	
313 	/**
314 	 * Returns nearest-visible friend - if no friend is visible returns null.
315 	 * 
316 	 * @return nearest visible friend
317 	 */
318 	public Player getNearestVisibleFriend() {		
319         return DistanceUtils.getNearest(friends.visible.values(), lastSelf.getLocation());
320 	}
321 	
322 	/**
323 	 * Returns nearest-visible player to the bot from the collection of 'players' - if no player
324 	 * is visible  returns null.
325 	 * 
326 	 * @param players collection to go through
327 	 * @return nearest visible player from the collection
328 	 */
329 	public Player getNearestVisiblePlayer(Collection<Player> players) {		
330         return DistanceUtils.getNearestVisible(players, lastSelf.getLocation());
331 	}
332 	
333 	/**
334 	 * Returns random visible player - if no if no player is visible returns null.
335 	 * 
336 	 * @return random visible player
337 	 */
338 	public Player getRandomVisiblePlayer() {		
339         return MyCollections.getRandom(players.visible.values());
340 	}
341 	
342 	/**
343 	 * Returns random visible enemy - if no enemy is visible returns null.
344 	 * 
345 	 * @return random visible enemy
346 	 */
347 	public Player getRandomVisibleEnemy() {		
348         return MyCollections.getRandom(enemies.visible.values());
349 	}
350 	
351 	/**
352 	 * Returns random friend - if no friend is visible returns null.
353 	 * 
354 	 * @return random visible friend
355 	 */
356 	public Player getRandomVisibleFriend() {		
357         return MyCollections.getRandom(friends.visible.values());
358 	}
359 
360 	/*========================================================================*/
361 
362 	/**
363 	 * Tells, whether the agent sees any other players.
364 	 *
365 	 * @return True, if at least one other player is visible; false otherwise.
366 	 *
367 	 * @see getVisiblePlayers()
368 	 */
369 	public boolean canSeePlayers()
370 	{
371 		// search map of all visible players
372 		return (players.visible.size() > 0);
373 	}
374 
375 	/**
376 	 * Tells, whether the agent sees any other enemies.
377 	 *
378 	 * @return True, if at least one other enemy is visible; false otherwise.
379 	 *
380 	 * @see getVisibleEnemies()
381 	 */
382 	public boolean canSeeEnemies()
383 	{
384 		// search map of all visible enemies
385 		return (enemies.visible.size() > 0);
386 	}
387 
388 	/**
389 	 * Tells, whether the agent sees any other friends.
390 	 *
391 	 * @return True, if at least one other friend is visible; false otherwise.
392 	 *
393 	 * @see getVisibleFriends()
394 	 */
395 	public boolean canSeeFriends()
396 	{
397 		// search map of all visible friends
398 		return (friends.visible.size() > 0);
399 	}
400 
401 	/*========================================================================*/
402 
403 	/**
404 	 * Tells, whether a given team is an enemy team to the agent.
405 	 *
406 	 * @param team Team number to be tested.
407 	 * @return True, if the given team is an enemy team.
408 	 *
409 	 * @see getTeam()
410 	 * @see isFriend(int)
411 	 */
412 	public boolean isEnemy(int team)
413 	{
414 		// freelancers' team or different team
415 		return (team == AgentInfo.TEAM_NONE) || (team != lastSelf.getTeam());
416 	}
417 
418 	/**
419 	 * Tells, whether a given player is an enemy to the agent.
420 	 *
421 	 * @param player Player to be tested.
422 	 * @return True, if the given player is an enemy.
423 	 *
424 	 * @see getTeam()
425 	 * @see isFriend(Player)
426 	 */
427 	public boolean isEnemy(Player player)
428 	{
429 		// test the enemy team number
430 		return isEnemy(player.getTeam());
431 	}
432 
433 	/**
434 	 * Tells, whether a given team is a friend team to the agent.
435 	 *
436 	 * @param team Team number to be tested.
437 	 * @return True, if the given team is a friend team.
438 	 *
439 	 * @see getTeam()
440 	 * @see isEnemy(int)
441 	 */
442 	public boolean isFriend(int team)
443 	{
444 		// same team only
445 		return team != AgentInfo.TEAM_NONE && (team == lastSelf.getTeam());
446 	}
447 
448 	/**
449 	 * Tells, whether a given player is a friend to the agent.
450 	 *
451 	 * @param player Player to be tested.
452 	 * @return True, if the given player is a friend.
453 	 *
454 	 * @see getTeam()
455 	 * @see isEnemy(Player)
456 	 */
457 	public boolean isFriend(Player player)
458 	{
459 		// test the friend team number
460 		return isFriend(player.getTeam());
461 	}
462 
463 	/*========================================================================*/
464 
465 	/**
466 	 * Maps of players of specific type.
467 	 */
468 	private class PlayerMaps
469 	{
470 		/** Map of all players of the specific type. */
471 		private HashMap<UnrealId, Player> all = new HashMap<UnrealId, Player> ();
472 		/** Map of visible players of the specific type. */
473 		private HashMap<UnrealId, Player> visible = new HashMap<UnrealId, Player> ();
474 
475 		/**
476 		 * Processes events.
477 		 * @param player Player to process.
478 		 */
479 		private void notify(Player player)
480 		{
481 			UnrealId uid = player.getId();
482 
483 			// be sure to be within all
484 			if (!all.containsKey(uid))
485 				all.put(uid, player);
486 
487 			// previous visibility
488 			boolean wasVisible = visible.containsKey(uid);
489 			boolean isVisible = player.isVisible();
490 
491 			// refresh visible
492 			if (isVisible && !wasVisible)
493 			{
494 				// add to visibles
495 				visible.put(uid, player);
496 			}
497 			else if (!isVisible && wasVisible)
498 			{
499 				// remove from visibles
500 				visible.remove(uid);
501 			}
502 
503 		}
504 
505 		/**
506 		 * Removes player from all maps.
507 		 * @param uid UnrealId of player to be removed.
508 		 */
509 		private void remove(UnrealId uid)
510 		{
511 			// remove from all maps
512 			all.remove(uid);
513 			visible.remove(uid);
514 		}
515 
516 		private void clear() {
517 			all.clear();
518 			visible.clear();
519 		}
520 	}
521 
522 	/** Maps of all players. */
523 	private PlayerMaps players = new PlayerMaps ();
524 	/** Maps of all enemies. */
525 	private PlayerMaps enemies = new PlayerMaps ();
526 	/** Maps of all friends. */
527 	private PlayerMaps friends = new PlayerMaps ();
528 
529 	/*========================================================================*/
530 
531 	/**
532 	 * Player listener.
533 	 */
534 	private class PlayerListener implements IWorldObjectEventListener<Player, WorldObjectUpdatedEvent<Player>>
535 	{
536 		@Override
537 		public void notify(WorldObjectUpdatedEvent<Player> event)
538 		{
539             Player player = event.getObject();
540 			// do the job in map of players
541 			players.notify(player);
542 			if (lastSelf == null) return; // we do not have self yet ... we do not know which team are we in
543 			// do the job in map of enemies
544 			if (isEnemy(player))
545 				enemies.notify(player);
546 			// do the job in map of friends
547 			if (isFriend(player))
548 				friends.notify(player);
549 		}
550 
551 		/**
552 		 * Constructor. Registers itself on the given WorldView object.
553 		 * @param worldView WorldView object to listent to.
554 		 */
555 		public PlayerListener(IWorldView worldView)
556 		{
557 			worldView.addObjectListener(Player.class, WorldObjectUpdatedEvent.class, this);
558 		}
559 	}
560 
561 	/** Player listener */
562 	PlayerListener playerListener;
563 
564 	/*========================================================================*/
565 
566 	/**
567 	 * PlayerLeft listener.
568 	 */
569 	private class PlayerLeftListener implements IWorldEventListener<PlayerLeft>
570 	{
571 		@Override
572 		public void notify(PlayerLeft event)
573 		{
574 			UnrealId uid = event.getId();
575 
576 			// remove from all maps
577 			players.remove(uid);
578 			enemies.remove(uid);
579 			friends.remove(uid);
580 		}
581 
582 		/**
583 		 * Constructor. Registers itself on the given WorldView object.
584 		 * @param worldView WorldView object to listent to.
585 		 */
586 		public PlayerLeftListener(IWorldView worldView)
587 		{
588 			worldView.addEventListener(PlayerLeft.class, this);
589 		}
590 	}
591 
592 	/** PlayerLeft listener */
593 	PlayerLeftListener playerLeftListener;
594 
595 	/*========================================================================*/
596 	
597 	/**
598 	 * Self listener.
599 	 */
600 	private class SelfListener implements IWorldObjectListener<Self>
601 	{
602 		@Override
603 		public void notify(IWorldObjectEvent<Self> event)
604 		{
605 			if (lastSelf == null || lastSelf.getTeam() != event.getObject().getTeam()) {
606 				lastSelf = event.getObject();
607 				for (Player plr : players.all.values()) {
608 					if (isFriend(plr)) friends.notify(plr);
609 					if (isEnemy(plr)) enemies.notify(plr);
610 				}
611 			} else {
612 				lastSelf = event.getObject();
613 			}
614 		}
615 
616 		/**
617 		 * Constructor. Registers itself on the given WorldView object.
618 		 * @param worldView WorldView object to listent to.
619 		 */
620 		public SelfListener(IWorldView worldView)
621 		{
622 			worldView.addObjectListener(Self.class, this);
623 		}
624 	}
625 
626 	/** Self listener */
627 	SelfListener selfListener;
628 	
629 	Self lastSelf = null;
630 	
631 	/**
632 	 * Constructor. Setups the memory module based on bot's world view.
633 	 * @param bot owner of the module that is using it
634 	 */
635 	public Players(UT2004Bot bot)
636 	{
637 		this(bot, null);
638 	}
639 	
640 	/**
641 	 * Constructor. Setups the memory module based on bot's world view.
642 	 * @param bot owner of the module that is using it
643 	 * @param log Logger to be used for logging runtime/debug info. If <i>null</i>, module creates its own logger.
644 	 */
645 	public Players(UT2004Bot bot, Logger log)
646 	{
647 		super(bot, log);
648 		
649 		// create listeners
650 		playerListener =     new PlayerListener(worldView);
651 		playerLeftListener = new PlayerLeftListener(worldView);
652 		selfListener =       new SelfListener(worldView);
653 		
654 		cleanUp();
655 	}
656 	
657 	@Override
658 	protected void cleanUp() {
659 		super.cleanUp();
660 		lastSelf = null;
661 		players.clear();
662 		friends.clear();
663 		enemies.clear();
664 	}
665 
666 }