Moving the BPN logic from C# to F# [Desiderius part #13]

Blog2 Comments on Moving the BPN logic from C# to F# [Desiderius part #13]

Moving the BPN logic from C# to F# [Desiderius part #13]

Like I wrote about a few weeks back, all the refactorings made me reflect on some names.

It started out simple, as I said three weeks back, Desi.Player I had in F# (so the type, not the class) should be called something like “direction” as it represents just where the players are. I think this is a nice example of why you should not forget names when you are are refactoring/evolving. When I was just building the bidding part, the direction was all I needed to represent a player, but now it is bigger.

Going even further, the game we built last weeks should actually be called “contractGame” as it is a game where the contract is known, it is initialized with the trump color which we only know if the contract is set.

I thus made two different types: contractGame and Deal, where the latter represents a game where the contract is not yet set:

module Deal = 
   type T = {Players: Player.T List}
   let create players = {Players = players}

module ContractGame = 
   type T = {Trump : Desi.Suit; Players: Player.T List}
   let create trump players = 
      {Trump = trump; Players = players}

So, now I would like to be able to initialize a deal in F#. But! The reader logic is in C# so that would create a circular dependency. Now being even more well vested in F#, I thought: why not move the logic (which I had in C#) to F# too?

Most moves were straightforward, as I used lots of lists, my code was already quite “functional”:

For example consider this:

public static List getHandList(string input)
{
   var hands = getHandsString(input);
   var ListofHandStrings = hands.Split(' ').ToList();

   //this list now contains 4 hand strings, with the suit separated by periods (i.e: .63.AKQ987.A9732)

   return ListofHandStrings.Select(x => toHand(x)).ToList();
}

This is almost F# 🙂 SO all in all the move was pretty easy. There was one exception though, the method getHand that transforms a string representing one hand (example: “A5.63.AKQ987.A9732 A8654.KQ5.T.QJT6 J973.J98742.3.K4 KQT2.AT.J6542.85”) into a hand type with a card list:

private static Desi.Hand toHand(string handString)
{
   var result = Desi.makeEmptyHanded
   if (handString != "-") //no hand given
   {
   var SuitList = handString.Split('.').ToList();
   var Suits = new List() { Desi.Suit.Spades, Desi.Suit.Hearts, Desi.Suit.Diamonds, Desi.Suit.Clubs };

   for (int i = 0; i < 4; i++)
   {
      foreach (char individualCard in SuitList[i])
      {
         var card = Desi.Card.NewCard(Suits[i], getValuefromChar(individualCard));
	 result = Desi.addCardtoHand(card, result);
      }
    }
}

Wow, what a method. I really had to think hard to remember what I was that I did here. We take the string for one hand, which look like this A5.63.AKQ987.A9732 A8654.KQ5.T.QJT6 J973.J98742.3.K4 KQT2.AT.J6542.85.

The four players are divided by spaces, so one player’s hand is: A5.63.AKQ987.A9732 The dots then separate the four suits in order. So A5 is spades and 63 is Hearts, etc.

What the code above does is it splits the first string on ‘ ‘ and than, we make a card with the rank from the string and the suit from the suit list that we have initialized in order. Because the ‘.’ indicate differences between the four suits, that works.

While a bit unreadable, the loop here is nice, as it allows us to do different things (make different suits) for different parts of the string. I must admit I missed the loops in F# and it took me a while to transform this. My first attempt was this:

let toHand (oneHand:string):Desi.Card List =
   match oneHand with
      | "-" -> List.Empty
      | _ -> 
         let suitList = oneHand.Split '.' |> Array.toList
         let suitOne = getElem* 0 suitList |> (toHandfromOneSuit Desi.Suit.Spades)
         let suitTwo = getElem 1 suitList |> (toHandfromOneSuit Desi.Suit.Hearts)
         let suitThree = getElem 2 suitList |> (toHandfromOneSuit Desi.Suit.Diamonds)
         let suitFour = getElem 3 suitList |> (toHandfromOneSuit Desi.Suit.Clubs)
         let allSuits = [suitOne; suitTwo; suitThree; suitFour]
         flatten allSuits

Where flatten is what you think it is. This is still a loop, more or less, but I already think it reads better than the C# loop.

But this intermediate step greatly helped, once I had this figured out, it was relatively easy to transform it into a map. First I made a list of all four suits in order, and paired every element with the corresponding cards from the string:

let suits = [Desi.Suit.Spades; Desi.Suit.Hearts ; Desi.Suit.Diamonds ; Desi.Suit.Clubs]
let suitsandSuitList = List.zip suits suitList

This yields a list of (4) pairs. We can then on each of these pairs apply toHandfromOneSuit and then we have the four lists we want to merge. As follows:

let allSuits = List.map toHandfromOneSuit (suitsandSuitList)

The only trick is here that we very slightly changed the signature of toHandfromOneSuit from:

let toHandfromOneSuit (suit : Desi.Suit) (s:string): Desi.Card List =

to:

let toHandfromOneSuit (suit : Desi.Suit, s:string): Desi.Card List =

because it is now working on pairs.

Dropping the intermediate value we get:

let toHand (oneHand:string):Desi.Card List =
match oneHand with
  | "-" -> List.Empty
  | _ ->
    let suitList = oneHand.Split '.' |> Array.toList
    let suits = [Desi.Suit.Spades; Desi.Suit.Hearts ; Desi.Suit.Diamonds ; Desi.Suit.Clubs]
    flatten (List.map toHandfromOneSuit (List.zip suits suitList))

Pretty nice eh?

—————-

*The getElem fuction is a helper I made, just swapping the arguments of nth:

let getElem i L =
 List.nth L i

This way I can combine it with the |> I guess the languages designers did not do it that way because you do not chain from a list to an element. Mayb this is bad style, I do not know 🙂

 

2 thoughts on “Moving the BPN logic from C# to F# [Desiderius part #13]

  1. Hi, that is a pretty cool refactoring. You can go a bit further 🙂

    let toHand = function
    | “-” -> List.Empty
    | _ ->
    let suitList = oneHand.Split ‘.’ |> Array.toList
    let suits =
    [ Desi.Suit.Spades
    Desi.Suit.Hearts
    Desi.Suit.Diamonds
    Desi.Suit.Clubs ]

    suitList |> List.zip suits |> List.map toHandfromOneSuit |> flatten

Comments are closed.

Back To Top