Project Euler - Poker Hands (2) - F#
In previous post I showed a C# solution to the Poker-hands problem. Since I’ve become more and more interested in functional programming languages, I would like to create another solution in F# and give a comparison to the C# one.
##Solution
open System
open System.IO
type Player = | Player1 | Player2
let cards = ['0';'1';'2'; '3'; '4'; '5'; '6'; '7'; '8'; '9'; 'T'; 'J'; 'Q'; 'K'; 'A']
let royal = cards |> List.skip 10 |> Array.ofList
let cardOrder c = cards |> List.findIndex ((=) c)
let highToLow = cardOrder >> (-) 15
let isInfix (hand: char array) =
let cardss = cards |> Array.ofList |> String
hand
|> String
|> fun s -> cardss.Contains(s)
|> (&&) (hand.Length = 5)
let handValue str =
let freq = Array.groupBy fst >> Array.map (fun (n, v) -> (n, v.Length)) >> Array.sortBy (fun (c, l) -> 100 * (5-l) + highToLow c)
let hand = str |> Array.map (fun (s:string) -> (s.[0], s.[1]))
let values = hand |> Array.map fst |> Array.sortBy cardOrder
let valuesH2L = values |> Array.rev |> String
let suits = hand |> Array.map snd
let isFlush = suits |> Array.distinct |> Array.length |> (=) 1 // same suit
let isStraight = values |> isInfix // consecutive
let isRoyal = values = royal
let expFreq e a = a |> Array.map snd |> function | v when v = e -> a |> Array.map fst |> String |> Some | _ -> None
let (|FourOfAKind|_|) = expFreq [|4;1|]
let (|ThreeOfAKind|_|) = expFreq [|3;1;1|]
let (|FullHouse|_|) = expFreq [|3;2|]
let (|TwoPairs|_|) = expFreq [|2;2;1|]
let (|OnePair|_|) = expFreq [|2;1;1;1|]
match freq hand with
| _ when isFlush && isRoyal -> "900000"
| _ when isFlush && isStraight -> "8" + valuesH2L
| FourOfAKind s -> "7000" + s
| FullHouse s -> "6000" + s
| _ when isFlush -> "5" + valuesH2L
| _ when isStraight -> "4" + valuesH2L
| ThreeOfAKind s -> "300" + s
| TwoPairs s -> "200" + s
| OnePair s -> "10" + s
| _ -> "0" + valuesH2L
let playHand line =
let h1 = line |> Array.take 5 |> handValue |> List.ofSeq
let h2 = line |> Array.skip 5 |> handValue |> List.ofSeq
let rec winner = function
| ((a, b)::xs) when cardOrder a > cardOrder b -> Player1
| ((a, b)::xs) when cardOrder a = cardOrder b -> winner xs
| _ -> Player2
List.zip h1 h2 |> winner
File.ReadAllLines(@"p054_poker.txt")
|> Seq.map (fun s -> s.Split ' ' |> playHand)
|> Seq.filter ((=) Player1)
|> Seq.length
|> printf "Player 1 won %d"
##Comparison
This implemetaion has only 66 lines of code which is much lesser than its C# conterpart. In C# soultion, I defined an interface, an abstract base class to abstract and encapsulate game rules. The business logic is hidding in each sub-class with the overhead of inheritance.
....
public class Full_House: Rank {
public Full_House(IEnumerable<string> cards) : base(cards) {
}
protected override bool Match() {
return cards.GroupBy(c => Mapper[c[0]], c=> 1).Count(g => g.Count()==3) == 1
&& cards.GroupBy(c => Mapper[c[0]], c=> 1).Count(g => g.Count()==2) == 1;
}
protected override double Remainder {
get{
return GetGroups(3).First().Key * 15 + GetGroups(2).First().Key;
}
}
}
...
In contrast, F#’s Discriminated unions enables a concise coding style when working with hierarchical data. Buisness logic is a collection of partially applied functions. This enables data to be processed streamly and results in an expressive and succinct code.
match freq hand with
| _ when isFlush && isRoyal -> "900000"
| _ when isFlush && isStraight -> "8" + valuesH2L
| FourOfAKind s -> "7000" + s
| FullHouse s -> "6000" + s
| _ when isFlush -> "5" + valuesH2L
| _ when isStraight -> "4" + valuesH2L
| ThreeOfAKind s -> "300" + s
| TwoPairs s -> "200" + s
| OnePair s -> "10" + s
| _ -> "0" + valuesH2L