This is article 3 in a series of posts in which I introduced the idea of building an F# robot for Robocode. The last installment showed an improved version of the robot that didn’t bump into walls, but did quake at the sight of an enemy.
The twitching behavior I described in the last post is a result of receiving a number of events each turn. When the robot is hit, it tries to evade. However, when it sights an opponent, it turns to attack. Evade, attack, evade, attack… the robot gets stuck in a loop as long as the opponent is facing it and continues to fire. Poor BetaFish. Let’s see if we can fix that:
There are a few minor changes worth mentioning here:
- I renamed the
DoNothing
ActionType
toEndGame
for readability - I’ve encapsulated the
defaultFirepower
andmoveUnit
values for reuse - I’m doing more sophisticated pattern matching, like the nested match in
Run
’slastEvent
matching for theEvade
type. - I dropped the type specifications on the handlers for Robocode events, since F#’s type inference engine can infer them
To solve the twitching issue, I created a shouldEvade
method that decides whether to try to evade fire. This version will stand and take it if it’s facing an enemy. I found it odd that I had to specify that bearing
was a float
type, until I realized that Math.Abs
has a number of overloads.
I changed the signature of randomTurn
so I could use the piplining operator (|>
). This operator allowed to me write robot |> randomTurn 30.0
, which reads more natually to me than randomTurn robot 30.0
. With pipelining, I’m able to say “with robot, do a random turn”. My code less like Yoda sounds, pipeline it makes.
I wrapped the code in Run
to solve another nagging issue. In previous versions, when the battle was over, BetaFish would often throw an exception or the battle simulator would have to terminate it. The try ... with _ -> lastEvent <- EndGame
facility allowed me to catch the DeathException
I often receive at the end of battle.
This BetaFish version, the one I’m submitting to the code kata, does far better than its predecessors. It almost beats the Fire sample robot. Fire often wins because it turns its gun and its body separately. If we get another go-round, I will try this strategy. I took a quick stab at it, but the first attempt did very poorly.
It still has problems, don’t get me wrong. It runs like a scaredy cat if it gets fired upon a lot. It’s performs poorly because it moves the whole tank body instead of just the gun or the radar. And it is easily defeated by the C# sample robot, that runs fast along the border and fires towards the middle. BetaFish can’t get a lock on it fast enough.
Beyond the robot, there are certainly more F# features to explore. For example, I’m halfway through reading “Real World Functional Programming” by Petricek and Skeet, and a future chapter teases at a better way to handle event registration and waiting on events to fire. I can’t wait!
I hope this article series has been of interest. Drop me a line if you found it interesting and would like to see more.