Showing posts with label katas. Show all posts
Showing posts with label katas. Show all posts

Tuesday, November 8, 2011

WattDepot Katas

So I spent part of this week and all of this evening getting up to speed with WattDepot by completing a few practice katas. Each kata took me about an hour, though for different reasons.

The first couple katas were simple requests from a WattDepot server.
Given the sample code from the WattDepot client tutorial, this required only a big copy-and-paste and the modification of a couple lines.

These still took a couple hours, though, because I did the first couple katas in class on a day that we tried pair-programming. I think this might be a good practice once both programmers are comfortable with the API and understand the problem at hand. As it was, we spent most of the time setting up our Eclipse projects and then reading the assignment and the WattDepot documentation over each other's shoulders. I also switched groups halfway through, and we were also required to swap laptops periodically. This means the second pair I was in ended up starting over again at the beginning of one of the katas on a different machine.

Lulled into a false sense of security that these katas would all be as easy as the first couple, I returned to the project late this afternoon. I first took some time to install the FindBugs plugin in Eclipse. (I've been gradually installing plugins for tools that I have so far been using Ant to run. It's generally tedious and slow to run Ant compared to a plugin. But it's been a slow transition since I like to spend an hour or so reading the documentation and playing around with each plugin before I install the next one.)

After cleaning up what code I threw together in the last few minutes of class to see it work, I finished up kata #2. That kata and the ones after it all require sorting a list of key-value pairs by value. This stumped me a for a little while because this should be very easy... but it's not. I surfed a few 1/2 to 1-page posts of code that sort a Map by value. There were a few nice one-liners, but they required Google Collections and I didn't want to mess with an extra library.

In the end, this is my solution to sorting a key-value list by value. It's conceptually a "two-liner", but all the generics and the anonymous class make this look horrible:

  Map<String, Long> map = ...  //the map to be sorted by value

  //dump map into a list of key-value pairs...
  List<Map.Entry<String, Long>> entryList = 
      new ArrayList<Map.Entry<String, Long>>(map.entrySet());
      
  //...and sort it with a comparator that sorts based on values
  Collections.sort(entryList, new Comparator<Map.Entry<String, Long>>() {
      public int compare(Map.Entry<String, Long> left, 
                         Map.Entry<String, Long> right) {
        return left.getValue().compareTo(right.getValue());
      }        
  });

  //now print the list in value order
  for (Map.Entry<String, Long> entry : entryList) {
    System.out.print(entry.getKey() + ": " + entry.getValue());
  }

This solution also assumes you don't need map-like access to the sorted version, but just need to run through it for a simple task like printing.

The other time-consumer was learning how to use java.util.GregorianCalendar and
javax.xml.datatype.XMLGregorianCalendar. (There's also org.wattdepot.util.tstamp.Tstamp,
though I didn't find this to be as useful as I'd initially hoped.) This was another example
of something that should be easy but isn't. For example: "Give me the date of the most recent
Monday". It seems we have to start with the current time, for which I chose to use a GregorianCalendar object. Then, after a few lines spent modifying that time to the desired relative Monday date,
I had to translate it to an XMLGregorianCalendar. This was fairly long and messy with the various required
factory objects and exception handlers. I still like Java a lot, but I must admit that detractors
have a point about how much grinding tedious boilerplate you have to wade through sometimes.

I also need to take a minute at some point to figure out how to control Eclipse's insertion of try/catches. The quick default just wraps the one line in a try block, which requires so much reformatting that the feature is hardly worth using in most cases.

In the end, I got all the katas done. This was the first time I've written an application that deals with a web connection behind the scenes. It is pretty slow and variable (read: flaky) at times due to network access delays and potential connection time-outs. For my last kata--which took a couple minutes to gather all the data it needed--I added a little "status indicator" by printing a series of dots.

I spot-tested my katas as I went. However, if they were production code, I think I'd like to test them a little bit more to make sure I'm really pulling the right data and that all my calendar manipulations worked correctly. But since they are mainly katas intended to practice solving the problem, I think they went quite well.

Wednesday, September 28, 2011

Ant Katas

So I'm continuing to pick up programming skills. This week, I played around with Ant.

Ant is a build system for Java projects, like make is for C projects. Like all build systems, its primary function is to let you write scripts that intelligently compile the source code files in your project. But it lets you do a lot more than that. Directories referenced in a build script can be relative to the project's directory, meaning its easy to transport the whole project to a different system without having it break. Ant handles any differences across OS environments, such as whether the local system uses \ or / as the path separator. You can write scripts to automate other tasks, such as running test cases, executing other tools like as javadoc, or cleaning up stray files. The scripts can also test conditions in the local environment and adapt their behavior accordingly. When combined with Ivy, Ant can also do dependency management for you, downloading any 3rd party libraries required by your project. In short, Ant lets you automate a lot of routine programming tasks for a specific project. And it does this in a transportable way, across OS environments, so that anyone else who downloads your project can perform those task out-of-the-box too.

To get some practice writing Ant scripts (which are specified in XML), I went through a few katas for a simple Hello, World program. The Ant tasks involved compiling, cleaning, generating javadocs, and packaging the system for distribution.

What I learned from this is that the Ant Manual takes a bit of getting used to. It provides a decent tutorial, but it can be hard to use as a reference. For example, the manual includes a extensive list of all the possible task tags that Ant supports, including documentation of all the attributes they support. But for a while, the only place I could find a link to this important list was an in-text link buried in text of the "Using Ant -> Tasks" manual section. (In writing this post, I found there is a more direct link under Ant Tasks in the Table of Contents, but this is easy to overlook because its location between "Running Apache Ant" and "Concepts and Types" makes it look like a tutorial chapter title.)

I had a bit of trouble using Eclipse write these scripts. Eclipse has a special Ant editor which provides some helpful syntax highlighting and content assistance. However, I couldn't find a way to create an Ant file directly. Instead, I had to create a normal XML file, then right-click on it and open it in the Ant Editor (which required some browsing to find among the various editor options). Once opened there, it was possible to just hit the run button to run the Ant script, which was handy. And I only had to do this once for each file; after that, Eclipse remembered that it was an Ant file.

So I've covered the basics of Ant at this point. I'd like to mess around with filesets and paths a bit more. When I get a minute, I'd also like to figure out how to exclude Eclipse contents (such as the /bin directory and .project file) from distribution zips I build with Ant. Then, since I'm still learning Eclipse too, I want to see how Eclipse handles importing projects that don't have any Eclipse metadata included.

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...