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 }