IBM®
Skip to main content
    Country/region [select]      Terms of use
 
 
      
     Home      Products      Services & solutions      Support & downloads      My account     
Secrets from the Robocode masters: Tracking bullets
Contents:
onBulletHit
A better way
Plugging it in
Taking it one step further
What now?
Resources
About the author
Choose the best aiming technique for each opponent

Ray Vermette (rayvermette@rogers.com)
IT Consultant, Rideau Code Works
July 2002

Using the best aiming technique for each opponent requires obtaining accurate stats on which techniques work and which don't. In this tip, Ray Vermette shows you how to implement a BulletTracker class to do just that.

back to Robocode Rumble

As your robot grows in both ability and complexity, you will probably employ several different aiming techniques. Choosing the best aiming technique for each opponent will require you to keep track of shots fired and bullet hits. You can do this by overriding the onBulletHit event handler of the AdvancedRobot class. However, I am going to show you how you can accomplish the very same thing with fewer lines of code and greater accuracy by using a custom event.

onBulletHit: Not a great solution
The problem with using the onBulletHit event handler is that it doesn't tell you if you were shooting at the bullet's victim in the first place. For example, if your robot shoots at sample.Walls and hits sample.SpinBot instead, you receive a BulletHitEvent for sample.SpinBot, when perhaps what you really want to be informed of is the fact that you missed sample.Walls. If you keep track of Bullet objects and target names, it is possible to determine if a bullet hit is intended or by accident, but this requires extra data structures to store your bullets and target names, plus extra code to insert, look up, and remove bullets and target names. It's a solution, but not a terribly elegant one.

A better way
Instead, let's use a custom event to do the same thing.

To create a custom event, you first need a Condition class. The Condition class has one abstract method that you have to override: public boolean test(). The test() method returns true if a custom event should be raised, and false otherwise. The BulletTracker class in Listing 1 demonstrates one possible implementation of the Condition class you will require.

Listing 1. The BulletTracker class

package bt;
import robocode.*;

public class BulletTracker extends Condition {

    private long                expectedTimeOfImpact;
    private String              targetName;
    private Bullet              bullet;
    private AdvancedRobot       myRobot;
    private int                 aimMethod;
    private boolean             hitTarget;


    public BulletTracker(
            AdvancedRobot ar,
            Bullet b,
            String targetName,
            long timeToImpact,
            int aimMethod) {

        if (b != null) {

            this.myRobot                = ar;
            this.bullet                 = b;
            this.targetName             = targetName;
            this.expectedTimeOfImpact   = ar.getTime() + timeToImpact;
            this.aimMethod              = aimMethod;
            this.hitTarget              = false;
            myRobot.addCustomEvent(this);
        }

    }


    public long     getExpectedTimeOfImpact() { return expectedTimeOfImpact; }
    public String   getTargetName()           { return targetName; }
    public Bullet   getBullet()               { return bullet; }
    public int      getAimMethod()            { return aimMethod; }
    public boolean  hitTarget()               { return hitTarget; }


    public boolean test() {

        if (targetName.equals(bullet.getVictim())) {
            hitTarget = true;
            myRobot.removeCustomEvent(this);
            return true;
        }

        if (bullet.getVictim() != null) {
            myRobot.removeCustomEvent(this);
        }

        if (expectedTimeOfImpact <= myRobot.getTime()) {
            hitTarget = false;
            myRobot.removeCustomEvent(this);
            return true;
        }

        return false;
    }
}

The BulletTracker condition needs a reference to your AdvancedRobot, a reference to the bullet it is supposed to track, the name of the intended target, the calculated time until bullet impact, and an integer corresponding to the aiming technique used. If the bullet is not null (null bullets occur if the robot attempts to fire when gun heat is greater than zero or at the beginning of a round), the parameters are stored and the BulletTracker object is added as a custom event.

From then on, the Robocode engine invokes the test() method of our BulletTracker object every turn to see if a custom event should be raised. If the bullet has hit the intended target, hitTarget is set to true, the custom event is removed, and the test() method returns true, which results in a custom event being sent to your AdvancedRobot. If the bullet's victim name is not equal to the intended target's name and is not null, then your bullet has accidentally hit another robot on its way to the intended target. If the timeToImpact has already elapsed and your bullet has still not hit the intended target, then the BulletTracker regards this as a miss; hitTarget is set to false, the event is removed, and the method returns true, resulting in a custom event being sent back to your AdvancedRobot. The BulletTracker class uses a timeout (expectedTimeOfImpact) to check for bullet misses, but you could very well check for bullet.isActive() instead. The advantage of using a timeout is explained later.

Plugging it in
To use the BulletTracker class, you need to add code to your AdvancedRobot in two different places. At the section where your robot fires, add the following code:

Listing 2. Instantiating the BulletTracker

Bullet bullet = fireBullet(firePower);
BulletTracker bt = 
  new BulletTracker(this, bullet, targetName, timeToImpact, aimMethod);

In the onCustomEvent handler, add the following code:

Listing 3. OnCustomEvent handler

Condition condition = e.getCondition();
if (condition instanceof BulletTracker) {

    BulletTracker bt = (BulletTracker)condition;

    if (bt.hitTarget()) {
        if (bt.getAimMethod() == AIM_TECHNIQUE_1) {
            /* Increment technique 1's hit count */;
        }
        if (bt.getAimMethod() == AIM_TECHNIQUE_2) {
            /* Increment technique 2's hit count */;
        }
    }
}

The BulletTracker object takes care of adding and removing itself as a custom event, so you only have to remember to create the BulletTracker when you fire and react to hits or misses when a BulletTracker custom event is raised. There are no vectors or lists of bullets and target names to maintain -- the BulletTracker class and the event mechanisms of Robocode do the dirty work for you. It's that simple.

Taking it one step further
Because the BulletTracker in the example uses expectedTimeOfImpact to judge when a bullet has missed, you can use this to detect and counter bullet-dodging bots. For example:

  1. Your robot fires a bullet at a completely stationary target 200 pixels away.
  2. The calculated timeToImpact is 15 game ticks, firing straight at the target's current location.
  3. A BulletTracker object is created to track the path of the bullet.
  4. After the bullet is fired, the target moves left to dodge the incoming bullet.
  5. When the 15 game ticks are up, the BulletTracker regards this as a miss.

If you add the target's initial x and y coordinates to the BulletTracker class, you can calculate the speed and heading the target actually took in reaction to being fired at. If the target predictably dodges bullets in the same manner, you can store these reactions and use them later to aim at and successfully hit the bullet-dodging foe.

You could also move the code that increments hit and miss counters to the BulletTracker class, eliminating the need for code in the onCustomEvent handler.

What now?
Download the example robot to see the BulletTracker class in action. To keep things simple, the example robot has no movement and will only work in a one-on-one battle, but has two aiming techniques: one that uses iteration to lead the target, and another that uses linear aiming. The BulletTracker tracks each bullet fired and is used to update hit counters for each aiming technique. The number of shots fired and successful hits are printed to the console at the end of each round.

Now put the BulletTracker to work and let's shoot some bot!

Resources

  • Download the source code for the BulletTracker example.

  • Read all of the Secrets from the Robocode masters . This page will be updated as new tips become available.

  • Download the latest version of Robocode from alphaWorks.

  • Robocode's creator, Mathew Nelson, maintains the official Robocode site. This should be the first stop for anyone serious about Robocode. From here, you can also participate in the discussion group moderated by Mathew Nelson. Also check out the to-do list for an "always current" list of requested features.

  • RoboLeague by Christian Schnell is a league and season manager for Robocode. It ensures that all possible groupings indeed play their matches, manages the results, and produces HTML status reports.

  • "Rock 'em, sock 'em Robocode" (developerWorks, January 2002) disarms Robocode and starts you on your way to building your own customized lean, mean, fighting machine.

  • In "Rock 'em, sock 'em Robocode: Round 2" (developerWorks, May 2002), Sing Li looks at advanced robot construction and team play.

  • New to Java? Check out "Java language essentials" (developerWorks, November 2000), a tutorial that steps you through the fundamentals of Java language programming.

  • Find other Java resources on the developerWorks Java technology zone.

About the author
Ray Vermette is a mild-mannered mainframe programmer and inline skater by day and avid Robocode and Java enthusiast by night. Contact Ray at
rayvermette@rogers.com.

back to Robocode Rumble



  About IBM  |  Privacy  |  Terms of use  |  Contact