Wednesday, September 28, 2011

Ant Code Katas

Build systems allow multiple developers on different platforms to coordinate work on a project with relative ease. Though I am by myself a single developer, I anticipate that I will have to work with others on projects in the future, and in any event becoming familiar with build systems should serve as a useful experience. For the moment, I am working in Ant (discussed in more detail at http://ant.apache.org ), which as I understand is the standard build system for Java applications. To familiarize myself with Ant, I have worked on eight Ant katas, similar in purpose to the Robocode Code Katas that I completed a little over a week ago.

1: Ant Hello World: Prints “Hello World.”
2: Ant Immutable Properties: Demonstrates the immutability of build properties; once defined, the value of a property cannot change.
3: Ant Dependencies: Shows how targets depend on one another.
4: Hello Ant Compilation: Compiles a file HelloAnt.java from a src directory and creates a build/classes directory for the class file to be placed into.
5: Hello Ant Execution: Runs the compiled HelloAnt program.
6: Hello Ant Documentation: Generates the JavaDocs for the HelloAnt program.
7: Cleaning Hello Ant: Adds a “clean” target to the compilation kata that removes the build directory.
8: Packaging Hello Ant: Creates a zip file containing the Ant katas and the source code for HelloAnt.

All eight of these katas were completed successfully, though as with the Robocode katas some difficulties became apparent only as I worked on the programming. Ant uses XML files, and though I have a passing familiarity with XML structure I have never actually written anything in XML for at least a few years. Fortunately these katas are relatively simple, and since Ant has its own keywords I did not have to learn XML and Ant simultaneously.

The main difficulty that I had in completing these katas was with Kata 5, in which I had to execute the HelloAnt program. The java task in Ant can take either a .jar or a class file as the file to run. Given that the kata preceding this compiled HelloAnt.java into a class file, I felt that using the class file was the more logical choice. However, the vast majority of the examples available on the Internet use .jar files with java.

This lack of examples led to the next problem. Kata 5 originally worked as intended, running the Java program without errors. However, when working on Kata 6, I realized that I had forgotten to specify the package that HelloAnt.java should be in. Fixing this was simple enough, and soon Kata 6 was working. Unfortunately, when I got to Kata 8, which required that everything in HelloAnt work perfectly, the code from Kata 5 began to produce errors. Specifically, Java could not find the HelloAnt.class file. After some investigation, I understood that the error had something to do with the package but could not find a solution; even specifying the exact folder that the file was in for the class path did not work. None of the resources online offered any substantive advice. It was not until much later, when I asked a classmate about the matter, that I learned that the class name for the java task had to be fully qualified. This resolved the problem immediately.

I understand why Ant would do this. After all, there could be classes in different packages with the same name, in which case the best and possibly only way to tell them apart might well be the package. At the same time, I cannot help but think that a system should be able to recognize that a file with the same name and in a series of folders that matches the package structure of the file that it is searching for could actually be the file that it is searching for.

Overall, the katas were not terribly difficult. The problem with the class name was the only significant delay in completing the katas, even if that one problem was severely stressful. Aside from that issue, there were only the usual problems with trying to learn what is essentially a new language for me. I do not consider myself fluent in Ant yet, but I can at least use Ant and write files that Ant can use, if with the occasional need to look through some reference source.

I learned a great deal about Ant from completing these katas. However, perhaps more importantly, I was reminded of the value of human sources of information. Most of the times when I want to learn something, I look on the Internet or in a book. Humans though serve as a source that can account for context and focus on relevant details. Although I still prefer written sources of information, I will have to remember that humans such as my classmates are still valid sources as well.

Monday, September 19, 2011

Robocode Code Katas

Robocode – Simple Robots

Robocode provides programmers with a game in which they can develop their programming skills in an enjoyable fashion. As one might guess from the name, Robocode revolves around designing and programming robots. These robots then compete against one another, using their movement, cannons, and radar to battle against their opponents. Robocode runs these simulations in real time for the user to see. More information concerning Robocode is available here:
http://robocode.sourceforge.net/

Rather than starting with creating a competitive robot, which would certainly be beyond my capabilities at the moment, I worked on creating simple robots to familiarize myself with Robocode. These robots serve as a sort of code kata, practices to help develop and maintain skill. The robots that I programmed are as follows:

Position01: The basic robot; does nothing aside from existing.
Position02: Move forward 100 pixels per turn and turns around upon hitting a wall.
Position03: With an integer n initialized to 15, for each turn move forward n pixels, then turn right and increment n by 15.
Position04: Move to the center of the field and spin around once.
Position05: Move to the upper right corner, then to the lower left corner, then to the upper left corner, and finally to the lower right corner before stopping.
Position06: Move to the center of the field, then move in a circle of approximately 100 pixels in radius.
Follow01: Select one enemy and follow that target.
Follow02: Select one enemy and follow that target, stopping if within 50 pixels of the target.
Follow03: On each turn, find the closest enemy and move 100 pixels away from that enemy.
Boom01: Remaining stationary, rotate the cannon and fire when the cannon is facing an enemy.
Boom02: Remaining stationary, select one enemy, rotate the cannon, and fire only when the cannon is facing that enemy.
Boom03: Remaining stationary, rotate the cannon and when the cannon is facing an enemy, fire a shot inversely proportional to the distance between the robot and the target.
Boom04: Remaining stationary, select one enemy and attempt to track the target with the cannon.

I worked on these robots in the order listed. The first difficulties I experienced were in Position04, specifically in calculating the bearing to the center. In theory, since the x and y distances to the center were known, the bearing to reach the center should have been simply arctan(dx/dy). However, doing this caused the robot to move in the direction opposite of the center under certain conditions. Through observing the values of dx and dy, I determined that the incorrect output occurred only when dy < 0 (that is, when the robot starts with a y-coordinate larger than the center of the field). The reasons for this are unknown; I understand that trigonometric functions and especially tangents can behave unusually if given negative input, but if so, then the error should have occurred when dx < 0 as well. Instead, the value of dy seems to have been the only factor in this error. Position04 works now, but an in-depth analysis of the trigonometry involved will be necessary before I can adequately explain this issue.

Position05 was somewhat problematic because the robot would run into a corner and then get itself stuck trying to move to the next corner. To resolve this, I added or subtracted the width of the robot from the corner coordinates, ensuring that there was enough room for the robot to maneuver.

I started to make new methods to handle movement in Position05. This was because doing so felt cleaner than placing the same movement code in run. Thus, my code from Position05 on contains reference to the move method, which takes two arguments for the x and y values to move the robot. This makes move relative to the position of the robot. When I start on a competitive robot, it may be useful to have another method similar to move that takes absolute coordinates as its parameters.

Position06 was probably the most difficult of the katas, due largely to my stubbornness. The initial problem was trying to find an equation for the circle. The equation that I remember is (x – h)^2 + (y – h)^2 = r^2. However, x and y would only be known for one point (the center of the field), so I would have to somehow vary one of those variables and calculate the value of the other. Fortunately, I was able to find the parametric equation for a circle, which solves for x and y independently of one another.

The next problem was that the radius of the circle was much larger than 100 pixels. After some testing, I finally realized that my calculations had found the center of the circle relative to the center of the field, while the calculations to follow the circle worked only for absolute coordinates. This too was fixed.

Finally, I had the preconception that the path the robot took to reach the center had to serve as a tangent line to the circle. This would make the path that the robot takes seem much smoother, as the robot would not have to make a noticeable turn to start the circular path. However, my method of determining the center of the circle caused some problems. To find the circle, I created a triangle with points at the center of the field, a point 100 pixels away from and on a line perpendicular to the heading of the robot, and a point on a line perpendicular to the aforementioned line that was also along the x or y-axis. I then used trigonometric functions to determine the distances to the center of the circle along the x and y axes. This created problems though in that sine and cosine would not provide consistent results in terms of these axes. For example, the opposite side of the triangle would be an x measurement in the first quadrant, while in the second quadrant it would be a y measurement. It took a very long time to account for this, and the solution given in the code feels somewhat inelegant because I had to exactly specify which cases needed sine and which needed cosine.

Starting with Follow01, I used a distinct search pattern for the radar that sweeps 180 degrees to one side, then back 360 degrees in the opposite direction, and then centers the radar with another 180 degree turn. This is terribly inefficient, as it essentially takes 720 degrees to cover 360 degrees worth of area. In theory, this pattern performs a more careful search of the area in front of the robot, increasing the likelihood that the robot will detect anything in front. In practice, this pattern has nothing to support it aside from looking more attractive than simply rotating in one direction.

Follow02 pursues a target until it is within 50 pixels of the target. While my implementation of Follow02 does this, it is practically impossible to differentiate between Follow01 and Follow02 in this regard. 50 pixels is not a large distance, particularly when the sizes of Follow02 and the target take up most of that space. If the specifications meant that there should be 50 pixels between the outsides of the robots rather than their centers, then I would simply add on the width of the robot to the 50 pixels restriction.

Follow03 posed a more discreet problem. For some reason, I thought that onScannedRobot would be called first for the closest enemy in the area scanned. Even if this was true, the area that Follow03 scanned with its radar might only be 180 degrees at a time, which might exclude the robot that really was closest. In any event, onScannedRobot is called as soon as the radar detects an enemy robot, which does not help in finding the closest robot. Upon discovering this problem, I created an ArrayList to store all of the ScannedRobotEvent instances that the radar produced and had Follow03 search through that list to find the enemy that was closest.

Boom03 caused some problems as I attempted to find a good formula to determine the strength of the bullets. Originally, I wanted to use nice numbers like 1, 2, 4, 8, 16, 32, 64, and so forth. Unfortunately, the domain of strengths for bullets must be between 0.1 and 3.0. In the end, I settled for a relatively simple formula based on the proportion of the distance to the enemy and the size of the field.

Boom04 was another difficult exercise in mathematics. Though getting the cannon to track the enemy was not too difficult, taking the fastest path to do so was problematic. Boom04 could turn right or left to follow the enemy, and on occasion it would take the “wrong” path around. This was due to working with both positive and negative values. Fortunately, after some extended thought I found a concise solution that only takes up a single line in code. As a result, the comments explaining how this turns the cannon to the correct heading are actually longer than the code that turns the cannon. To test how well Boom04 could track enemy targets, I added a command to fire the cannon once the gun was aligned with the enemy. As expected, trying to hit moving targets with Boom04 is an exercise in futility: Boom04 does not account for the velocity or heading of the target, so it cannot lead the enemy. A more noticeable consequence of firing the cannon, one that might prove more useful to know when creating a competitive robot, is that firing the cannon takes an appreciable amount of time. If Boom04 fires several times in a row, the cannon can fall far enough behind the movement of the enemy to lose track of its target.

Naturally, I learned a lot about how to program Robocode robots. I also gained experience in using Eclipse, particularly in regards to installing and integrating all the files necessary for Robocode. This project allowed me to put various design principles and standards into practice. Trigonometry has finally found a practical application after six years of disuse. This is my first experience with code katas, and they seem to serve as an excellent means of learning and reviewing.

Moving on from these katas, I intend to start designing a competitive robot. In competition with one another, it is Boom01 and Boom03 that unsurprisingly tend to survive the longest, as they are the only ones to utilize their cannons. However, Position02 and Position03 also do quite well; since they move around fairly rapidly, the Boom series of robots have difficulties in tracking their movement. If Boom04 is modified to actually fire bullets at the enemy rather than just tracking, it also does very well due to its focus on a single subject. My first goal with a competitive robot will be to defeat all of the sample robots, one-on-one or all at once.