Playing the game [Desiderius part 9]

Hi Desiderius fans,

I can’t imagine anyone else is reading these posts but true fans. I’d like to extend a special welcome to Fred, thanks for favoriting my bridge tweets every weekend 🙂

A while back I was at DDD Europe where I played a real life game of bridge. Unfortunately my phone was dead so I don’t have proof. But I swear they were real and not programmed like my new bestie Desiderius (“Desi” as I call him)

After a few games of bridge, we also played a few rounds of the related but simpler Belgian game Manillen which is a bit like the Dutch Klaverjassen and also a bit like Rikken.

After some drinks I promised one of my new friends that I could use Desi to also program the game of Manillen, and also that I could do so during the train ride back (that might have been the gin-topic speaking)

If you have been following my posts, you might know that means I had to make the entire playing side of the game, as so far I only focused on the bidding part!

Unfortunately I did not make it in the train ride back, but I did 1) make some useful progress and 2) I gained some interesting insights.

Time pressure is a *@^*(!

My s.o. is often complaining about lousy code quality at work, and I often respond with: you can just refactor a bit and make it nicer as you go. I guess I forgot how annoying it was to finish code while on a schedule! (I am sorry sweetie!) I really felt my brain changing from “let’s make this a work of art” into “okay, I’ll just cut a few corners here and there. and there. And just one more, I’ll make it better later” Wow. Useful experience!

I started leaning back to C#

Some parts of my implementation so far have been made in in C#, for example, I did some string processing of the BPN format in C# as it is ugly code and I do not want to mess up my nice F# with it. I wanted to make the running of the game in C# anyway, as there is a table manager to connect with over http requests, but under time pressure I thought I’d program just a tiny bit of the playing logic there too. Of course that got out of hand quickly. Before I knew it, I have made a Game class in C# for example:

class Game
{
   private Desi.Suit trump;
   public Desi.Card winningCard(List<Desi.Card> cards)
   {
    //any trumps
       List<Desi.Card> trumps = cards.Where(x => x.Item1 == trump).ToList();
       if (trumps != null)
      {
         var maxRank = (trumps.Max(y => y.Item2));
         return trumps.First(x => x.Item2 == maxRank);
      }
      else
      {
         //get all cards of the openSuit
         List<Desi.Card> openCards = cards.Where(x => x.Item1 == cards[0].Item1).ToList();
          //this is never null because there is at least the open card itself of this suit
         var maxRank = (openCards.Max(y => y.Item2));
         return openCards.First(x => x.Item2 == maxRank);
       }
    }
}

Jugh! The uglyness! The .Item1 and Item2 are artefacts of an F# tuple, but I did not bother to make a helper, because OMG hurry!

In the mean time I moved the class to F#:

module Game =
   type T = {Trump : Desi.Suit}
   let create trump = {Trump = trump}
   let winningCard {Trump=trump} (trick: List<Desi.Card>):Desi.Card =
 
      let allTrumps = trick |> List.filter (fun x-> getSuit(x) = trump)
      let allTrumpsinOrder = List.sortBy (fun x-> getRank(x)) allTrumps
   
      match allTrumpsinOrder with
      | h::t -> h
      | [] ->
         let openSuit = List.nth trick 0 |> getSuit
         let allOpenSuit = List.filter (fun x -> getSuit(x) = openSuit) trick
         let allOpenSuitinOrder = List.sortBy (fun x-> getRank(x)) allOpenSuit
         List.nth allOpenSuitinOrder 0

For this to work, by the way, I also had to modify the Rank type from a normal type into an enum. We can only sort if we assign values to all options:

type Rank = A = 14 | K = 13 | Q = 12 | J = 11 | T = 10 | Nine = 9 | Eight = 8 | Seven = 7 | Six = 6 | Five = 5 | Four = 4 | Three = 3 | Two = 2

In addition to the Game code, I also made a helper module to pick apart the tuples.

module helper =
   let getSuit (c: Desi.Card) : Desi.Suit =
      match c with 
      | Desi.Card (s,r) -> s
   let getRank (c: Desi.Card) : Desi.Rank =
      match c with 
      | Desi.Card (s,r) -> r

Why did I not make it like this in the first place? As I said, first of all time pressure, but also I did not know how to properly organize classes in F#. So far, my F# projects so far have been small, and I basically used the F# side as static methods which I called from C#. When I was out of the train (and on the internet) I found this lovely article that helped me get it right.

I have said it before, and I will say it again: this dichotomy is why I love the F#/C# combo so much, better than languages like Scala (or JavaScript) where you can mix functional programming with imperative OO. In that case there is no clear one or the other, and most likely I would have left my code as it.

There are some more changes I made to the code during and after my train adventure, will post about them next weekend.

4 Comments

  1. Pingback: More playing [Desiderius part 10] – Felienne's blog

  2. Pingback: A tale on testing [Desiderius part #12] – Felienne's blog

  3. Kris

    Please make the font of the code samples smaller, having to scroll sideways to read them make it difficult to understand them.

    1. felienne (Post author)

      Done!

Comments are closed.