euler/haskell/Util/Poker.hs

106 lines
3.7 KiB
Haskell

module Util.Poker where
import Data.Char (digitToInt)
import Data.List (sort, sortBy, group)
import Data.Ord (comparing)
data Suit = Hearts | Diamonds | Clubs | Spades
deriving (Show, Eq, Ord)
data Card = Card { cardValue :: Int
, cardSuit :: Suit
}
deriving (Show)
instance Eq Card where
x == y = (cardValue x) == (cardValue y)
x /= y = (cardValue x) /= (cardValue y)
instance Ord Card where
compare x y = compare (cardValue x) (cardValue y)
parseCard :: String -> Card
parseCard s = parseCard' $ break (\x -> notElem x ('A':'K':'Q':'J':'T':['0'..'9'])) s
parseCard' :: (String, String) -> Card
parseCard' (v, s) = Card v' s'
where v' = case v of
"T" -> 10
"J" -> 11
"Q" -> 12
"K" -> 13
"A" -> 14
_ -> foldl (\x y -> x * 10 + y) 0 $ map (digitToInt) v
s' = case s of
"H" -> Hearts
"D" -> Diamonds
"C" -> Clubs
"S" -> Spades
_ -> error "Unknown card suit"
type Hand = [Card]
data HandRank = HighCard
| OnePair
| TwoPair
| ThreeOfAKind
| Straight
| Flush
| FullHouse
| FourOfAKind
| StraightFlush
| RoyalFlush
deriving(Show, Eq, Ord)
type HandValue = (HandRank, [Int])
rankHand :: Hand -> HandValue
rankHand h
| biggestSet > 1 = rankSets grouped
| isFlush h && isStraight h = (StraightFlush, cardValues $ fixStraight h')
| isFlush h = (Flush, cardValues h')
| isStraight h = (Straight, cardValues $ fixStraight h')
| otherwise = (HighCard, cardValues h')
where h' = take 5 $ reverse $ sort h
grouped = sets $ take 5 h
biggestSet = length $ head grouped
rankSets :: [[Card]] -> HandValue
rankSets (x:xs) = case (length x) of
4 -> (FourOfAKind, values)
1 -> (HighCard, cardValues $ concat (x:xs))
_ -> case rest of
(OnePair, values') -> case l of 3 -> (FullHouse, (cardValue $ head x) : values')
2 -> (TwoPair, (cardValue $ head x) : values')
_ -> (OnePair, values)
_ -> case l of 3 -> (ThreeOfAKind, values)
_ -> (OnePair, values)
where l = length x
rest = rankSets $ filter (\x' -> length x' <= l) xs
values = (cardValue $ head x) : (take 1 $ cardValues $ concat xs)
rankSets [] = (HighCard, [])
cardValues :: [Card] -> [Int]
cardValues = map (cardValue)
sets :: Hand -> [[Card]]
sets h = reverse $ sortBy (comparing (\x -> length x)) $ group $ sort h
isFlush :: Hand -> Bool
isFlush h = suits == 5
where groups = reverse $ sortBy (comparing (\x -> length x)) $ group $ sort $ map (cardSuit) h
suits = length $ head groups
isStraight :: Hand -> Bool
isStraight h = isStraight' $ sort $ cardValues h
isStraight' :: [Int] -> Bool
isStraight' [] = False
isStraight' (2:3:4:5:14:[]) = True
isStraight' xs = xs == [(head xs) .. (last xs)]
fixStraight :: Hand -> Hand
fixStraight (x:xs)
| 2 `elem` values && 14 `elem` values = xs ++ [(Card 1 (cardSuit x))]
| otherwise = (x:xs)
where values = cardValues (x:xs)