Tuesday, September 20, 2011

Robocode Katas

So for the past week or so, I've been coding robots. Well, virtual robots.

Robocode is a software architecture for constructing little tank-like robots. Each tank is equipped with treads, a rotating gun, and a scanning radar. You can then run battles to the death between the different tanks. Robocode handles all the game physics and animation so that you can just focus on the tank logic.

I've dabbled with virtual robots before. I teach an Introduction to Computer Science lab, and, for the past 2 or 3 years, I've had my students implement a virtual roomba as the last assignment. Like Robocode, I provide the architecture, rule system, and GUI interface, and students then provide the "vroomba" logic. After mastering such a simple system, I was excited to try something a little more complicated.

Luckily, this Robocode work has been part of my Software Engineering class. (Sadly, I probably wouldn't be able to find the time to play around with it otherwise!) We were given 13 "code katas" to bring us up to speed with Robocode. A code kata is basically what it sounds like: a challenging programming exercise intended to build skills. Since the purpose of a code kata is mindful practice, failure can be a learning experience rather than a costly embarrassment, as it would be if the same lessons were being learned as part of a large production project.

These katas were a great way to pick up Robocode, and I recommend them to anyone just starting out with it. Each one tackles another aspect of tank management.

Here are the katas, and what I personally learned from each one. (Most of these lessons were fairly technical, though, and so they may not be of much use to you until you try Robocode yourself!)

Position01: The minimal robot. Does absolutely nothing at all.

Learned: How to create and run a Robot. This is basically the "Hello, World" of RoboCode.

Position02: Move forward a total of 100 pixels per turn. When you hit a wall, reverse direction.

Learned: You can call the ahead(int) method with a negative value to move backwards. Same with the turning methods. A move ends early if you hit something, whether a wall or another tank.

Position03: Each turn, move forward a total of N pixels per turn, then turn right. N is initialized to 15, and increases by 15 per turn.

Position04: Move to the center of the playing field, spin around in a circle, and stop.

Learned: After 15 years of neglect, my trigonometry is very rusty!
It took me over 2 hours to get a working method to compute the absolute heading from Point 1 to Point 2. It seems that Robocode lacks a method for this sort of calculation, though it does have a method for plotting a heading towards another bot. I also learned how to use Eclipse to
refactor methods into a new superclass and proved that the Scala interpreter does indeed make for a very handy interactive Java console.

Position05: Move to the upper right corner. Then move to the lower left corner. Then move to the upper left corner. Then move to the lower right corner.

Learned: I needed to account for the tank's dimensions in order to make it into the corners without hitting the walls on the way in. It seems the (getX(), getY()) point is the tank's center. getWidth() and getHeight() give the bot's dimensions, though, so dividing these by 2 gave me an effective tank "radius". (I added 1 pixel to this value, just to be on the safe side.)

Position06: Move to the center, then move in a circle with a radius of approximately 100 pixels, ending up where you started.

Learned: Circles are too hard/tedious with a Robot (which, unlike an AdvancedRobot, can't turn while moving). So I just traced a diamond here. I also took a break to learn how to color my tank. Important lesson there: setting colors needs to be done in run() or later. Trying to do it in a constructor will cause the bot to explode at the start of each round.

Follow01: Pick one enemy and follow them.

Learned: This got me comfortable with the onScannedRobot(ScannedRobotEvent) method in particular and Robocode's event-driven approach in general. It's initially weird how frequently an infinite while loop in run() will be suspended by other events.

Follow02: Pick one enemy and follow them, but stop if your robot gets within 50 pixels of them.

Learned: This was a surprisingly simple modification of Follow01. Since a negative distance to
ahead(int) moves the bot backwards, I got some cute retreating behavior for free here.

Follow03: Each turn, find the closest enemy, and move in the opposite direction by 100 pixels, then stop.

Learned: Each robot has a unique name accessible with getName(). This is true even if there are more than one instance of the same bot class on the board at a time. From a classmate (Ted), I learned that you can turn the radar (or anything else) to Double.POSITIVE_INFINITY to turn forever without needing an infinite while loop.

Boom01: Sit still. Rotate gun. When it is pointing at an enemy, fire.

Learned: The gun takes a while to cool down. So, even though I continued to rotate my gun, scanning multiple bots per rotation, I could only fire a bullet about once per rotation.

Boom02: Sit still. Pick one enemy. Only fire your gun when it is pointing at the chosen enemy.

Learned: ScannedRobotEvent.getHeading() is the heading in which the scanned robot is pointing, not the heading from you to that bot.

Boom03: Sit still. Rotate gun. When it is pointing at an enemy, use bullet power proportional to the distance of the enemy from you. The farther away the enemy, the less power your bullet should use.

Boom04: Sit still. Pick one enemy and attempt to track it with your gun. In other words, try to have your gun always pointing at that enemy. Don't fire.

Learned: I set my radar to keep rotating in my run() method. Yet I found that the radar would stopped rotating whenever I tracked a moving tank with my gun. This produced some cute behavior, like a little dog that stops panting and puts his ears up while his
target is moving, but relaxes again once the target stops. But it's still a little weird. Presumably, ScannedRobotEvents fire so frequently that, if I update my gun position for each one, Robocode doesn't have time to get back to my run() method before another ScannedRobotEvent fires.

So not every bot here taught me something new, but all were good practice.
Trig review aside, I found the hardest one here was Boom02. I think I may have misinterpetted the requirements for this one, though. I actively tracked the bot with the gun rather than waiting for the bot to pass in front of me. This made Boom04 a bit redundant.

Now that I'm familiar with Robocode, I'm looking forward to designing a combat-ready bot! More on that soon...


  1. The DaCruzer robot was named after yours truly. Did you try to beat it yet?

    You should check out the katas I've been writing about on my blog if you want to get more programming practice. There's one kata I did in C recently that was hard to solve, mainly because I didn't have ArrayLists or other easy-to-use ADTs to help me out.