packages feed

monte-carlo-0.2: examples/Poker.hs

module Main where
    
import Control.Monad
import Control.Monad.MC
import Data.List
import Data.Map( Map )
import qualified Data.Map as Map
import System.Environment
import Text.Printf
    
-- | Data types for representing cards.  An Ace has 'number' equal to @1@.
-- Jack, Queen, and King have numbers @11@, @12@, and @13@, respectively.
data Suit = Club | Diamond  | Heart | Spade deriving (Eq, Show)
data Card = Card { number :: Int 
                 , suit   :: Suit
                 }
          deriving (Eq, Show)

-- | The number values of the aces and face cards.
ace, jack, queen, king :: Int
ace   = 1
jack  = 11
queen = 12
king  = 13

-- | A type for the various poker hands.
data Hand = HighCard  | Pair | TwoPair | ThreeOfAKind | Straight | Flush
          | FullHouse | FourOfAKind | StraightFlush 
          deriving (Eq, Show, Ord)

-- | Determine the hand corresponding to a list of five cards.
hand :: [Card] -> Hand
hand cs = 
    case matches of 
        [1,1,1,1,1] -> case undefined of
                           _ | isStraight && isFlush -> StraightFlush
                           _ | isFlush               -> Flush
                           _ | isStraight            -> Straight
                           _ | otherwise             -> HighCard
        [1,1,1,2]                                    -> Pair
        [1,2,2]                                      -> TwoPair
        [1,1,3]                                      -> ThreeOfAKind
        [2,3]                                        -> FullHouse
        [1,4]                                        -> FourOfAKind
  where
    (x:xs) = (sort . map number) cs
    (s:ss) = map suit cs
    
    isStraight | x == ace && xs == [ 10..king ] = True
               | otherwise                      = xs == [ x+1..x+4 ]

    isFlush = all (== s) ss

    matches = (sort . map length . group) (x:xs)

    
-- | Get a list of cards that make up a 52-card deck.
deck :: [Card]
deck = [ Card i s | i <- [ 1..13 ], s <- [ Club, Diamond, Heart, Spade ] ]

-- | Deal a five-card hand by choosing a random subset of the deck.
deal :: (MonadMC m) => m [Card]
deal = sampleSubset 5 52 deck

-- | A type for storing the frequencies of the various hands.
type HandCounts = Map Hand Int

-- | An empty frequency count.
emptyCounts :: HandCounts
emptyCounts = Map.empty

-- | Update the count of the hand corresponding to a list of five cards.
updateCounts :: HandCounts -> [Card] -> HandCounts
updateCounts counts cs = Map.insertWith' (+) (hand cs) 1 counts


main = do
    [reps] <- map read `fmap` getArgs
    main' reps

main' reps =
    let seed   = 0
        counts = repeatMCWith updateCounts emptyCounts reps deal
                 `evalMC` mt19937 seed in do
    printf "\n"
    printf "    Hand       Count    Probability     99%% Interval   \n"
    printf "-------------------------------------------------------\n"
    forM_ ((reverse . Map.toAscList) counts) $ \(h,c) ->
        let n     = fromIntegral reps :: Double
            p     = fromIntegral c / n 
            se    = sqrt (p * (1 - p) / n)
            delta = 2.575829 * se
            (l,u) = (p-delta, p+delta) in
        printf "%-13s %7d    %.6f   (%.6f,%.6f)\n" (show h) c p l u
    printf "\n"