Tuesday, October 11, 2011

CrazyTracker

Introduction
If you have been keeping track of this blog, you might remember the post on Robocode katas.  While those exercises may have been a little mundane, they were my first step on the road to the ICS 314 Robocode Tournament for which I had to create my own robot to compete along with the various test cases needed to ensure that it works as intended.  Consequently, I have created a slightly more complex robot called "CrazyTracker" to compete in the tournament (and hopefully win!).

Overview
Robocode robots have three main functions: movement, firing, and targeting.  CrazyTracker incorporates all of these in different ways.

Movement:
CrazyTracker uses a random movement pattern (hence the crazy) where it moves to a random point between 50 and 150 pixels away every iteration of the run loop.  The idea is to prevent it from being predictable so it can dodge as many of the opponents bullets as possible.  In addition, it will move in a random direction away from a wall or rammed robot for a random distance between 50 and 100 pixels.  This is to prevent it from getting hung up on walls or enemies and to keep those enemies at a distance to reduce the amount of damage taken.

Using random movement to dodge enemy bullets.


Firing:
CrazyTracker uses two factors in it's firing algorithm as it uses both the current hit rate (how many bullets have hit so far / total number of bullets fired) and distance to determine the strength of the bullet to fire, if it should fire at all.  Here, the robot tries to reduce the energy wasted on missed bullets as it fires weaker, but cheaper bullets at longer distances and only fires at closer distances if the hit rate is too low.

Tracking:
CrazyTracker uses a simple targeting scheme.  The targeting algorithm basically just scans for the enemy, points the gun at it, and shoots.  While I did want to add some leading if the enemy robot is moving, the current algorithm only aims for the center of the enemy.  While this means that it is not as accurate as it could be, it still allows CrazyTracker to fire at enemies regardless of which way its body is facing which varies a lot due to the random nature of its movement.

The targeting algorithm faces the gun towards the enemy when it is time to fire.

Results
While I believe that my strategy is sound, it did not turn out so well in practice.  CrazyTracker can only reliably beat the SittingDuck, Crazy, and Fire robots and even then, it does lose to Crazy and Fire every once in a while due to its random nature.  The results versus the other sample robots are even worse with a 48% win rate against Tracker, 41% against Corners, 19% against RamFire, 18% against Spinbot, and an abysmal 15% against Walls.  This is probably due to the targeting algorithm as it fires at the center of the enemy, hence it has a hard time hitting fast moving or non-linearly moving targets like Walls or Spinbot.  Here some leading could have helped.  The low win rate against RamFire is likely due to the random nature as to how CrazyTracker handles running into another robot.  This means that it can get stuck in a corner or just spin in circles if it is unlucky, hence giving RamFire the advantage.  That behavior could be fixed, but I wanted to keep that random aspect even though it could create a disadvantage.

Testing
To check the the functionality of the CrazyTracker robot, I have created 6  JUnit test cases.  Two of these test cases are acceptance test cases which just check to see that CrazyTracker is in first place against a certain robot (Crazy and Fire in my case) and can beat that robot at least 80% of the time to account for its random nature.

In addition to these acceptance tests, I also have two unit tests.  The first unit test checks the a subclass of Robot that I implemented that provides a moveToPoint method and ensures that the moveToPoint method actually moves the robot to that point and that it properly handles points that are out of reach (i.e. outside of the battlefield) by trying to move to the closest valid point.  The second unit test checks CrazyTracker's calculatePower method.  This method determines the power of the bullet that the robot should fire (if any) depending on the its current hit rate and the distance from the target and the unit test ensures that it returns the expected values at the boundary cases.

Finally, I created two behavioral tests to check the firing and tracking behaviors of the robot.  For firing, I wanted to make sure that only the bullets of the powers specified by the calculatePower method were being fired, so it checks all of the bullets and sets a flag variable to true if a bullet with an unexpected power is found.  The tracking test pits CrazyTracker against the SittingDuck robot and checks that CrazyTracker's power never drops below 95.  This would be the case if the tracking algorithm works properly as each successful hit would restore energy, hence the making the robot gain energy if the bullets hit as they should.  The power threshold of 95 must be  used however as the initial bullet shot will drop the energy below 100 until it hits.

All in all, I believe these test cases cover the majority of CrazyTracker's features and functions and help ensure that everything is working as intended.

Conclusion
Overall, CrazyTracker was not only a good experience in learning Robocode, but also a great way to learn software engineering techniques like the use of automated quality assurance tools. As mentioned, CrazyTracker made me learn how to use testing tools like JUnit to help ensure that everything works and it has also showed the usefulness of other tools like Checkstyle, PMD, and Findbugs.  These tools do not do much in terms of testing, but they help to verify that my code is properly formatted and that there are no obvious bugs.  Finally, this project has taught me that adamantly sticking to a plan is not always the best idea.  From beginning to end, I kept the same general strategy behind the CrazyTracker robot even though it became obvious that it would be hard to beat many of the sample robots with that design.  Nevertheless, I stuck with that strategy until it was too late to make any real changes.  As a result, I learned that I should be more flexible as that could lead to more optimal solutions.  However, I do hope that CrazyTracker will be able to hold its own against the other robots in the tournament despite its weakness and hopefully the random number generator will return values that are in my favor.    Go CrazyTracker!

0 comments:

Post a Comment