Files
2025-08-03 20:16:55 -07:00

201 lines
6.3 KiB
FSharp

module _54
type Suit =
Diamonds | Clubs | Hearts | Spades
type Card =
Jack of Suit | Queen of Suit | King of Suit | Ace of Suit | Value of int * Suit
type Rank =
| HighCard of Card
| OnePair of List<Card>
| TwoPair of List<Card>
| ThreeKind of List<Card>
| Straight of List<Card>
| Flush of List<Card>
| FullHouse of List<Card>
| StraightFlush of List<Card>
| RoyalFlush of List<Card>
let pokerWinner =
let cardValue = function
| Value(v,_) -> v
| Jack(_) -> 11
| Queen(_) -> 12
| King(_) -> 13
| Ace(_) -> 14
let cardSuit = function Value(_,s) | Jack(s) | Queen(s) | King(s) | Ace(s) -> s
let values cards =
cards
|> Seq.groupBy cardValue
let isFlush cards =
cards |> Seq.groupBy cardSuit |> Seq.length = 1
let isStraight cards =
cards |> Seq.map cardValue |> Seq.distinct |> Seq.toArray
|> (fun vs -> Array.length vs = 5 && Array.max vs - Array.min vs = 4)
let isStraightFlush cards =
isStraight cards && isFlush cards
let highCard cards =
cards |> Seq.sortBy cardValue |> Seq.toList |> List.rev |> Seq.nth 0
let isRoyalFlush cards =
let hasAce cards =
cards |> Seq.exists (function | Ace(_) -> true | _ -> false)
isStraightFlush cards && hasAce cards
let multiples cards =
cards
|> Seq.groupBy cardValue |> Seq.map snd
|> Seq.sortBy Seq.length |> Seq.toList |> List.rev
//|> Seq.filter (Seq.length >> ((<) 1))
let rankCards cards =
let multiplesLength cards =
multiples cards
|> List.map Seq.length
|> List.filter ((<) 1)
let flattenMultiples =
multiples cards
|> Seq.collect (fun c -> c)
|> Seq.toList
match cards, multiplesLength cards with
| c,_ when isRoyalFlush c -> RoyalFlush cards
| c,_ when isStraightFlush c -> StraightFlush cards
| _,[3;2] -> FullHouse cards
| c,_ when isFlush c -> Flush cards
| c,_ when isStraight c -> Straight cards
| _,[3] -> ThreeKind flattenMultiples
| _,[2;2] -> TwoPair flattenMultiples
| _,[2] -> OnePair flattenMultiples
| _,_ -> HighCard (highCard cards)
let rankTwoHands (c1,c2) =
(rankCards c1, rankCards c2)
let compareRanks (r1, r2) =
let highCardValue = highCard >> cardValue
let compareHighCard (c1,c2) =
compare (highCardValue c1) (highCardValue c2)
let compareMultiples (c1,c2) =
let rec cm (m1, m2) =
match m1, m2 with
| [],[] -> 0
| h1::_, h2::_ when Seq.length h1 <> Seq.length h2 -> compare (Seq.length h1) (Seq.length h2)
| h1::_, h2::_ when compareHighCard (h1, h2) <> 0 -> compareHighCard (h1, h2)
| _::t1, _::t2 -> cm(t1,t2)
| _,_ -> failwith "huh"
cm (multiples c1, multiples c2)
let r1win, r2win = compare 1 0, compare 0 1
match r1, r2 with
| RoyalFlush(_), RoyalFlush(_) -> 0
| StraightFlush(c1), StraightFlush(c2)
| Flush(c1), Flush(c2)
| Straight(c1), Straight(c2)
-> compareHighCard (c1,c2)
| FullHouse(c1), FullHouse(c2)
| TwoPair(c1), TwoPair(c2)
| OnePair(c1), OnePair(c2)
| ThreeKind(c1), ThreeKind(c2)
-> compareMultiples (c1,c2)
| RoyalFlush(_), _ -> r1win
| _, RoyalFlush(_) -> r2win
| StraightFlush(_), _ -> r1win
| _, StraightFlush(_) -> r2win
| FullHouse(_), _ -> r1win
| _, FullHouse(_) -> r2win
| Flush(_), _ -> r1win
| _, Flush(_) -> r2win
| Straight(_), _ -> r1win
| _, Straight(_) -> r2win
| ThreeKind(_), _ -> r1win
| _, ThreeKind(_) -> r2win
| TwoPair(_), _ -> r1win
| _, TwoPair(_) -> r2win
| OnePair(_), _ -> r1win
| _, OnePair(_) -> r2win
| HighCard(c1), HighCard(c2) -> compare (cardValue c1) (cardValue c2)
let parseCard s =
let parseSuit = function
| 'H' -> Hearts
| 'D' -> Diamonds
| 'C' -> Clubs
| 'S' -> Spades
| _ -> failwith "unknown suit"
if String.length s <> 2 then failwith "wrong length"
let charVal (c:char) = int c - 48
let validVal v = v >= 2 && v <= 9
let suit = parseSuit s.[1]
match s.[0] with
| v when (charVal >> validVal) v -> Value(charVal v, suit)
| 'T' -> Value(10, suit)
| 'J' -> Jack(suit)
| 'Q' -> Queen(suit)
| 'K' -> King(suit)
| 'A' -> Ace(suit)
| _ -> failwith ("unknownCard " + s)
let parseCards (s:string) =
s.Split(' ') |> Array.map parseCard
let parseTwoHands s =
let c = parseCards s
(c.[..4] |> List.ofArray, c.[5..] |> List.ofArray)
// ["5H 5C 6S 7S KD 2C 3S 8S 8D TD";
// "5D 8C 9S JS AC 2C 5C 7D 8S QH";
// "2D 9C AS AH AC 3D 6D 7D TD QD";
// "4D 6S 9H QH QC 3D 6D 7H QD QS";
// "2H 2D 4C 4D 4S 3C 3D 3S 9S 9D"
// ]
System.IO.File.ReadAllLines(@"54_poker.txt")
|> Seq.map (parseTwoHands >> rankTwoHands >> compareRanks) |> Array.ofSeq
|> Seq.filter ((<) 0)
|> Seq.length
//
// ["KD QD JD TD AD"; // royal flush
// "2D 6D 3D 4D 5D"; // straight flush
// "8D TD QD JD 7D"; // flush
// "3D 3S 3H 5D 5H"; // full house
// "KS QD JD TD AD"; // straight
// "3D 3S 3H 5D 6H"; // 3 kind
// "3D 3S 4H 5D 5H"; // 2 pair
// "3D 3S 4H 5D 6H"; // 1 pair
// "KD 3S 4H 5D 6H"; // 1 pair
// ]
// |> Seq.map (parseCards >> Array.toList >> rankCards)
// |> common.crossSelfMapList
// |> Seq.map (
// fun (r1, r2) ->
// let cr = compareRanks (r1, r2)
// sprintf "%A %i %A" r1 cr r2
// )
// |> Array.ofSeq
// |> Seq.map (
// fun c ->
// let pc = parseCards c
// let rf = isRoyalFlush pc
// let s = isStraight pc
// let f = isFlush pc
// let sf = isStraightFlush pc
// let hc = sprintf "%A" (highCard pc)
// let rank = pc |> List.ofArray |> rankCards
// //sprintf "%s RF:%b SF:%b F:%b S:%b HC:%s" c rf sf f s hc
// rank
// )
// |> Seq.toArray