Free cookie consent management tool by TermsFeed Policy Generator

source: branches/Robocode.TrunkInt/HeuristicLab.Problems.Robocode/3.3/robocode/robots/samplesentry/BorderGuard.java @ 13019

Last change on this file since 13019 was 13019, checked in by gkronber, 9 years ago

#2069: added necessary robocode files to project to make sure that the robocode problem is self-contained (only a java installation is necessary)

File size: 27.2 KB
Line 
1/**
2 * Copyright (c) 2001-2014 Mathew A. Nelson and Robocode contributors
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://robocode.sourceforge.net/license/epl-v10.html
7 */
8package samplesentry;
9
10
11import robocode.*;
12import robocode.util.Utils;
13
14import java.awt.BasicStroke;
15import java.awt.Color;
16import java.awt.Graphics2D;
17import java.awt.geom.Point2D;
18import java.util.*;
19
20
21/**
22 * BorderGuard - is a sample robot that demonstrates how the BorderSentry interface can be used for
23 * creating a robot that guards the border area of the battle field, and which is efficient against
24 * robots trying to hide at corners and robots sneaking around near the borders.
25 * <p>
26 * This robot is somewhat advanced due to:<br>
27 * 1) it uses linear targeting for predicting how to hit robots that moves in straight lines.<br>
28 * 2) it will only fire at a robot, if it figures out that its bullets will do damage to that
29 * particular robot, e.g. when the robots predicted future position will be within the sentry border
30 * attack range.<br>
31 * 3) it has an efficient scanner that keeps the scan angle as little as possible to get as new
32 * scanned data for enemy robots as possible.<br>
33 * 4) it picks a target robot to fire at, which is the nearest robot our robot will be able to
34 * damage (by predicting its future position using linear targeting).<br>
35 * 5) it only moves along the borders of the battle field and constantly changes its direction, so
36 * it is not an easy target, and it will try to get as close to its target robot as possible.<br>
37 * <p>
38 * Lots of improvements can be made to this robot by copying it - making it even stronger. The
39 * intention with of this sample robot is to serve as a more advanced example of how a AdvancedRobot
40 * can be made, and how it can be structured as most sample robots are far simpler.
41 * <p>
42 *
43 * Credits goes to the hard working Robocoders at the RoboWiki. :-)
44 * <p>
45 *
46 * This robot makes use of the Oldest Scanned melee scanner from the RoboWiki:<br>
47 * http://robowiki.net/wiki/Melee_Radar
48 * <p>
49 *
50 * In addition, it makes use of the Exact Non-iterative Solution for Linear Targeting from the
51 * RoboWiki:<br>
52 * http://robowiki.net/wiki/Linear_Targeting
53 *
54 * @author Flemming N. Larsen
55 *
56 * @version 1.0
57 *
58 * @since 1.9.0.0
59 */
60public class BorderGuard extends AdvancedRobot implements BorderSentry {
61
62  // Constants
63  final double FIREPOWER = 3; // Max. power => violent as this robot can afford it!
64  final double HALF_ROBOT_SIZE = 18; // Robot size is 36x36 units, so the half size is 18 units
65
66  // Map containing data for all scanned robots.
67  // The key to the map is a robot name and the value is an object containing robot data.
68  final Map<String, RobotData> enemyMap;
69
70  // Scanning direction, where the radar turns to the right with positive values, and turns
71  // to the left with negative values.
72  double scanDir = 1;
73
74  // Oldest scanned robot. Can be null.
75  RobotData oldestScanned;
76
77  // Target robot for the gun. Can be null meaning that there is currently no target robot.
78  RobotData target;
79
80  // Last time when the robot shifted its direction
81  long lastDirectionShift;
82
83  // Current direction, where 1 means ahead (forward) and -1 means back
84  int direction = 1;
85
86  /**
87   * Constructs this robot.
88   */
89  public BorderGuard() {
90    // We initialize a specialized HashMap that uses a linked list for the access order.
91    // This means that the last accessed robot entry is listed first, when we iterate over its
92    // values. This robot always sweep the radar towards the oldest scanned robot.
93    enemyMap = new LinkedHashMap<String, RobotData>(5, 2, true);
94  }
95
96  /**
97   * Main method that is called by the game when the robot engage in the next round of a battle.
98   */
99  @Override
100  public void run() {
101    // Do initialization stuff here before the loop
102    initialize();
103
104    // Loop forever. If the robot does not take action, the game will disable our robot!
105    while (true) {
106      // Handle a single turn...
107
108      // Handle the radar that scans enemy robots
109      handleRadar();
110      // Handle the gun by turning it and fire at our target
111      handleGun();
112      // Move the robot around on the battlefield
113      moveRobot();
114
115      // Scan for other robots. Note that this method will execute all pending commands for
116      // the next turn. Hence, scan() ends the turn for our robot.
117      scan();
118    }
119  }
120
121  /**
122   * This method is called by the game when your robot sees another robot, i.e. when the robot's
123   * radar scan "hits" another robot.
124   *
125   * @param scannedRobotEvent
126   *            is a ScannedRobotEvent event.
127   */
128  @Override
129  public void onScannedRobot(ScannedRobotEvent scannedRobotEvent) {
130    // Check that the scanned robot is not a sentry robot
131    if (!scannedRobotEvent.isSentryRobot()) {
132      // The scanned robot is not a sentry robot...
133
134      // Update the enemy map
135      updateEnemyMap(scannedRobotEvent);
136
137      // Update the scan direction
138      updateScanDirection(scannedRobotEvent);
139
140      // Update enemy target positions
141      updateEnemyTargetPositions();
142    }
143  }
144
145  /**
146   * This method is called by the game when another robot dies.
147   *
148   * @param robotDeathEvent
149   *            is the RobotDeathEvent that occurs, when another robot dies, which contains data
150   *            for the robot that died.
151   */
152  @Override
153  public void onRobotDeath(RobotDeathEvent robotDeathEvent) {
154    // Gets the name of the robot that died
155    final String deadRobotName = robotDeathEvent.getName();
156
157    // Remove the robot data for the robot that died from the enemy map
158    enemyMap.remove(deadRobotName);
159
160    // Remove the data entry for the oldest scanned robot, if we have such an entry
161    if (oldestScanned != null && oldestScanned.name.equals(deadRobotName)) {
162      oldestScanned = null;
163    }
164    if (target != null && target.name.equals(deadRobotName)) {
165      target = null;
166    }
167  }
168
169  /**
170   * This method is called by the every time the robot is painted. In order to see the painting,
171   * make sure to enable the Paint button on the robot console for this robot.
172   *
173   * @param g
174   *            is the {@link Graphics2D} object, which is the graphics context used for painting
175   *            various shapes like rectangles, circles, lines etc. on top of the battlefield for
176   *            debugging graphics.
177   */
178  @Override
179  public void onPaint(Graphics2D g) {
180    // Set the line width to 2 pixels
181    g.setStroke(new BasicStroke(2f));
182
183    // Prepare colors for painting the scanned coordinate and target coordinate
184    Color color1 = new Color(0x00, 0xFF, 0x00, 0x40); // Green with 25% alpha blending
185    Color color2 = new Color(0xFF, 0xFF, 0x00, 0x40); // Yellow with 25% alhpa blending
186
187    // Paint a two circles for each robot in the enemy map. One circle where the robot was
188    // scanned the last time, and another circle where our robot must point the gun in order to
189    // hit it (target coordinate). In addition, a line is drawn between these circles.
190    for (RobotData robot : enemyMap.values()) {
191      // Paint the two circles and a line
192      fillCircle(g, robot.scannedX, robot.scannedY, color1); // scanned coordinate
193      fillCircle(g, robot.targetX, robot.targetY, color2); // target coordinate
194      g.setColor(color1);
195      g.drawLine((int) robot.scannedX, (int) robot.scannedY, (int) robot.targetX, (int) robot.targetY);
196    }
197
198    // Paint a two circles for the target robot. One circle where the robot was
199    // scanned the last time, and another circle where our robot must point the gun in order to
200    // hit it (target coordinate). In addition, a line is drawn between these circles.
201    if (target != null) {
202      // Prepare colors for painting the scanned coordinate and target coordinate
203      color1 = new Color(0xFF, 0x7F, 0x00, 0x40); // Orange with 25% alpha blending
204      color2 = new Color(0xFF, 0x00, 0x00, 0x80); // Red with 50% alpha blending
205
206      // Paint the two circles and a line
207      fillCircle(g, target.scannedX, target.scannedY, color1); // scanned coordinate
208      fillCircle(g, target.targetX, target.targetY, color2); // target coordinate
209      g.setColor(color1);
210      g.drawLine((int) target.scannedX, (int) target.scannedY, (int) target.targetX, (int) target.targetY);
211    }
212  }
213
214  /**
215   * Initializes this robot before a new round in a battle.
216   */
217  private void initialize() {
218    // Let the robot body, gun, and radar turn independently of each other
219    setAdjustRadarForGunTurn(true);
220    setAdjustGunForRobotTurn(true);
221
222    // Set robot colors
223    setBodyColor(new Color(0x5C, 0x33, 0x17)); // Chocolate Brown
224    setGunColor(new Color(0x45, 0x8B, 0x74)); // Aqua Marine
225    setRadarColor(new Color(0xD2, 0x69, 0x1E)); // Orange Chocolate
226    setBulletColor(new Color(0xFF, 0xD3, 0x9B)); // Burly wood
227    setScanColor(new Color(0xCA, 0xFF, 0x70)); // Olive Green
228  }
229
230  /**
231   * This method handles the radar that scans for enemy robots.
232   */
233  private void handleRadar() {
234    // Set the radar to turn infinitely to the right if the scan direction is positive;
235    // otherwise the radar is moved to the left, if the scan direction is negative.
236    // Notice that onScannedRobot(ScannedRobotEvent) is responsible for determining the scan
237    // direction.
238    setTurnRadarRightRadians(scanDir * Double.POSITIVE_INFINITY);
239  }
240
241  /**
242   * Method that handles the gun be turning it and fire at a target.
243   */
244  private void handleGun() {
245    // Update our target robot to fire at
246    updateTarget();
247    // Update the gun direction
248    updateGunDirection();
249    // Fires the gun, when it is ready
250    fireGunWhenReady();
251  }
252
253  /**
254   * Method that moves our robot around the battlefield.
255   */
256  private void moveRobot() {
257
258    // The movement strategy is to move as close to our target robot as possible.
259    // Our robot should move along the borders all the time, vertically or horizontally.
260    // When we get close to our target, or have no where to go, our robot should shift its
261    // direction from side to side so it does not stand still at any time.
262    // If the robot stands still, it will be an easy target for enemy robots.
263
264    int newDirection = direction;
265
266    // Get closer to our target if we have a target robot
267    if (target != null) {
268      // Calculate the range from the walls/borders, our robot should keep within
269      int borderRange = getSentryBorderSize() - 20;
270
271      // The horizontal and vertical flags are used for determining, if our robot should
272      // move horizontal or vertical.
273      boolean horizontal = false;
274      boolean vertical = false;
275
276      // Initialize the new heading of the robot to the current heading of the robot
277      double newHeading = getHeadingRadians();
278
279      // Check if our robot is at the upper or lower border and hence should move horizontally
280      if (getY() < borderRange || getY() > getBattleFieldHeight() - borderRange) {
281        horizontal = true;
282      }
283      // Check if our robot is at the left or right border and hence should move vertically
284      if (getX() < borderRange || getX() > getBattleFieldWidth() - borderRange) {
285        vertical = true;
286      }
287
288      // If we are in one of the corners of the battlefield, we could move both horizontally
289      // or vertically.
290      // In this situation, we need to choose one of the two directions.
291      if (horizontal && vertical) {
292        // If the horizontal distance to our target is lesser than the vertical distance,
293        // then we choose to move vertically, and hence we clear the horizontal flag.
294        if (Math.abs(target.targetX - getX()) <= Math.abs(target.targetY - getY())) {
295          horizontal = false; // Do not move horizontally => move vertically
296        }
297      }
298      // Adjust the heading of our robot with 90 degrees, if it must move horizontally.
299      // Otherwise the calculated heading is towards moving vertically.
300      if (horizontal) {
301        newHeading -= Math.PI / 2;
302      }
303      // Set the robot to turn left the amount of radians we have just calculated
304      setTurnLeftRadians(Utils.normalRelativeAngle(newHeading));
305
306      // Check if our robot has finished turning, i.e. has less than 1 degrees left to turn
307      if (Math.abs(getTurnRemaining()) < 1 || Math.abs(getVelocity()) < 0.01) {
308        // If we should move horizontally, the set the robot to move ahead with the
309        // horizontal distance to the target robot. Otherwise, use the vertical distance.
310        double delta; // delta is the delta distance to move
311        if (horizontal) {
312          delta = target.targetX - getX();
313        } else {
314          delta = target.targetY - getY();
315        }
316        setAhead(delta);
317
318        // Set the new direction of our robot to 1 (meaning move forward) if the delta
319        // distance is positive; otherwise it is set to -1 (meaning move backward).
320        newDirection = delta > 0 ? 1 : -1;
321
322        // Check if more than 10 turns have past since we changed the direction the last
323        // time
324        if (getTime() - lastDirectionShift > 10) {
325          // If so, set the new direction to be the reverse direction if the velocity < 1
326          if (Math.abs(getVelocity()) < 1) {
327            newDirection = direction * -1;
328          }
329          // Check if the direction really changed
330          if (newDirection != direction) {
331            // If the new direction != current direction, then set the current direction
332            // to be the new direction and save the current time so we know when we
333            // changed the direction the last time.
334            direction = newDirection;
335            lastDirectionShift = getTime();
336          }
337        }
338      }
339    }
340    // Set ahead 100 units forward or backward depending on the direction
341    setAhead(100 * direction);
342  }
343
344  /**
345   * Method the updates the enemy map based on new scan data for a scanned robot.
346   *
347   * @param scannedRobotEvent
348   *            is a ScannedRobotEvent event containing data about a scanned robot.
349   */
350  private void updateEnemyMap(ScannedRobotEvent scannedRobotEvent) {
351    // Gets the name of the scanned robot
352    final String scannedRobotName = scannedRobotEvent.getName();
353
354    // Get robot data for the scanned robot, if we have an entry in the enemy map
355    RobotData scannedRobot = enemyMap.get(scannedRobotName);
356
357    // Check if data entry exists for the scanned robot
358    if (scannedRobot == null) {
359      // No data entry exists => Create a new data entry for the scanned robot
360      scannedRobot = new RobotData(scannedRobotEvent);
361      // Put the new data entry into the enemy map
362      enemyMap.put(scannedRobotName, scannedRobot);
363    } else {
364      // Data entry exists => Update the current entry with new scanned data
365      scannedRobot.update(scannedRobotEvent);
366    }
367  }
368
369  /**
370   * Method that updates the direction of the radar based on new scan data for a scanned robot.
371   *
372   * @param scannedRobotEvent
373   *            is a ScannedRobotEvent event containing data about a scanned robot.
374   */
375  private void updateScanDirection(ScannedRobotEvent scannedRobotEvent) {
376    // Gets the name of the scanned robot
377    final String scannedRobotName = scannedRobotEvent.getName();
378
379    // Change the scanning direction if and only if we have no record for the oldest scanned
380    // robot or the scanned robot IS the oldest scanned robot (based on the name) AND the enemy
381    // map contains scanned data entries for ALL robots (the size of the enemy map is equal to
382    // the number of opponent robots found by calling the getOthers() method).
383    if ((oldestScanned == null || scannedRobotName.equals(oldestScanned.name)) && enemyMap.size() == getOthers()) {
384
385      // Get the oldest scanned robot data from our LinkedHashMap, where the first value
386      // contains the oldest accessed entry, which is the robot we need to get.
387      RobotData oldestScannedRobot = enemyMap.values().iterator().next();
388
389      // Get the recent scanned position (x,y) of the oldest scanned robot
390      double x = oldestScannedRobot.scannedX;
391      double y = oldestScannedRobot.scannedY;
392
393      // Get the heading of our robot
394      double ourHeading = getRadarHeadingRadians();
395
396      // Calculate the bearing to the oldest scanned robot.
397      // The bearing is the delta angle between the heading of our robot and the other robot,
398      // which can be a positive or negative angle.
399      double bearing = bearingTo(ourHeading, x, y);
400
401      // Update the scan direction based on the bearing.
402      // If the bearing is positive, the radar will be moved to the right.
403      // If the bearing is negative, the radar will be moved to the left.
404      scanDir = bearing;
405    }
406  }
407
408  /**
409   * Updates the target positions for all enemies. The target position is the position our robot
410   * must fire at in order to hit the target robot. This robot uses Linear Targeting (Exact
411   * Non-iterative Solution) as described on the RoboWiki here:
412   * http://robowiki.net/wiki/Linear_Targeting
413   */
414  private void updateEnemyTargetPositions() {
415    // Go thru all robots in the enemy map
416    for (RobotData enemy : enemyMap.values()) {
417
418      // Variables prefixed with e- refer to enemy and b- refer to bullet
419      double bV = Rules.getBulletSpeed(FIREPOWER);
420      double eX = enemy.scannedX;
421      double eY = enemy.scannedY;
422      double eV = enemy.scannedVelocity;
423      double eH = enemy.scannedHeading;
424
425      // These constants make calculating the quadratic coefficients below easier
426      double A = (eX - getX()) / bV;
427      double B = (eY - getY()) / bV;
428      double C = eV / bV * Math.sin(eH);
429      double D = eV / bV * Math.cos(eH);
430
431      // Quadratic coefficients: a*(1/t)^2 + b*(1/t) + c = 0
432      double a = A * A + B * B;
433      double b = 2 * (A * C + B * D);
434      double c = (C * C + D * D - 1);
435
436      // If the discriminant of the quadratic formula is >= 0, we have a solution meaning that
437      // at some time, t, the bullet will hit the enemy robot if we fire at it now.
438      double discrim = b * b - 4 * a * c;
439      if (discrim >= 0) {
440        // Reciprocal of quadratic formula. Calculate the two possible solution for the
441        // time, t
442        double t1 = 2 * a / (-b - Math.sqrt(discrim));
443        double t2 = 2 * a / (-b + Math.sqrt(discrim));
444
445        // Choose the minimum positive time or select the one closest to 0, if the time is
446        // negative
447        double t = Math.min(t1, t2) >= 0 ? Math.min(t1, t2) : Math.max(t1, t2);
448
449        // Calculate the target position (x,y) for the enemy. That is the point that our gun
450        // should point at in order to hit the enemy at the time, t.
451        double targetX = eX + eV * t * Math.sin(eH);
452        double targetY = eY + eV * t * Math.cos(eH);
453
454        // Assume enemy stops at walls. Hence, we limit that target position at the walls.
455        double minX = HALF_ROBOT_SIZE;
456        double minY = HALF_ROBOT_SIZE;
457        double maxX = getBattleFieldWidth() - HALF_ROBOT_SIZE;
458        double maxY = getBattleFieldHeight() - HALF_ROBOT_SIZE;
459
460        enemy.targetX = limit(targetX, minX, maxX);
461        enemy.targetY = limit(targetY, minY, maxY);
462      }
463    }
464  }
465
466  /**
467   * Updates which enemy robot from the enemy map that should be our current target.
468   */
469  private void updateTarget() {
470    // Set target to null, meaning that we have no target robot yet
471    target = null;
472
473    // Create a list over possible target robots that is a copy of robot data from the enemy map
474    List<RobotData> targets = new ArrayList<RobotData>(enemyMap.values());
475
476    // Run thru all the possible target robots and remove those that are outside the attack
477    // range for this border sentry robot as our robot cannot do harm to robots outside its
478    // range.
479    Iterator<RobotData> it = targets.iterator();
480    while (it.hasNext()) {
481      RobotData robot = it.next();
482      if (isOutsideAttackRange(robot.targetX, robot.targetY)) {
483        it.remove();
484      }
485    }
486
487    // Set the target robot to be the one among all possible target robots that is closest to
488    // our robot.
489    double minDist = Double.POSITIVE_INFINITY;
490    for (RobotData robot : targets) {
491      double dist = distanceTo(robot.targetX, robot.targetY);
492      if (dist < minDist) {
493        minDist = dist;
494        target = robot;
495      }
496    }
497
498    // If we still haven't got a target robot, then take the first one from our list of target
499    // robots if the list is not empty.
500    if (target == null && targets.size() > 0) {
501      target = targets.get(0);
502    }
503  }
504
505  /**
506   * Method that updates the gun direction to point at the current target.
507   */
508  private void updateGunDirection() {
509    // Only update the gun direction, if we have a current target
510    if (target != null) {
511      // Calculate the bearing between the gun and the target, which can be positive or
512      // negative
513      double targetBearing = bearingTo(getGunHeadingRadians(), target.targetX, target.targetY);
514      // Set the gun to turn right the amount of radians defined by the bearing to the target
515      setTurnGunRightRadians(targetBearing); // positive => turn right, negative => turn left
516    }
517  }
518
519  /**
520   * Method that fires a bullet when the gun is ready to fire.
521   */
522  private void fireGunWhenReady() {
523    // We only fire the fun, when we have a target robot
524    if (target != null) {
525      // Only fire when the angle of the gun is pointing at our (virtual) target robot
526
527      // Calculate the distance between between our robot and the target robot
528      double dist = distanceTo(target.targetX, target.targetY);
529      // Angle that "covers" the the target robot from its center to its edge
530      double angle = Math.atan(HALF_ROBOT_SIZE / dist);
531
532      // Check if the remaining angle (turn) to move the gun is less than our calculated cover
533      // angle
534      if (Math.abs(getGunTurnRemaining()) < angle) {
535        // If so, our gun should be pointing at our target so we can hit it => fire!!
536        setFire(FIREPOWER);
537      }
538    }
539  }
540
541  /**
542   * Method that checks if a coordinate (x,y) is outside the Border Sentry's attack range.
543   *
544   * @param x
545   *            is the x coordinate.
546   * @param y
547   *            is the y coordinate.
548   * @return true if the coordinate is outside the attack range; false otherwise.
549   */
550  private boolean isOutsideAttackRange(double x, double y) {
551    double minBorderX = getSentryBorderSize();
552    double minBorderY = getSentryBorderSize();
553    double maxBorderX = getBattleFieldWidth() - getSentryBorderSize();
554    double maxBorderY = getBattleFieldHeight() - getSentryBorderSize();
555
556    return (x > minBorderX) && (y > minBorderY) && (x < maxBorderX) && (y < maxBorderY);
557  }
558
559  /**
560   * Method that returns a value that is guaranteed to be within a value range defined by a
561   * minimum and maximum value based on an input value.<br>
562   * If the input value is lesser than the minimum value, the returned value will be set to the
563   * minimum value.<br>
564   * If the input value is greater than the maximum value, the returned value will be set to the
565   * maximum value.<br>
566   * Otherwise the returned value will be equal to the input value.
567   *
568   * @param value
569   *            is the input value to limit.
570   * @param min
571   *            is the allowed minimum value.
572   * @param max
573   *            is the allowed maximum value.
574   * @return the limited input value that is guaranteed to be within the specified minimum and
575   *         maximum range.
576   */
577  private double limit(double value, double min, double max) {
578    return Math.min(max, Math.max(min, value));
579  }
580
581  /**
582   * Methods that returns the distance to a coordinate (x,y) from our robot.
583   *
584   * @param x
585   *            is the x coordinate.
586   * @param y
587   *            is the y coordinate.
588   * @return the distance to the coordinate (x,y).
589   */
590  private double distanceTo(double x, double y) {
591    return Math.hypot(x - getX(), y - getY());
592  }
593
594  /**
595   * Method that returns the angle to a coordinate (x,y) from our robot.
596   *
597   * @param x
598   *            is the x coordinate.
599   * @param y
600   *            is the y coordinate.
601   * @return the angle to the coordinate (x,y).
602   */
603  private double angleTo(double x, double y) {
604    return Math.atan2(x - getX(), y - getY());
605  }
606
607  /**
608   * Method that returns the bearing to a coordinate (x,y) from the position and heading of our
609   * robot. The bearing is the delta angle between the heading of our robot and the angle of the
610   * specified coordinate.
611   *
612   * @param x
613   *            is the x coordinate.
614   * @param y
615   *            is the y coordinate.
616   * @return the angle to the coordinate (x,y).
617   */
618  private double bearingTo(double heading, double x, double y) {
619    return Utils.normalRelativeAngle(angleTo(x, y) - heading);
620  }
621
622  /**
623   * Method that paints a filled circle at the specified coordinate (x,y) and given color. The
624   * circle will have a radius of 20 pixels (meaning that the diameter will be 40 pixels).
625   *
626   * @param gfx
627   *            is the graphics context to draw within.
628   * @param x
629   *            is the x coordinate for the center of the circle.
630   * @param y
631   *            is the y coordinate for the center of the circle.
632   * @param color
633   *            is the color of the filled circle.
634   */
635  private void fillCircle(Graphics2D gfx, double x, double y, Color color) {
636    // Set the pen color
637    gfx.setColor(color);
638    // Paint a filled circle (oval) that has a radius of 20 pixels with a center at the input
639    // coordinates.
640    gfx.fillOval((int) x - 20, (int) y - 20, 40, 40);
641  }
642
643  /**
644   * This class is used for storing data about a robot that has been scanned.<br>
645   * The data is mainly a snapshot of specific scanned data like the scanned position (x,y),
646   * velocity and heading, put also the calculated predicted target position of the robot when our
647   * robot needs to fire at the scanned robot.<br>
648   * Note that this class calculates the position (x,y) of the scanned robot as our robot moves,
649   * and hence data like the angle and distance to the scanned robot will change over time. by
650   * using the position, it is easy to calculate a new angle and distance to the robot.
651   */
652  class RobotData {
653    final String name; // name of the scanned robot
654    double scannedX; // x coordinate of the scanned robot based on the last update
655    double scannedY; // y coordinate of the scanned robot based on the last update
656    double scannedVelocity; // velocity of the scanned robot from the last update
657    double scannedHeading; // heading of the scanned robot from the last update
658    double targetX; // predicated x coordinate to aim our gun at, when firing at the robot
659    double targetY; // predicated y coordinate to aim our gun at, when firing at the robot
660
661    /**
662     * Creates a new robot data entry based on new scan data for a scanned robot.
663     *
664     * @param event
665     *            is a ScannedRobotEvent event containing data about a scanned robot.
666     */
667    RobotData(ScannedRobotEvent event) {
668      // Store the name of the scanned robot
669      name = event.getName();
670      // Updates all scanned facts like position, velocity, and heading
671      update(event);
672      // Initialize the coordinates (x,y) to fire at to the updated scanned position
673      targetX = scannedX;
674      targetY = scannedY;
675    }
676
677    /**
678     * Updates the scanned data based on new scan data for a scanned robot.
679     *
680     * @param event
681     *            is a ScannedRobotEvent event containing data about a scanned robot.
682     */
683    void update(ScannedRobotEvent event) {
684      // Get the position of the scanned robot based on the ScannedRobotEvent
685      Point2D.Double pos = getPosition(event);
686      // Store the scanned position (x,y)
687      scannedX = pos.x;
688      scannedY = pos.y;
689      // Store the scanned velocity and heading
690      scannedVelocity = event.getVelocity();
691      scannedHeading = event.getHeadingRadians();
692    }
693
694    /**
695     * Returns the position of the scanned robot based on new scan data for a scanned robot.
696     *
697     * @param event
698     *            is a ScannedRobotEvent event containing data about a scanned robot.
699     * @return the position (x,y) of the scanned robot.
700     */
701    Point2D.Double getPosition(ScannedRobotEvent event) {
702      // Gets the distance to the scanned robot
703      double distance = event.getDistance();
704      // Calculate the angle to the scanned robot (our robot heading + bearing to scanned
705      // robot)
706      double angle = getHeadingRadians() + event.getBearingRadians();
707
708      // Calculate the coordinates (x,y) of the scanned robot
709      double x = getX() + Math.sin(angle) * distance;
710      double y = getY() + Math.cos(angle) * distance;
711
712      // Return the position as a point (x,y)
713      return new Point2D.Double(x, y);
714    }
715  }
716}
Note: See TracBrowser for help on using the repository browser.