Built a Haskell library to rank poker hands

This commit is contained in:
Correl Roush 2012-01-31 22:47:20 -05:00
parent defbd97d9b
commit 2398bdc7b2
2 changed files with 127 additions and 0 deletions

106
haskell/Util/Poker.hs Normal file
View file

@ -0,0 +1,106 @@
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)

21
haskell/Util/TestPoker.hs Normal file
View file

@ -0,0 +1,21 @@
import Test.HUnit
import Util.Poker
testFour = [parseCard "9D", parseCard "2H", parseCard "2S", parseCard "2D", parseCard "2C"]
testThree = [parseCard "9D", parseCard "2H", parseCard "5S", parseCard "2D", parseCard "2C"]
testFull = [parseCard "9D", parseCard "2H", parseCard "9S", parseCard "2D", parseCard "2C"]
testTwo = [parseCard "9D", parseCard "2H", parseCard "9S", parseCard "2D", parseCard "5C"]
testFlush = [parseCard "9D", parseCard "2D", parseCard "KD", parseCard "JD", parseCard "5D"]
testStraight = [parseCard "9D", parseCard "TS", parseCard "JD", parseCard "QD", parseCard "KD"]
testWheel = [parseCard "2D", parseCard "3S", parseCard "4D", parseCard "5D", parseCard "AD"]
tests = TestList [ "Rank 4 of a kind" ~: FourOfAKind ~=? (fst $ rankHand testFour)
, "Rank 3 of a kind" ~: ThreeOfAKind ~=? (fst $ rankHand testThree)
, "Rank a full house" ~: FullHouse ~=? (fst $ rankHand testFull)
, "Rank two pair" ~: TwoPair ~=? (fst $ rankHand testTwo)
, "Rank a flush" ~: Flush ~=? (fst $ rankHand testFlush)
, "Rank a straight" ~: Straight ~=? (fst $ rankHand testStraight)
, "Rank the wheel" ~: Straight ~=? (fst $ rankHand testWheel)
]
main = runTestTT tests