Modelling problem domains in C# and F# – Part 1


I’m trying more and more to use F# for hobby projects etc. and finding, as usual, that there are very elegant, lightweight solutions to abstractions. This time: Monopoly.

Defining a Problem Space

A while ago I did something fairly simple in C# to simulate the Monopoly board that rolls two dice, moves a player on and records where they land. It then does this x times and shows what board spots get landed on the most. Here’s a simplified definition of the problem space: –

  • A board is made up of n positions
  • A position can be one of: –
    • a named property
    • a chance card
    • community chest card
    • station
    • free parking
    • go to jail
    • tax
    • jail
  • Some properties have movement actions associated with them. For example, landing on the Go To Jail position should move you to Jail. Landing on the Chance deck can have a number of random movements, such as moving to Go, Jail etc. etc.

Now I’m not going to write out the whole code etc. here for illustration. What I will do is give some snippets of how I defined the problem using C# constructs and how this might map into F#.

Mixing behaviour with data with Classes

So what if we wanted to do something simple like “handle a roll of the dice”? This should move to the appropriate position on the board, print it out, and also react if the position is a “movement” position like “Go To Jail”. Well, let’s first think about modelling the board. A relationship like this would in OO terms normally be defined through a type hierarchy; we might create a type for each position e.g. Property, Chance, Community Chest etc..

abstract class Position
{
   public abstract String Name { get; }
}

class Property : Position
{
   private readonly String propertyName; // e.g. Old Kent Road
   Property(string propertyName)
   {
       this.propertyName = propertyName;
   }

    public override Name { get { return this.propertyName; } }
}

class Station : Position
{
   private readonly String stationName; // e.g. Kings Cross
   Property(string stationName)
   {
       this.stationName = stationName;
   }

    public override Name { get { return this.stationName; } }
}

I’ve only implemented a few classes above as there’s just too much code to show the lot. To be honest, in reality I probably wouldn’t even bother with immutable types as above and would have just used public get/set properties; there’s just too much boilerplate.

We would also need something like an IMovementPosition for when you land on “Go to Jail” or “Chance” to move after landing to another position on the board.

class GoToJail : Position, IMovementPosition
{
    public override Name { get { return “Go To Jail”; } } // Always the same
    public Boolean CalculateMove(IEnumerator<Position> currentPosition)
    {
         MoveTo<Jail>(); // helper method that enumerates Position until we hit a Position that is of type Jail.
         return true;
    }
}

Notice how our Position types now have behaviour associated with them; getters that sometimes do logic (i.e. delegate to a private field), sometimes not. Some types have extra behaviour. Some have none. This is all classic OO that we learned years ago from Booch and his mates – behaviour and state etc.. So, given all code above, we can now do as follows to roll the dice and move.

class BoardController
{
   private IEnumerator<Position> position; // maintain current position

   public void Roll(int firstDie, int secondDie)
   {
      //hidden side effect on position
      MoveBy(firstDie, secondDie);
      Console.WriteLine(“Landed on {0}.“, position.Name);

      // see whether the position implements IMovementPosition
      var movementPosition = position as IMovementPosition;
      if (movementPosition != null)
      {
         //hidden side effect on position again, this time from another class
         if (movementPosition.CalculateMove(position))
            Console.WriteLine(“Moved to {0}”, position.Name);
      }
   }
}

Notice how we check whether the newly-landed-on position is an “IMovementPosition”, and if so we then execute code to calculate the “extra” movement e.g. Go To Jail

Conclusion

This is obviously a simplified example, and the code is more instructional to illustrate modelling some basic domain in C#. Things to note: –

  • Data and Behaviour travel together in a type.
  • State mutated throughout implicitly.
  • Extra types e.g. IMovementPosition required to indicate optional extra capabilities.

In Part 2 I’ll model the same thing in F#.

Advertisements

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

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

Connecting to %s