Robocode Advance
Robocode Advance
Go beyond the basics with advanced robot building and team play
Sing Li
Author
Makawave
01 May 2002
Get ready to venture further into the realm of Robocode with this comprehensive look at
advanced robot construction and team play. Veteran Java developer and newly converted
Robocode fanatic Sing Li capitalizes on Robocode's unique -- and wildly fun -- approach to
learning to walk you through more advanced Java programming techniques, algorithm design,
basic trigonometry, and even distributed computing principles. Your opponents won't know what
hit them.
Back in January, we presented a behind-the-scenes glimpse at how the Robocode simulator
works. The robots we created were very simple and did not take advantage of the advanced
features built into Robocode. In this article, we complete our introduction to Robocode by working
with these advanced features. Along the way, we'll explore Java programming, math, and software
design. To make things fun -- even for more advanced robot builders -- we'll look at the new "team
play" feature in Robocode and survey some expert designers to learn about the super robots they
have built.
DWStraight extends Robot class, meaning we get to use all the methods provided by the class,
such as turnRight() and turnLeft(). One very restrictive thing about these methods is that they
do not return control to our code until they finish operation. In fact, these operations can take many
turns to complete. By calling these methods, we are essentially passing up on the ability to make
decisions on every turn. Fortunately, there is a class, called AdvancedRobot, that gives this control
back to us. To use all the methods provided by AdvancedRobot, all we need to do is make our robot
a subclass of it. For example, if we want to create a robot called MultiMoveBot that inherits from
AdvancedRobot, we'd use the following code:
Copyright IBM Corporation 2002
Rock 'em, sock 'em Robocode: Round 2
Trademarks
Page 1 of 19
developerWorks
ibm.com/developerWorks/
It is important to note that AdvancedRobot is actually a subclass of Robot, and our own
MultiMoveBot is in turn a subclass of AdvancedRobot, as illustrated in the class hierarchy shown in
Figure 1.
Remember, inheriting from a class -- or becoming a subclass of it -- means we get to use all its
public methods. As a result, we'll be able to use the methods from both the AdvancedRobot class
and the Robot class in our robot.
Next we'll explore the new capabilities we inherited from the AdvancedRobot class.
Note: This article has many associated code files. I recommend that you download the code now.
The article itself covers a lot of ground, so be sure to take time to analyze the code as you read
along for maximum benefit, as the code is supplementary to the discussion.
Page 2 of 19
ibm.com/developerWorks/
developerWorks
turnRight()
setTurnRight()
turnLeft()
setTurnLeft()
turnGunRight()
setTurnGunRight()
turnGunLeft()
setTurnGunLeft()
turnRadarRight()
setTurnRadarRight()
turnRadarLeft()
setTurnRadarLeft()
ahead()
setAhead()
back()
setback()
Notice the pattern here: all of the new non-blocking calls begin with set. Using these calls, we can
tell our robot to do multiple things at once. For example, take a look at the MultiMoveBot.java file in
the source code distribution. Our analysis of this source file begins with Listing 1:
This code instructs MultiMoveBot to turn right, move ahead, then turn the gun left -- all at the same
time during the next turn.
Note that all of the above methods return control to your program immediately without turning or
moving the robot and the gun. Nothing will happen until you give control back to Robocode. You
can give control back by making a blocking call (that is, any in the left column of the table above),
or by using the special execute() method.
The execute() method gives control back to the Robocode engine for exactly one tick. Like a
chess game, you are indicating that you have decided on your move for this turn, and Robocode
will now move your piece for you.
Our MultiMoveBot robot actually uses another technique, shown in Listing 2, to give control back to
Robocode:
Page 3 of 19
developerWorks
ibm.com/developerWorks/
The turn rate of the robot depends on its velocity. But because the gun is mounted on the
robot, and the radar is mounted on the gun, their turn rates do affect one another. If you need
to figure out the precise math, select the menu option Help->Robocode FAQ for a more
detailed answer.
The waitFor() method is a blocking call. It will block until the condition specified is satisfied. In
this case, we are telling Robocode to return control to us after it has completed the vehicle turn. Of
course, since we have set up the robot to move ahead and turn at the same time, it will actually be
moving in a curved motion.
The toggleDirection() method is actually our own method. It reverses the robot's direction (and
gun direction) each time it is called, as shown in Listing 3:
There are a couple of other methods we haven't covered; I encourage you to further review the
file at your leisure. To try out MultiMoveBot, select Battle->Open from the Robocode battlefield
window and load the battle named showMultiMove.battle. Observe the interesting movement
pattern that this robot makes.
To find out all the events that you as a robot creator can handle, select Help->Robocode API. Just
the Robot class API allows you to handle the following events:
onBulletHit()
onBulletHitBullet()
onBulletMissed()
onDeath()
onHitByBullet()
Page 4 of 19
ibm.com/developerWorks/
developerWorks
onHitRobot()
onHitWall()
onRobotDeath()
onScannedRobot()
onWin()
We handle an event by providing an event handler method. In fact, the class that we inherit from
already contains these methods. However, if we create our own method, that one will be used
instead of the one provided by our superclass. This is sometimes called a virtual method override,
or just override.
The added flexibility provided by the AdvancedRobot class includes the ability to create our own
custom event. A custom event is one in which we can define the condition when Robocode will call
us. For example, take a look at CustomEventBot shown in Listing 5:
This code shows how to create two new custom events using a mechanism known as an
anonymous class. Inside the addCustomEvent() method, we are defining a new subclass of the
Condition class and overriding its test() method. This new subclass is anonymous, meaning it
has no name. The argument, such as "LeftLimit", is passed as an argument to the constructor
method of the Condition class.
Figure 2 shows the coordinates and direction conventions used by Robocode. The "LeftLimit"
custom event that we have defined will be triggered whenever our robot has a heading of 90
degrees or less. The "RightLimit" condition will be triggered whenever the heading is 270
degrees or higher.
Page 5 of 19
developerWorks
ibm.com/developerWorks/
To handle these custom events, the AdvancedRobot class has an onCustomEvent() method that we
can override. In our case, we switch the turn direction of the vehicle and gun, as shown in Listing
6:
Together, our custom event definition and handler restrict the turn of the robot between 90 and 270
degrees, sending the robot into a twisting motion. Try this out by loading the twistergalore.battle
file, and start it. Select Options->Preferences->Visible Scan Arcs for a more dramatic
presentation of the "twist."
Page 6 of 19
ibm.com/developerWorks/
developerWorks
Take a look at the DuckSeekerBot code shown below in Listing 7. This simple bot will "target locate"
sitting ducks (the most docile robot in the Robocode samples collection) on the battlefield, move in,
and roast them.
Not surprisingly, this robot subclasses AdvancedRobot for extra flexibility. It also implements the
interface DuckConstants and shares those constants.
If you take a look at DuckConstants.java, you will find the interface definition. In this case, it
contains all the constants that we have been using all along (halfTurn, fullTurn, and so on.).
Therefore, implementing the DuckConstants interface is a shorthand way to include all the
constants into our class. In the code fragment above, notice that we created a variable called
curTarget, of the type Target. But Target isn't part of the Robocode library, so what is it?
If you carefully examine the latter part of DuckSeekerBot.java, shown below in Listing 8, it will
provide a clue. We see the definition of a completely new class inside the DuckSeekerBot class.
This is an inner class definition, or more specifically a "member class."
By declaring the class Target inside DuckSeekerBot as a member class, we have made it available
for use inside DuckSeekerBot. As you examine the code, you will see that curTarget is used to hold
the current target that is detected with the onScannedRobot() method. We use a very simplistic
means of homing in on the target: we turn toward it, move in, then fire. Listing 9 shows the code
inside the onScannedRobot() method that we need to roast our prey.
Page 7 of 19
developerWorks
ibm.com/developerWorks/
The DuckSeekerBot alternates between scanning for new ducks and locking in on a target. To see
the DuckSeekerBot in action, select Battle->Open and open the duckoAducko.battle file. This will
pit our DuckSeekerBot against a flock of sitting ducks.
In Listing 10, we first create a new instance of Duck, storing all the information we gathered from
the scan about the duck. Then, we make sure that we have not previously scanned this duck
(using the contains() method provided by the Flock/Vector class) before we add it to the flock.
Rock 'em, sock 'em Robocode: Round 2
Page 8 of 19
ibm.com/developerWorks/
developerWorks
If you examine Duck.java carefully, you'll see that we have defined how to compare one Duck
instance against another by overriding the implementation of the equals() method. This step is
essential in ensuring that the contains() method works properly.
It is interesting to note that java.util.Vector's contains() method was originally written by the
Java API library creators, none of whom had any way of knowing about our Duck class. Yet it works
perfectly with our Duck class. This is an example of Java language's use of polymorphism: the
programming logic within the contains() method works across any class(es), present or future,
that implements its own equals() method properly.
Finally, we must approach the inevitable: mathematics (more specifically, trigonometry). If you
examine the source code to Duck.java, you'll notice that in addition to all the methods from the old
Target member class of the DuckRoasterBot, it also has two other interesting methods:
bearingToDuck: Given a current x,y position, determine the bearing to the targeted duck.
distanceToDuck: Given a current x,y position, determine the distance to the targeted duck.
These methods make the Duck class quite versatile, as they hide (or encapsulate in Java
language parlance) the trigonometric mathematics used to determine these quantities. See
the accompanying sidebar Unit circle trigonometry to refresh your memory on this arcane but
incredibly useful topic. If you are keen and serious, you may also consider examining the detailed
source code to the bearingToDuck() and distanceToDuck() methods. These methods make
extensive use of the java.Math code library. The java.Math library contains enough sophisticated
mathematical methods to make any mathematician proud.
FlockRoasterBot uses the distanceToDuck() method to determine the nearest duck to roast. In
the getNextRoaster() method of the Flock class, shown in Listing 11, is the logic that selects the
nearest duck:
After we've determined the next duck to roast, the bearingToDuck() method can be used to home
in on it.
To see FlockRoasterBot in action, load in the RoastedDucks.battle file. You can see how similar
the action is to the "from-the-hip" style of the DuckRoasterBot. However, if you turn on the
Visible Scan Arcs option (select Options->Preferences->Visible Scan Arcs), you will see that
Rock 'em, sock 'em Robocode: Round 2
Page 9 of 19
developerWorks
ibm.com/developerWorks/
does not rescan between each duck; it just "knows" who to cook next through its
big picture of the battlefield.
FlockRoasterBot
We're going to revise our FlockRoasterBot to work in the new team play mode. Our team will
consist of one intelligent leader and two droids. We need to distribute the "intelligence" contained
in FlockRoasterBot among the team leader and the droids. To do this, we'll use the following two
classes:
Rock 'em, sock 'em Robocode: Round 2
Page 10 of 19
ibm.com/developerWorks/
developerWorks
FlockSweepLeader: Performs the scanning and maintains the intelligence map of the battlefield
and tells other team droids to sweep up the ducks. It will also participate in the roasting.
DuckCookerDroid: Monitors for "command to roast" from the FlockSweepLeader, then locks in,
homes in on, and roasts the target duck. Finally, it reports back to FlockSweepLeader once the
duck is bagged.
While it would be ideal if we could reuse Duck.java and Flock.java, which we created earlier with
the FlockRoasterBot, we cannot. In team play, a duck in a flock may be alive, but not locked in by
the FlockSweepLeader -- and is, thus, not the current target for the map maintainer class. That is,
the duck in question may be delegated to a droid for roasting. This means that we must be able to
determine whether a duck is claimed by one of our team members. The TeamDuck and TeamFlock
classes add this additional state to the common duck. They subclass Duck and Flock, respectively,
to reuse their built-in capabilities.
To
Message
Description
Leader
Droids
Droid
Leader
Leader
Droid
To see the code that starts the Cook-a-duck protocol, take a look at the assignMission() method
in FlockSweepLeader.java, shown in Listing 12:
Page 11 of 19
developerWorks
ibm.com/developerWorks/
The broadcastMessage() method is part of the new capability offered by the TeamRobot class.
REPORT_POSITION is a constant that is part of the TeamCommand.java interface. This interface
contains the protocol constants for the messages we send.
On the droid side, take a look at the onMessageReceived() method. This is where a team robot
will receive messages. Note that the message details are available as part of the MessageEvent
instance passed in. The content of the message itself can be of any serializable Java object
instance, and both the REPORT_POSITION and FLOCK_GONE messages send a String class as
message, while the duck assignment message from the FlockSweepLeader has a serialized
TeamDuck instance in the message. Listing 13 demonstrates the instanceof operator to determine
the class (type) of object that is passed in the message:
Duck-bagged protocol
The Duck-bagged protocol, detailed in Table 3, is used by the droid to signal the leader whenever
it bags a duck. The leader should update its intelligence map and check to see if it has another
duck for the droid. If it does, the leader uses the Cook-a-duck protocol to tell the droid.
To
Leader
Message
Serialized DuckBagged (see DuckBagged.java for
information)
Description
Notifies the leader that the assignment is completed and
the duck is in the bag.
Flock-gone protocol
The Flock-gone protocol, detailed in Table 4, is used by the leader to tell all the remaining droids
that the flock is gone.
To
Message
Description
Page 12 of 19
ibm.com/developerWorks/
Leader
Droids
developerWorks
The pickRandAvoidance() method is called whenever we hit a robot on our way to a locked-in
target. The onHitRobot() event handler, shown in Listing 15, calls this method:
Because both FlockSweepLeader and DuckCookerDroid will perform duck removal, they both need
the random back off and onHitRobot() implementations above. These common methods are
extracted (refactored in Java programmer's lingo) into a common base class called dwTeamRobot.
Both FlockSweepLeader and DuckCookerDroid are subclasses of dwTeamRobot. This type of
refactoring is very common in Java programming as requirements or designs change.
To see our distributed "duck sweeping team" in action, select Battle->Open and load the
teamsweep.battle file. This will pit our team consisting of one leader and two droids against a
group of four sitting ducks. You will be able to observe the delegation of command after the initial
scan by the leader. The collision-avoidance moves will also be quite visible. Figure 4 shows an
ongoing distributed duck sweep team in action.
Page 13 of 19
developerWorks
ibm.com/developerWorks/
Page 14 of 19
ibm.com/developerWorks/
developerWorks
maintains a big picture of the battlefield. It also makes extensive use of the multiple non-blocking
action of the AdvancedRobot class to make decisions on every tick, and uses custom classes
extensively. (Alisdair shares his anti-gravity technique in "Secrets from the Robocode masters" -see Resources.)
Page 15 of 19
developerWorks
ibm.com/developerWorks/
Page 16 of 19
ibm.com/developerWorks/
developerWorks
Downloads
Description
Name
Size
j-robocode2.zip
35KB
Page 17 of 19
developerWorks
ibm.com/developerWorks/
Resources
Download the complete source code for all the robots analyzed in this article.
Check out Sing Li's code for Rock 'em, sock 'em, Robocode, Round 2.
See the first article in this series for a basic introduction to Robocode (developerWorks,
January 2002).
Download Robocode.
Read all of the Secrets from the Robocode masters. This page will be updated as new tips
become available.
Robocode's creator, Mathew Nelson, maintains the official Robocode site. This should be the
first stop for anyone serious about Robocode.
For league play, Christian Schnell's RoboLeague is concurrently being developed with
Robocode.
A Yahoo Robocode group is available for sharing Robocode information if you are already a
Yahoo member.
Be sure to visit alphaWorks for other interesting early-access technologies.
Find other Java-related resources on the developerWorks Java technology zone.
Page 18 of 19
ibm.com/developerWorks/
developerWorks
Page 19 of 19