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 }