Tuesday, October 11, 2011

Competitive Robocode Robot

Overview

In continuation of my work on the Robocode and Ant katas, I have produced a competitive robot called “DeaconBlues” (though represented as “deaconblues” in Robocode itself) for use in the Robocode system. I use “competitive” in the loosest possible sense, of course; this particular robot has difficulty even in defeating some of the sample robots. However, this robot does have the potential to succeed so long as circumstances are not too adverse.

My objective in this project was to create a robot that could reliably defeat the sample Walls robot; my understanding from my initial observations of Robocode was that Walls would be the most difficult to fight against. However, I spent most of the time from when I finished the Robocode katas until the present on developing a targeting system that would have been extremely valuable in any competitive robot. Of course, being able to track an enemy is useful for any target, not just against Walls. I actually did manage to implement my system a few days before this writing. It was fast, reasonably efficient, and only required knowledge of basic trigonometry to understand. It also did not work, which was a significant problem.

Fortunately, I also had a backup plan for a robot that focused primarily on defeating Walls. This robot is described below.

Design

Movement
DeaconBlues moves in a pattern very similar to that of Walls in that it circles around the outer edge of the field. When fighting against robots that are not Walls, DeaconBlues and Walls are virtually identical in their movement. However, when DeaconBlues detects an enemy that appears to be using a Walls strategy, it determines whether the enemy is moving clockwise or counterclockwise around the field and then goes in the opposite direction. The hope is that doing so will result in a situation such as that in the picture below.


The sample Walls robot always tries to face toward the center of the field. This leaves its flanks vulnerable as it moves along the edges. As the picture shows, DeaconBlues is able to fire at Walls without fear of counterattack, even though Walls has nearly seventy more energy than DeaconBlues does. In fact, a few seconds later DeaconBlues was able to destroy Walls through a combination of ramming and shooting.


Targeting
Unfortunately, targeting is one of the weak points in DeaconBlues. Without the working targeting system that I was hoping to develop, DeaconBlues must simply fire upon seeing an enemy and hope that it is still there when the bullet arrives. DeaconBlues also falls a bit short in selecting targets, though this is not as great a problem if DeaconBlues only has to deal with a single opponent at a time. The one aspect of targeting that DeaconBlues is reasonably competent in is remembering the characteristics of its targets; the downside is that DeaconBlues must thus focus wholly on a single target or else invalidate all that information.

Firing
DeaconBlues is somewhat weak in terms of firing, for the same reasons as its weakness in targeting. However, DeaconBlues does have a formula to determine the strength of its shots, accounting for factors such as enemy velocity, heading, bearing, and distance. As mentioned in the Targeting section, DeaconBlues fires as soon as it sees its target. Aside from quite possibly missing a moving target, this makes it difficult to hit even stationary targets at times. This is because DeaconBlues fires as soon as its radar detects the target, which is in the moment when the radar touches the edge of the enemy. As a result, DeaconBlues is aiming for the edge, not the center of the enemy, and thus from difficult angles misses are much more common for DeaconBlues.

Results
DeaconBlues can reliably beat any of the sample robots provided with Robocode. The results provided here may be slightly optimistic about the performance of DeaconBlues, as usually the scores are much closer. Nonetheless, DeaconBlues has an excellent record against the sample robots, with single defeats only to SpinBot, Tracker, and Walls.

SpinBot is difficult to fight against largely because of the circular motion, which makes aiming difficult. The same problem exists for Crazy, but SpinBot has a more regular pattern. Since DeaconBlues moves along the walls, if SpinBot is also near a wall that DeaconBlues must pass by there will inevitably be some shots exchanged.

Losing to Tracker was a bit of a surprise. My best guess as to what happened is that DeaconBlues was stuck in a corner at some point where Tracker should shoot at DeaconBlues while DeaconBlues was too focused on escaping to fight back.
I had anticipated that Walls would be a difficult opponent. I believe that this is so because Walls stays on the outside, away from danger, where enemies only have 180 degrees of space in which to aim at Walls. Walls also benefits from constantly moving around, making it difficult for opponents to aim shots. Since it is a very effective strategy, I integrated it into DeaconBlues as its course of action when not hunting down Walls.

Improvements to DeaconBlues would naturally be in the area where it is currently weakest: targeting. A good targeting system should make DeaconBlues a much more effective combatant.


Testing
Testing DeaconBlues provided another level of challenges to overcome. This was really my first time working with JUnit, and as such there were a few early struggles to deal with. After the initial learning curve though, JUnit was not too difficult to get used to.

I created six separate tests for DeaconBlues. The first two tests were fairly simple initially, as I only had to set up SpinBot and Walls to fight against DeaconBlues. As is usual for me though, I managed to make both more complicated than necessary. DeaconBlues only has to beat SpinBot seventy-five percent of the time to pass the test, and to pass Walls DeaconBlues must obtain more points than Walls during their fights. Despite these tests being objectively rather easy, DeaconBlues has some difficulty with them. DeaconBlues is not a very consistent competitor: on one test it might win all of its matches against Walls while on the next it must struggle to win half of the battles.

Three of the other tests focused on the behavior of DeaconBlues. One tested the ability of DeaconBlues to move to the corners. Since the decision of which corner to move to is somewhat random, this test looks more at how truly random the randomizing method is, keeping track of how often DeaconBlues moves to each corner and examining whether the time spent at these different corners is equally distributed amongst the corners. Another test examines the ability of DeaconBlues to evade enemy fire. That test is slightly inaccurate: it does notice when DeaconBlues takes damage, but it does not adequately discern between the different potential causes for the change in energy. DeaconBlues could have fired its cannon or run into an enemy, both of which could also cause a decrease in energy. The final behavioral test is on the accuracy of fire that DeaconBlues has. As noted above, DeaconBlues is horribly inaccurate.

The last test is a fairly simple unit test that runs through some of the utility methods in DeaconBlues. The calculations are nothing too intensive, and one could probably go through them even without a calculator to prove that DeaconBlues is performing as expected in that regard.

Lessons Learned
I have learned that software engineering is certainly a very intensive process. As I write this, I have spent three of the last four nights without sleep in order to work on DeaconBlues. At the same time, the work is amongst the most rewarding that I have ever done. I enjoy the work, and perhaps the fact that it is an enjoyable project helps as well.

From the perspective of software development, in the future I would prefer to incorporate testing into my project much earlier than I did this time. Checkstyle in particular caused a brief moment of panic when it informed me that I had twenty-seven errors to fix this past evening. Most of those errors were in regards to not simplifying expressions (ex. writing “if (isWalls == true)” instead of just “if (isWalls).”) My instinctive reaction to this was to remove the Boolean value and the comparison from all of the lines that Checkstyle warned about, grumbling that this would make the code more difficult to understand. This had an immediate effect on the code, as “if (isWalls == false)” is somewhat different in meaning from “if (isWalls).” Fortunately, I was able to find and fix these errors (at least the ones that I could find). In any event, this sort of testing would be better done earlier in the development process so that these problems could be discovered and resolved with less difficulty.

1 comment:

  1. As an addendum to this entry, I should note that saying "I can just go back and fix the documentation when I am done" at any point past midnight is really not a good idea. As a result, looking through my code now I can find several stylistic errors, particularly in regards to the JavaDocs comments.

    ReplyDelete