Wednesday, September 30, 2009

Quality Assurance: Robocode Improvements

For approximately the last month, I have been working with the Robocode project, which has me creating battle robots in Java. Initially, this project development was a completely individual assignment, where I programmed robots that were designed to run only on my computer. However, during the code review we did in class a few weeks ago, this design flaw was exposed when my partner had an extremely difficult time getting my robots to work on his computer. One of the solutions to this problem is the Ant build system combined with the Ivy dependency manager, which together not only include tools to put together projects for distribution, but also includes quality assurance tools that can be used check that files have the correct formatting.

In the past week, I have installed the Ant system on my computer, and used it in conjunction with Ivy to improve my PacerEvader robot that I had designed for the class Roborumble. The first thing that I did was to adapt the Ant code that my professor provided the class with for use with my robot, and then used the Checkstyle, PMD, and FindBugs quality assurance tools to check my code. Each of these tools does slightly different tasks, ranging from formatting, to analyzing the way code works, so it was important to run each of them. In total, the tools found 34 errors, with the following breakdown:
  • Checkstyle - 31 errors
  • PMD - 3 errors
  • FindBugs - 0 errors
The Checkstyle errors were all formatting errors, mostly dealing with a lack of white space. For instance, the following line of code:
fire(minPower+(maxDist-dist)*(maxPower-minPower));
initially returned 8 errors, because I had no whitespace preceeding or following each of the operators. I fixed it to read:
fire(minPower + (maxDist - dist) * (maxPower - minPower));
The PMD errors were slightly higher-level and included unnecessary return statements in functions.

I found that automated quality assurance tools are extremely useful because they provide an easy way to check formatting so that you don't have to worry about catching every little error while programming. I think that this would be extremely valuable if I got into a programming zone and wanted to write code without having to constantly worry about the formatting or optimizing my code. However, since these errors do need to be fixed eventually, it is advantageous to try to control the number of errors that these quality assurance tools would generate.

In addition to fixing the problems found by the quality assurance tools, I also created a basic junit test case for my robot, to ensure that my robot beats SittingDuck. Again, this utilized Ant and Ivy to run the test itself and check the results.

Finally, I made a small improvement to my robot's performance and prepared a new distribution file. The improvement was to add in another case where my robot would move to the opposite wall. My robot moves ahead and backwards along a wall, and if it is hit from nearly directly in front or from nearly directly behind, it will move to the opposite wall. This is to prevent the robot from getting caught in the first of a Walls-like robot that peeks ahead and continues firing until the wall it wants to move across is cleared.

My updated PacerEvader robot can be found here.
The distribution file for the robot can be found here.
Updated distribution file (10/5): here.

After completing this assignment for class, I am beginning to really appreciate the value of Ant. At first, I was skeptical about how much it would truly do to help me, but after completing this assignment, I see that the power is not only in its features for a build system, but also in its integration of Ivy, which allows it to pull together many additonal tools. Although I am still getting use to interpreting Ant code and doubt that I would be able to write Ant code on my own, I have already reaped many of the benefits from Ant. It provided an easy way to evaluate my code for possible errors, and also packaged my robot for me, for easier distribution to others. I look forward to having Ant available to me for future projects, and also learning how to adapt Ant further for use in other projects.

Monday, September 21, 2009

Robocode Battling: Battle 1

In my last entry, I wrote an analysis of the strategies used by a selection of the sample robots in Robocode, the battle robot program that I have been working with for the last three weeks. Over the last few days, I have worked to develop a robot that will generally defeat as many of these sample robots as possible, when placed in a 1-on-1 battle. This represents my first attempt to design a battle robot, after spending the last several weeks learning basic Robocode movements and studying sample strategies.

Before I began programming, I tried to think of what general strategy I should use. It seemed like it would be best to keep my robot constantly moving in some generally straight pattern, so as to avoid any bullets that are shot at me, as well as to avoid swerving back into stray bullets. Other than that, I decided to adopt one of the instructions from the basic robots that I constructed two weeks ago, to fire using power proportional to the enemy robot's distance.

I began by writing my own implementation of the sample robot Walls. Although this consistently beat all other sample robots as expected, I wasn't happy with the fact that I was simply copying the exact idea of a sample. Furthermore, my implementation of Walls had a very difficult time dealing with Corners.

I then decided to simplify my robot, by having it essentially pace back and forth along one wall, keeping the advantage of having fewer directions for enemies to target it from, as well as the advantage of being able to shoot at most enemies, since none could be directly behind my robot. However, again I had trouble dealing with Corners, since my robot generally shot straight ahead, and half of the time Corners would be to the side of me. I then gave my robot some evasion tactics, by having it move to the opposite wall when it hit another robot, and thus PacerEvader was born.

My robot's overall strategy can be summarized as follows:



PacerEvader
(Source code)
Movement: Moves back and forth along the top wall. If it hits a robot, it moves to the opposite wall and resumes moving back and forth.
Tracking: No sophisiticated tracking; it will look for enemies to target arbitrarily.
Firing: Fires when an enemy is spotted, using power proportional to the enemy's distance (more power for closer enemies).



Here are the results of 1-on-1 battles between my robot and each of the samples. As a note, points are awarded for a variety of battle elements, including successful shots, ramming into other robots, and surviving. The winner is based on overall score, and not the number of placings in survival. I have provided the score percentage, as well as the number of wins in actual battle.

Walls: score percent: 12; 3 wins, 97 losses
RamFire: score percent: 48; 66 wins, 34 losses
SpinBot: score percent: 73; 89 wins, 11 losses
Crazy: score percent: 78; 94 wins, 6 losses
Fire: score percent: 92; 99 wins, 1 loss
Corners: score percent: 85; 99 wins, 1 loss
Tracker: score percent: 55; 58 wins, 42 losses
SittingDuck: score percent: 100; 100 wins, 0 losses

So overall, my robot can win against SpinBot, Crazy, Fire, Corners, and SittingDuck. Furthermore, it approximately ties with RamFire and Tracker, leaving its only real problem opponent as Walls. I believe that PacerEvader is successful because it constantly moves, generally avoiding bullets from semi-stationary targets, and it does not waste power excessively on targets that it has little chance of hitting.

I had a lot of headaches while trying to develop my battle robot. Most of the problems occurred while I was still trying to implement a very close copy of Walls, and as I was trying to do too much with a single robot. I don't think I completely understand the order of events as they are triggered, since I was having trouble generating OnScannedRobot events when I needed them to be, despite putting in scan() commands when my robot's gun would be pointed directly at a target. This is still an unresolved problem, since I merely developed a crude, inefficient workaround before abandoning that battle strategy altogether. My main lesson learned from this was that it is impossible to program without having a firm grasp of what you are trying to do, as well as the tools that you're working with. Although the Robocode API was extremely helpful, I was thinking out of the scope of my programming abilities.

For future development, I would like to find a strategy that will reliably beat Walls without compromising results against too many of the other sample robots. If possible, I think that I should also look into other ways to earn more bonus points, since RamFire beats PacerEvader from the bonus points earned through ramming my robot, rather than surviving until the end of the battle.

Wednesday, September 16, 2009

Robocode Strategy Analysis - Preparing For Battle

“To be prepared is half the victory.”
- Miguel de Cervantes Saavedra

One of the main objectives of building Robocode robots is to program robots for competitions. Thus far, my Robocode experience has consisted of programming basic robots that often do not attempt to attack enemy robots in any way, shape or form. Now, I have embarked upon the first steps in the process of building a competitive robot.

The Robocode download includes fifteen sample robots that make use of a wide variety of strategies. I looked at some of these robots while completing my thirteen basic robots for class, but for the most part, they were robots that I could include in a field to test anything that was supposed to track and/or fire at enemies. So today, I examined eight of the sample robots, and looked at their approaches to the following elements of robot operation:
  • Movement: How does the robot move? Does it have an avoidance or following strategy?
  • Targeting: How does it find a target to fire?
  • Firing: What is its criteria for firing?
What follows are my observations on the robots that I studied.



Walls
"Moves around the outer edge with the gun facing in"
Movement: Generally ignores enemies during movement. If it collides with an enemy it moves away from the enemy slightly, before continuing its usual pattern of movement.
Targeting: Moves along outer wall in a regular pattern; fires at any enemy robot when scanned
Firing: Fires at any enemy

Overall observations: This seems to be a good way for a robot to stay alive, but it does not appear to be very efficient in killing other robots because the robot has very few opportunities to shoot at any one robot.



RamFire
"Drives at robots, trying to ram them. Fires when it hits them."
Movement: RamFire tries to ram into robots, which earns bonus points.
Targeting: It does not fire until it is hitting a robot. It does not track any enemy in particular.
Firing: It fires at any enemy, once it rams into the enemy. It uses a low enough power that the target will not die, so that RamFire can continue to ram into the enemy.

Overall observations: This seems to be a very effective way to earn bonus points in battle.



SpinBot
"Moves in a circle, firing hard when an enemy is detected"
Movement: Turns continuously in a circle; neither follows nor avoids enemies, although if it rams into another robot, it moves off to the side before resuming circling, thus giving it a slight avoidance technique.
Targeting: It continuously turns in a circle, searching for enemies.
Firing: Fires at any enemy spotted, with the maximum bullet power.

Overall Observations: This seems to be a good way to inflict a high amount of damage on enemies, but I'm not sure that it's a good idea to always use the highest possible power when firing at any enemy. It seems to have good evasion tactics against other robots, since it's more difficult for a robot to aim at SpinBot.



Crazy
"Moves around in a crazy pattern"
Movement: Does exactly as its name says; swerves left and right, reversing if it hits walls or robots. Moves away from robots it hits, so somewhat of an avoidance strategy, although it isn't proactive.
Targeting: Just looks for anything that crosses its path.
Firing: Fires indiscriminately at any enemy it sees.

Overall Observations: This robot really lives up to its name! It seemed to do reasonably well in combat, but I really couldn't follow what it was doing and a lot of what it succeeded in doing appeared to be luck, since it moves around randomly until it happens to hit something.



Fire
"Sits still. Spins gun around. Moves when hit."
Movement: Moves only when hit by a bullet.
Targeting: Spins its gun slowly, aiming at everything it detects.
Firing: Takes health into account while firing. Fires larger bullets if the target is healthy and nearby close. Otherwise, fires using power 1.

Overall observations: This is a simple robot that still attacks with some strategy.



Sitting Duck
"Sits there and provides a target until dead."
Movement: Does not move at all.
Targeting: Does not target at all.
Firing: Does not fire at all.

Overall observations: A good robot to use while testing, but it definitely does not have any strategies that I would want to make use of in combat.



Corners
"Moves to a corner, then swings the gun back and forth. If it dies, it tries a new corner in the next round."
Movement: Runs into a wall, turns left, and hits a corner. If it sees a robot, it stops and aims at it.
Targeting: Pans back and forth from the corner, targeting the first robot it sees.
Firing: Fires using more powerful bullets the closer the target is to itself.

Overall Observations: Corners seems rather vulnerable to other robots because it sits in one place for the entire round. Any robot that picked a specific target to attack would probably have a huge advantage against Corners.



Tracker
"Picks one robot, follows it to a certain distance, and attacks it until it's dead."
Movement: Scans the area, not moving until a robot is spotted. Once a target is spotted, Tracker follows it within 80 pixels or so, attempting to maintain this distance. It does not move if it loses it's target.
Targeting: Targets a single robot, and fires upon it until it's dead.
Firing: Fires with a power of 3 at the target robot no matter what.

Overall Observations: Tracker does what its name says very well. However, it doesn't seem like this is an effective single strategy for battle.



Overall, this exercise was useful because I was able to see the full range of possible strategies I could have my robots adapt. The one main idea that seemed to work was to have the robot keep moving - SittingDuck and Corners both were very unsuccessful in killing enemy robots. I think that I am beginning to formulate some ideas as to what strategies I would have a competitive robot implement, but the one thing that this exercise has shown me is that testing will be a big part of perfecting whatever plan I do decide to work on.

Monday, September 14, 2009

Robocode Edting - Conforming to Standards, and The Case of the Missing Symbols

As part 2 of the Robocode assignment for class (as discussed below), I went through all thirteen of my implementation files and edited them to match the following coding standards:
where the rules of earlier listed items superceed any following standards.

My edited code can be found here.

Coding standards are important because they promote understanding of a program, both by future users as well as the author. Programmers who carefully implement coding standards create code that works as it was designed to, because they document the purpose of each piece of a program. Furthermore, if the meaning of something is altered in the programming process, the programmer will become aware of such changes as variable, method, and class names no longer make sense. Coding standards are important because it creates a uniform work that can be read more easily by others. Even if people use slightly different coding standards, they will still be able to more easily adapt to a single style within a programming project than a programming style that jumps everywhere. Coding standards promote neat, organized code, and these characteristics will always increase the readability of a program.

For me, the editing process was relatively painless, since I had taken the time to organize and comment my code for the first submission. The two major things that I needed to do were to import the ICS SE Eclipse formatting file, which would provide a template for my code in Eclipse, including using spaces instead of hard tabs. Although this was something I had to do only once for the entire assignment, it was significant because I had no idea that it was possible to define your own formatting within Eclipse, much less how I could go about doing so.

Second, I had to edit the comments within my files. The biggest change was that I had to add in JavaDoc comments. Prior to this class, I honestly had no idea what a JavaDoc comment was, and before this specific assignment, I really didn't know how to use them. I'm actually surprised that none of my introductory Java courses at UH taught us about the JavaDoc comments; I remember my teachers emphasizing commenting, but we didn't learn anything about commenting conventions or JavaDocs. In my original submission, I had used JavaDoc comments once or twice without much thought; mostly copying the style of comments when I was working off a sample robot. A lot of the edits amounted to adding in the extra asterisk at the start of my comments and reorganizing what I had so that the first sentence was the best stand-alone description of what the robot did. It was good practice for me, though, and I think that I'll find it a lot more intuitive to write JavaDoc comments this way from the start in future assignments.

Another main change I had to make to my program's comments was to re-write them to be in third-person active voice. I don't feel like I have ever used a standard voice when writing comments, so this will give me a good base to work from in the future. One commenting standard that I have not used previously was using end-of-line comments to describe the purpose of a variable. In the past, I have worried that it would be too messy and confusing, but after being consistent about using end-of-line comments throughout the project, I can appreciate the fact that they make it much easier to determine the purpose of a variable.

Finally, I'd like to include a few observations I made while reformatting my Robocode project. First, I love Eclipse's "refactor" tool, which allows one to easily rename a project or a variable within a Java document. I had a few variable names that either no longer aptly described the variable's purpose, or were not formatted correctly, and it was extremely easy to change the names of these variables and feel confident that I had made all of the necessary changes. I did run into a problem, however, when starting up Robocode to test some changes I had made - Robocode was set to look for the robots in the newly non-existent project directory, Robocode. I had to rename my project back to the original name, open up Robocode, then rename the project to the correct one for the standards, and then edit the directory where Robocode would look for robot files. It was a complicated process, but I'm glad that I did catch it right away, because I would probably have panicked if I hadn't discovered the problem until later.

Next, I realized that the smallest things can trip you up, even when "just" editing. I decided to try to better optimize some of my implementations for the movement functions, as I noted that I wanted to in my previous blog entry. For the most part, I was able to get everything to work together without a hitch after looking at Edward Meyer's code. However, my robot that was supposed to move between all four corners would just not go in the correct direction when it was supposed to navigate between the top left and bottom right corners. I easily spent half an hour trying to figure out where I changed the code, which was simply a specific adaptation of my already-working code to move the robot to the center of the field. The final verdict? I had somehow deleted an end parenthesis to end a nested function, and had solved the problem by tacking on a parenthesis at the end of the line.

Overall, I feel that this assignment was an extremely valuable learning process. Although it required little to no actual coding, it showed me that the comments for a program are just as important as the code itself. Moreover, I now have guidelines available, so that it will be easier for me to write uniform comments on future programming projects. I think that formatting programs correctly will be much easier when the coding standards are enforced from the beginning, and this exercise will serve as a reminder of the importance of getting it right (or as close to right as possible) the first time through.



Important links referenced in this post:

Wednesday, September 9, 2009

Robocode

Over the past week, I have been working with the Robocode project, a Java-based project available on SourceForge.net that allows users to create and battle robots. I downloaded Robocode from SourceForge.net, installed it on my computer, and then used the Eclipse IDE to do all of my programming. I only used the basic Robot, although there are also the AdvancedRobot and JuniorRobot.

I encountered this project as part of my software engineering class, but I didn't immediately begin building the robots that we needed to for class. Instead, I began experimenting with Robocode by going through the wiki for the project. It had a very useful tutorial on integrating Robocode with Eclipse, as well as a walkthrough on creating a baisc bot. The first robot that I created was this sample, which essentially moved along the walls of the field, firing when an enemy was in sight. The sample provided me with a basic skeleton for the code for all of my subsequent robots.

After getting a feel for Robocode, I moved on the the actual assignment. We were given descriptions of 13 basic robots that we were to implement within a week. The robots were categorized by their overall goals: movement, tracking, and firing. The process of building these robots allowed me to get used to programming in Robocode, from finding functions in the Robocode API, to understanding all of the robot's functionalities. Another resource that I occasionally turned to was this class website. Although I wasn't able to access the sample robots, there were code snippets that helped to point me in the right direction for some of the robots for my assignment.

The movement robots introduced me to the robots' coordinate system and movement options. Some of the counter-intuitive things about Robocode is that its coordinate system has the origin in the lower left corner, and degrees are measured clockwise, from the top of the screen. The origin was counter-intuitive to my experience with Java applets, which have it located in the top left corner, and I expected the degrees measured according to the unit circle, counter-clockwise from the right side of the screen. The basic Robot class can only use one move at a time, so turning or moving ahead, but not both. Thus, my general strategy was to orient the robot in the correct direction, and then move ahead the appropriate amount. I was able to implement all of the moves relatively quickly, with room for improvement as I describe later.

The tracking robots were much more challenging for me. Despite all of the practice at having my robot moving in very rigid patterns, I found it hard to get my robot to track another moving robot. Eventually, I used the Tracker sample robot provided with the download as a basis for several of my tracking robots. I had to eliminate a lot of code that was based on firing at targets, but digging through the sample code gave me a lot of experience at seeing the Robocode functions and getting a feel for what is involved in finding and tracking a robot.

Finally, with the firing robots, I moved on to having my robot shooting at other robots. Again, I found the biggest obstacle in tracking a single opponent with my gun. I ended up adapting the code from one of my tracking robots to complete this task. Firing is easy to do - you use the function fire(), which takes a single parameter on how much power to use - the tricky part is in finding your target. You can also be smart about how much power you use, so as not to waste power on targets that are very far away.

Overall, it seems like coding robots to track well is the biggest challenge we were assigned. Given more time, I would have liked to play around with my tracking code further, to be able to understand the reason for picking the intervals at which to move the robot ahead the full distance, or nearly full distance. I did end up changing the numbers for my purposes, but I don't know what would have been optimal.

I was able to successfully implement all 13 robots. However, I would be interested in optimizing some of my movement robots. Several of them move in a stair-case motion, going up, then across, or vice-versa, instead of moving in a diagonal line. I implemented the robots this way after running into some difficulty trying to get them to complete moves with a single straight line and opting to code in a way that I knew worked and that would save me time coding.

My implementation of all 13 basic robots for class are contained in this zip file.


Important links referenced in this post: