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