Skip to content

BetaFish Part 2 – Twitchy

19 March 2012

Last time, I introduced the idea of building an F# robot for Robocode. The second iteration contains some improvements:

namespace Neontapir
open Robocode
open System

type ActionType =
    | DoNothing // end of game
    | Search
    | Evade of float
    | Attack of float
    | AvoidWall

type BetaFish() =
    inherit Robot()
    let random = Random(DateTime.Now.Millisecond)
    let firepower = 1.0
    let randomTurn (robot:Robot) amount =
        let direction = random.Next 2
        match direction with
            | 1 -> robot.TurnLeft amount
            | 0 -> robot.TurnRight amount
            | _ -> failwith "Unexpected direction value"
     let mutable lastEvent : ActionType = Search
     override robot.Run() =
        while true do
            match lastEvent with
            | DoNothing -> ()
            | AvoidWall ->
                robot.Back 20.0
                randomTurn robot 30.0
                lastEvent <- Search           
            | Evade(bearing) ->
                randomTurn robot (90.0 - bearing)
                lastEvent <- Search
            | Attack firepower ->
                robot.Fire firepower               
            | Search | _ ->                           
                robot.Ahead 20.0
                robot.TurnRight 40.0
    override robot.OnBulletHit(event) =
        let newEvent = match lastEvent with
            | Attack strength -> Attack (strength + 1.0)
            | _ -> Attack 1.0       
        lastEvent <- newEvent
    override robot.OnScannedRobot(event : ScannedRobotEvent) = lastEvent <- Attack 1.0         
    override robot.OnBulletMissed(event) = lastEvent <- Search          
    override robot.OnHitByBullet(event : HitByBulletEvent) = lastEvent <- Evade(event.Bearing)
    override robot.OnHitWall(event) = lastEvent <- AvoidWall
    override robot.OnDeath(event) = lastEvent <- DoNothing
    override robot.OnWin(event) = lastEvent <- DoNothing

You may recall me saying that the first version of this robot could be defeated by a wall. While trying to implement this feature, it became clear that firepower didn’t represent enough state for the BetaFish robot to care about. There were a number of intermediate versions between last post’s code and the above, as I experimented with created a class to encapsulate state.

While trying to use multiple classes, I was stymied for a time by Visual Studio. In an F# project, it wasn’t clear to me how to spread my solution over multiple source files. I couldn’t reference my other classes. I discovered that in a functional language, it’s necessary to order the source files to resolve dependencies, so that items do not depend on other items that are compiled afterward. It turned out that the classes I wanted to use came after the main class in alphabetical order. In Visual Studio, you can reorder source files in an F# project by right-clicking the source file in Source Explorer and moving it up or down.

After a number of iterations, I discarded that secondary class in favor of a discriminated union, which I called ActionType. Conceptually, a discriminated union is kind of like a generic C# enumeration, but of course much more useful. For example, notice that the Evade and Attack elements take a float argument, whereas the others don’t. And pattern matching provides a more robust way to examine the contents of an ActionType than C# enumeration’s ever would. To accomplish this in C#, you’d need a hierarchy of classes.

The Run method has become more sophisticated also, now matching against the AttackType instead of just a scalar value. Notice that I’m able to name the union type’s argument inline, such as in the case of Evade‘s bearing. This is a feature of pattern matching, not discriminated unions, as you can see with strength in the OnBulletHit event handler.

While this code looks more impressive, it actually stinks in battle. If you try this robot, you’ll find that once BetaFish engages the enemy, the robot freezes in battle as soon as it’s hit, twitching until it is annihilated. Can you see why? The next article in the series contains the answer.

No comments yet

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s