-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Simple probability library for dice rolls, card games and similar -- -- A library designed to aid in easily calculating the probability of -- various outcomes of dice rolls and card draws. The central types are -- held in the Numeric.Probability.Game.Event module, the dice are -- defined in the Numeric.Probability.Game.Dice module, and the -- functions for calculating probabilities are in the -- Numeric.Probability.Game.Query module. The functions for -- playing cards cand be found in the -- Numeric.Probability.Game.Cards module, and more efficient -- calculation helpers can be found in the -- Numeric.Probability.Game.Cards.Hand module. -- -- Various examples are scattered throughout the library, but here are -- some more: -- -- Evalulates the chance of a coin toss turning up heads: -- --
--   chanceTrue coinToss
--   
-- -- Shows the chances of each outcome of rolling two six-sided dice, as a -- textual bar chart: -- --
--   show (2*d6)
--   
-- -- The chance of getting an 18 when rolling 3 six-sided dice and -- rerolling on any total less than 8: -- --
--   chancePred (== 18) ((3*d6) `rerollOn` [3..7])
--   
-- -- As a more complex example, this implements my memory/understanding of -- the original World of Darkness dice system -- (http://en.wikipedia.org/wiki/Storytelling_System). You roll a -- given number of 10-sided dice, rolling one extra for every 10 you -- score if you are specialised. The number of 1s on the original roll -- are subtracted from the total number of dice that equal or exceed the -- difficulty target: -- --
--   successes :: Int -> Int -> Bool -> EventM Int
--   successes target dice specialised
--     = do initial <- replicateM dice d10
--          extra <- if specialised
--                     then replicateM (count (== 10) initial) d10
--                     else return []
--          return (count (>= target) (initial ++ extra) - count (== 1) initial)
--     where
--       count f xs = length (filter f xs)
--   
-- -- If only all RPGs specified their rules in Haskell! -- -- See also the blog posts on the design of the library: -- http://chplib.wordpress.com/2010/08/13/nice-dice-in-haskell/, -- http://chplib.wordpress.com/2010/08/23/sharp-cards-in-haskell-drawing-cards/ -- and -- http://chplib.wordpress.com/2010/09/08/sharp-cards-in-haskell-the-odds/ @package game-probability @version 1.1 -- | A module containing the central type of the library, EventM, -- and various related helper functions. module Numeric.Probability.Game.Event -- | A probabilistic event with an outcome of type a. See the -- enact function to actually run the event and randomly pick an -- outcome. -- -- For an explanation of the Num instance, see the DieRoll type in -- the Numeric.Probability.Game.Dice module. -- -- The Eq instance compares the two distributions to see if they -- are equal. This looks at all the outcomes and sees if their -- probabilities are equal on the left-hand side and the right-hand side. -- For example, coinToss == fmap (>= 4) d6, but d12 /= d6 -- + d6. -- -- The Show instance will display a horizontal bar-chart of -- relative outcome probability. Note: this really is a relative -- probability -- common factors are cancelled, and is not a count of the -- different outcomes. If you wish to show the raw numbers, use show -- . outcomes instead. -- -- The Functor instance allows you to modify the outcome values -- without changing their associated probabilities. For example, fmap -- show d6 changes the outcomes into their String representations. -- -- The Applicative instance allows you to join together the -- results of two events in a predetermined manner. For example, -- makeEvent [id, (* 2)] <*> d6 allows you to roll a d6 -- that has a 50% chance of being doubled. Note that pure 6 is -- an event that is certain to produce the outcome 6. -- -- The Monad instance allows you to base the choice of the next -- event on the result of the previous event. For example, coinToss -- >>= x -> if x then d6 else d4 will roll a d4 50% of the -- time and a d6 the other 50%. Note that return 6 is an event -- that is certain to produce the outcome 6. data EventM a -- | Makes an event that has an equal chance of taking on the value of each -- entry in the list. Note that duplicates in the list are permitted and -- do have an effect: makeEvent [True, False] has a 50% chance -- of giving a True result, but makeEvent [True, True, False, False, -- False] only has a 40% chance of giving a True result. If you do -- not want this behaviour, use makeEvent . nub to remove -- duplicates. -- -- The result of passing the empty list is undefined. makeEvent :: [a] -> EventM a -- | Given a list of events and their associated probabilities, forms a -- corresponding event. The probabilities must be non-negative. If the -- probabilities do not sum to one, they are all scaled linearly so that -- their sum is one. Duplicate items will have their probabilities added. -- -- The result of passing the empty list, a list containing negative -- probabilities, or a list where all the probabilities are zero is -- undefined. makeEventProb :: (Ord a, Real prob) => [(a, prob)] -> EventM a -- | Gets a list of all the outcomes of the event and their associated -- probability. You can be sure that the probabilities will all sum to 1, -- and that there will only be one item in the list per outcome. It is -- possible that some of the outcomes in the list will have zero -- probability. outcomes :: Ord a => EventM a -> [(a, Rational)] -- | Actually enacts the event and produces a single result according to -- the probabilities in the EventM a parameter. enact :: EventM a -> IO a -- | An event with a 50% chance of giving True, and a 50% chance of giving -- False. coinToss :: EventM Bool -- | If the EventM a parameter returns a result equal to the first -- parameter, it is changed to be the second parameter; otherwise it is -- left untouched. For example replace 4 8 d4 has an equal -- chance of producing the outcomes 1, 2, 3 and 8, replace 10 0 d10 -- == z10, and replace 10 20 d6 == d6. subst :: Eq a => a -> a -> EventM a -> EventM a -- | Compares the outcomes of the two events, and works out the probability -- associated with the first outcome being greater than, equal to or less -- than the second outcome. The probabilites for each are returned in an -- associative map. -- -- Added in version 1.1. compareEvent :: Ord a => EventM a -> EventM a -> Map Ordering Rational instance Monad EventM instance Functor EventM instance Applicative EventM instance (Show a, Ord a) => Show (EventM a) instance Num (EventM Int) instance Ord a => Eq (EventM a) -- | A module containing various definitions of dice as random events, and -- a few associated helper functions. See DieRoll, which is really -- a synonym for EventM Int. module Numeric.Probability.Game.Dice -- | A type synonym for events with an integer outcome (i.e. all standard -- die rolls). -- -- The Num instance for EventM Int allows you to add the -- results of two die rolls, or subtract them (if it helps, (+) = -- liftA2 (+)). -- -- Multiplication works as follows. d * e evaluates the first -- die roll, then sums that many rolls of the second. So 2 * d6 -- rolls two d6 and adds the outcomes. However, this definition means -- that d6 * 2 rolls one d6, then effectively scales the result -- by 2. And d6 * d4 rolls one d6, then rolls that number of -- d4, adding their results together. The simple rule when one -- of the terms is a constant is: use the constant on the left-hand side -- to get more dice, and use the constant on the right-hand side to scale -- the result. type DieRoll = EventM Int -- | A nice synonym for enact: actually rolls the die and produces a -- single result according to the probabilities in the EventM a -- parameter. roll :: DieRoll -> IO Int -- | A die with an equal chance of rolling 1, 2, 3 or 4. d4 :: DieRoll -- | A die with an equal chance of rolling 1, 2, 3, 4, 5 or 6. d6 :: DieRoll -- | A die with an equal chance of rolling 1, 2, 3, 4, 5, 6, 7 or 8. d8 :: DieRoll -- | A die with an equal chance of rolling 0, 1, 2, 3, 4, 5, 6, 7, 8 or 9. z10 :: DieRoll -- | A die with an equal chance of rolling 1, 2, 3, 4, 5, 6, 7, 8, 9 or 10. d10 :: DieRoll -- | A die with an equal chance of rolling 1 to 12 inclusive. d12 :: DieRoll -- | A die with an equal chance of rolling 1 to 20 inclusive. d20 :: DieRoll -- | A die with an equal chance of rolling 1 to 100 inclusive. d100 :: DieRoll -- | A die with an equal chance of rolling 0 to 99 inclusive. z100 :: DieRoll -- | Makes a die that has an equal chance of achieving the numbers 1 -- through the number given. d 4 has an equal chance of -- producing the outcomes 1, 2, 3 and 4, d 1 is equivalent to -- return 1 (a certain result of 1), and d is undefined -- for any number below 1. For convenience, all the standard dice are -- provided, e.g. d6 = d 6. d :: Int -> DieRoll -- | Makes a die that has an equal chance of achieving the numbers 0 -- through the one less than the number given. z 4 has an equal -- chance of producing the outcomes 0, 1, 2 and 3, while z 1 is -- equivalent to return 0 (a certain result of 0), and -- z is undefined for any number below 1. For convenience, -- several standard dice that can be interpreted with a lower result of 0 -- are provided, e.g. z10 = z 10. z :: Int -> DieRoll -- | Rerolls the die when the specified outcome(s) occur. This has the -- effect of removing the outcomes from the set of outcomes and rescaling -- all the other probabilities linearly to sum to 1. For example: -- --
--   d6 `rerollOn` [5,6] == d4
--   chancePred (== 12) ((2*d6) `rerollOn` [7]) == 1/30
--   
-- -- With the latter example, the standard chance of 12 on 2d6 is 1/36, -- which is rescaled by 36/30, the reciprocal of the chance of not -- hitting a 7. rerollOn :: DieRoll -> [Int] -> DieRoll module Numeric.Probability.Game.Cards -- | A collection/deck of cards. The collection of cards has no implicit -- order, and each card is deemed to be equally likely to be drawn. -- -- So, for example, makeCards ["a","a","a","b","c"] is a -- collection of cards with a 3/5 chance of drawing an "a". -- -- Note that in Cards and all functions using it, the Ord -- instance is considered to be authoritative. Imagine you have some type -- like: -- --
--   data MyCard = MyCard {cardType :: String, cardDescription :: String}
--   instance Ord MyCard where compare = comparing cardType
--   
-- -- If you then create a collection of cards, all those with the same -- cardType will be considered the same, and differences in -- cardDescription will be collapsed. So, for example you may -- find that: -- --
--   cardsMap (makeCards [MyCard "Sword" "Long Sword", MyCard "Sword" "Legendary Sword of the Ancient King of Rak'Tharr", MyCard "Shield" "Buckler"])
--    == fromList [(MyCard "Sword" "Long Sword", 2), (MyCard "Shield" Buckler", 1)]
--   
-- -- The two sword cards are indistinguishable from each other by the Ord -- instance, so an arbitrary card for the two is kept in the collection -- to represent them both -- the legendary sword is treated the same as -- the long sword (so, equally, you might get two legendary swords in the -- deck and no long sword). -- -- If you want the difference to matter, use an Ord instance that -- recognises the difference. If you want the difference to matter some -- of the time, and not matter at other times, you may want to use -- mapCards to either pick out just the aspects you are interested -- in, or to use a default value (e.g. empty description) for the aspects -- you are not interested in. -- -- The Monoid instance can be used to get an empty Cards -- object, and to add two collections of cards together. data Cards a -- | Gets a map from card to frequency for the given Cards item. cardsMap :: Cards a -> Map a Int -- | Gets a sorted list of cards. For example: -- --
--   ["a","a","a","b","c","c"] == sortedCards (makeCardsMap (fromList [("c", 2), ("b", 1), ("a", 3)]))
--   
sortedCards :: Cards a -> [a] -- | Counts the number of cards (i.e. the sum of the frequencies of each -- distinct card) in the collection. cardCount cards == length -- (sortedCards count) -- -- If you want the number of distinct cards in a collection, use size -- . cardsMap. cardCount :: Cards a -> Int -- | Makes a collection of cards from the given list. The order of the list -- does not matter, but duplicates are important: if a card occurs -- multiple times in the list, it will appear multiple times in the -- collection. So makeCards [a,b] has one card -- named "a" and one named "b", but makeCards -- [a,a,b,a] has three cards named "a" -- and one named "b". makeCards :: Ord a => [a] -> Cards a -- | Makes a Cards item using a Map from card to frequency. -- Any card with a frequency of 0 or less will be ignored. makeCardsMap :: Ord a => Map a Int -> Cards a -- | Adds the given card and frequency to the collection of cards. If the -- card is already in the collection, the frequencies are added. -- -- Example: -- --
--   addCard ("c", 2) (makeCards ["a","a","b"]) == makeCards ["a","a","b","c","c"]
--   addCard ("b", 1) (makeCards ["a","a","b"]) == makeCards ["a","a","b","b"]
--   
addCard :: Ord a => (a, Int) -> Cards a -> Cards a -- | Removes a given number of cards that match the given criteria. -- -- As the name suggests, the choice of cards removed is arbitrary. This -- function is mainly useful if you later want to check for the odds of -- finding a card that does match the given criteria, but first -- want to express that you know of many cards that don't meet the -- criteria that aren't in the deck. -- -- If not enough cards meet the criteria in the collection, all that -- don't meet the criteria will be removed. removeArbitrary :: Ord a => (a -> Bool) -> Int -> Cards a -> Cards a -- | Removes one of the given cards from the collection. This only reduces -- the frequency by one; it does not remove all of the given card from -- the collection. If the card is not in the collection, this has no -- effect. -- -- Example: -- --
--   removeOneCard "a" (makeCards ["a","a","a","b"]) == makeCards ["a","a","b"]
--   removeOneCard "c" (makeCards ["a","a","a","b"]) == makeCards ["a","a","a","b"]
--   
removeOneCard :: Ord a => a -> Cards a -> Cards a -- | Applies a function to the cards. Like fmap for Cards, -- but we can't use Functor because of the Ord constraint. -- -- If this function maps two old cards to the same single new card, their -- frequencies are added together, but otherwise the frequencies are left -- untouched. -- -- This function is particularly useful for narrowing the number of -- distinct cards; see functions in the -- Numeric.Probability.Game.Cards.Hand module. -- -- Example: -- --
--   mapCards (map toUpper) (makeCardsMap (fromList [("a", 2), ("A", 3), ("b", 2)])) == makeCardsMap (fromList [("A", 5), ("B", 2)])
--   
mapCards :: Ord b => (a -> b) -> Cards a -> Cards b -- | Removes the cards in the second parameter from the cards in the first -- parameter. If the frequency of a card in the second parameter is -- greater than or equal to the frequency of a card in the first -- parameter, all of them are removed. Negative frequencies are not -- possible. -- -- Example: -- --
--   makeCardsMap (fromList [("a", 3), ("b", 1), ("c", 2)]) `minusCards` (makeCards ["a","b","b","c"]) == makeCards ["a","a","c"]
--   
minusCards :: Ord a => Cards a -> Cards a -> Cards a -- | Draws one card from the given collection of cards at random. Returns -- the card, and the collection of cards after the card has been drawn -- (i.e. with one of that card removed). If the collection is empty, the -- result is undefined. -- -- Note that using this function repeatedly to draw a hand of cards can -- be quite computationally intensive; for more efficient methods, see -- the Numeric.Probability.Game.Cards.Hand module. -- -- Example: -- --
--   outcomes (drawOne (makeCards ["a","a","a","b"])) == [(("a", makeCards ["a","a","b"]), 3 % 4), (("b", makeCards ["a","a","a"]), 1 % 4)]
--   
drawOne :: Ord a => Cards a -> EventM (a, Cards a) -- | Draws the given number of cards from the given collection with -- replacement. Returns the collection of cards that will be drawn (and -- thus you can be sure that: cardsCount <$> drawReplace n -- cards will be n, provided cards is not empty). -- If the given collection of cards is empty, the result is undefined. -- -- Note that using this function to draw a hand of cards can be quite -- computationally intensive; for more efficient methods, see the -- Numeric.Probability.Game.Cards.Hand module. -- -- Example: -- --
--   outcomes (drawReplace 2 (makeCards ["a","a","a","b"])) ==
--     [(makeCards ["a","a"], 9 % 16), (makeCards ["a","b"], 3 % 8), (makeCards ["b","b"], 1 % 16)]
--   
drawReplace :: Ord a => Int -> Cards a -> EventM (Cards a) -- | Draws the given number of cards from the given deck of cards at -- random, without replacement. Returns the collection of cards that were -- drawn (the first part of the result pait), and the corresponding -- remaining deck of cards. If the deck is empty or does not contain -- enough cards to draw the specified number, the result is undefined. -- -- Note that using this function to draw a hand of cards can be quite -- computationally intensive; for more efficient methods, see the -- Numeric.Probability.Game.Cards.Hand module. -- -- Note that makeCards n cards == swap <$> makeCards (cardCount -- cards - n) cards; this method will be much more efficient with a -- smaller number as parameter than a larger number. -- -- Example: -- --
--   outcomes (drawNoReplace 2 (makeCards ["a","a","a","a", "b"])) ==
--     [((makeCards ["a","a"], makeCards ["a","a", "b"]), 3 % 5), ((makeCards ["a","b"], makeCards ["a,"a","a"]), 2 % 5)]
--   
drawNoReplace :: Ord a => Int -> Cards a -> EventM (Cards a, Cards a) instance Ord a => Monoid (Cards a) instance Show a => Show (Cards a) instance Ord a => Ord (Cards a) instance Ord a => Eq (Cards a) -- | A module containing some simple data definitions for a deck of playing -- cards. module Numeric.Probability.Game.PlayingCards -- | A playing card with a rank and suit. The ordering on them is arbitrary -- (by rank then by suit). data PlayingCard PlayingCard :: Rank -> Suit -> PlayingCard rank :: PlayingCard -> Rank suit :: PlayingCard -> Suit -- | The standard four suits of playing cards. The ordering on them is -- arbitrary (alphabetical, in fact). data Suit Clubs :: Suit Diamonds :: Suit Hearts :: Suit Spades :: Suit -- | The rank of playing cards. The ranking is specified ace-high, as this -- is how many games operate. If you wish to have an ace-low ordering you -- can use the AceLowRank newtype. data Rank Two :: Rank Three :: Rank Four :: Rank Five :: Rank Six :: Rank Seven :: Rank Eight :: Rank Nine :: Rank Ten :: Rank Jack :: Rank Queen :: Rank King :: Rank Ace :: Rank -- | A wrapper for Rank where the Ord, Enum and Bounded instances -- are adjusted to list Ace as the lowest item rather than the highest. newtype AceLowRank AceLow :: Rank -> AceLowRank alRank :: AceLowRank -> Rank -- | The standard full deck of 52 playing cards. deck :: Cards PlayingCard -- | Checks if the two cards have the same suit. sameSuit :: PlayingCard -> PlayingCard -> Bool -- | Checks if the two cards have the same rank. sameRank :: PlayingCard -> PlayingCard -> Bool instance Eq PlayingCard instance Ord PlayingCard instance Show PlayingCard instance Read PlayingCard instance Eq Suit instance Ord Suit instance Show Suit instance Read Suit instance Eq AceLowRank instance Show AceLowRank instance Read AceLowRank instance Bounded Rank instance Enum Rank instance Eq Rank instance Ord Rank instance Show Rank instance Read Rank instance Ord AceLowRank instance Enum AceLowRank instance Bounded AceLowRank -- | Functions for efficiently calculating the probability of drawing -- cards. Here are some examples of using the module. -- -- In the game Dominion you start out with a deck consisting of 7 -- "Copper" cards and 3 "Estate" cards. On your first turn you draw five -- cards from this deck. To calculate the chances of drawing the -- different number of "Copper" cards (as a map from number of "Copper" -- cards to probability), you can use this code: -- --
--   copperChances :: Map Int Rational
--   copperChances = chanceMap startingDeck (drawCount (== "Copper") 5)
--     where startingDeck = makeCards $ replicate 7 "Copper " ++ replicate 3 "Estate"
--   
-- -- You could equally define a data-type for the cards rather than use -- Strings, but often Strings are easiest for one-off queries. -- -- As a different example, in the game Ticket To Ride: Europe, the deck -- of cards consists of 12 cards of each of eight colours and 14 -- multi-colour cards. We can describe it using a custom data-type this -- time: -- --
--   data TTRECard = Purple | White | Blue | Yellow | Orange | Black | Red | Green | MultiColour
--   
-- --
--   ttreDeck :: Cards TTRECard
--   ttreDeck = replicate 14 MultiColour ++ concatMap (replicate 12) [Purple, White, Blue, Yellow, Orange, Black, Red, Green]
--   
-- -- In the game, there are always 5 communal cards visible. Imagine you -- wanted to calculate the probability of receiving a particular colour -- when drawing from the deck. You must first remove the cards in your -- hand and those visible communal cards (we'll ignore the discards), -- then calculate the probability for drawing one card with the -- draw function: -- --
--   colourChances :: Map TTRECard Rational
--   colourChances = chanceMap (ttreDeck `minusCards` (myHand `mappend` communal)) draw
--   
-- -- This will give you a map from TTRECard (i.e. colour) to probability. -- -- To continue with that example, when you build tunnels in the game, you -- must lay out the required number of coloured cards, then draw three -- from the deck. If any of the three match the colour of tunnel you are -- building, you must pay that many additional cards. You may want a -- function that, given your hand (we'll ignore the communal cards to -- keep the example shorter) and the length of the tunnel, works out if -- you are likely to make it. One way to do this is: -- --
--   tunnel :: Cards TTRECard -> Int -> Rational
--   tunnel myHand n = chance (ttreDeck `minusCards` myHand)
--                            (drawCount match 3 >>= ensure . (<= spare))
--     where
--       spare = length (filter match $ sortedCards myHand) - n
--       match a = a == MultiColour || a == tunnelColour
--   
-- -- That should be fairly fast. But to illustrate how to speed up these -- calculations, here is another, faster way to do this: pre-process the -- cards into those that match and those that don't, using -- chanceOn: -- --
--   tunnel :: Cards TTRECard -> Int -> Rational
--   tunnel myHand n = chanceOn match (ttreDeck `minusCards` myHand)
--                            (drawCount (== True) 3 >>= ensure . (<= spare))
--     where
--       spare = length (filter match $ sortedCards myHand) - n
--       match a = a == MultiColour || a == tunnelColour
--   
-- -- This may seem like a relatively small difference, and indeed it is a -- small change to the code. However, it will execute much faster, -- because the chanceOn function only has two different card -- values to consider: True, and False, so it just considers those two. -- Previously it had to consider the nine different types of card -- separately, even though only two would match (the function has no way -- of knowing that a priori). module Numeric.Probability.Game.Cards.Hand -- | A monad for describing drawing cards. -- -- The first parameter is the type of the card (this must match the deck -- you end up drawing from), the second parameter is the monadic return -- type as normal. -- -- Each action in the monad is the drawing of a card, see draw and -- similar functions. There is the notion of failure: badHand -- makes the current draw fail, as does drawWhere if no cards -- satisfy the criteria, and attempting to draw when there are no -- more cards will also fail. -- -- The Alternative instance allows you to choose between two -- sequences of draws. If the LHS of '(<|>)' fails, the right-hand -- side is used instead. empty is the same as badHand. data DrawM card a -- | Checks that the given property holds, otherwise fails the current -- draw. Its definition is simple: -- --
--   ensure b = if b then return () else badHand
--   
ensure :: Bool -> DrawM a () -- | Indicates that the current draw should not be continued. badHand :: DrawM card a -- | Tries to perform the two draws interleaved with each other in any -- sequence, favouring those where the left-hand side acts first. -- -- As an example: -- --
--   interleave (replicateM 2 (drawWhere (== "a"))) (replicateM 3 (drawWhere (== "b")))
--   
-- -- will attempt to draw two "a" cards and three "b" cards, in any order -- and return them as a pair. If you want to draw identical groupings -- like this where the exact values of the cards can vary, look at -- drawGroups. interleave :: DrawM card a -> DrawM card b -> DrawM card (a, b) -- | Draws a single card and returns it. -- -- If you are not interested in the value of the returned card, -- drawAny is much more efficient. If you want to constrain which -- card might be drawn, use drawWhere. draw :: DrawM card card -- | Draws any card from the deck. In cases where you are not interested in -- what the card is, this is much more efficient than draw. drawAny :: DrawM card () -- | Draws a single card that matches the given criteria (i.e. where the -- given function returns True for the card). -- -- For example: -- --
--   drawWhere (/= "c")
--   
-- -- will draw any card that is not "c". Note that: -- --
--   (draw >>= ensure f) == (drawWhere f >> return ())
--   
drawWhere :: (card -> Bool) -> DrawM card card -- | Draws cards until it draws a card that satisfies the given condition -- or it hits the optional limit of cards. If the limit is zero, the -- function will fail every time, 1 will only draw a single card, 2 will -- draw up to 2 and so on. -- -- All the cards drawn will be returned in order, therefore you can be -- guaranteed that the last card in the list (and only that card) -- satisfies the given function. drawUntil :: (card -> Bool) -> Maybe Int -> DrawM card [card] -- | Draws the given number of cards and then counts how many meet the -- given criteria. The definition is: -- --
--   drawCount f n = length . filter f <$> replicateM n draw
--   
-- -- Note that this is definitely NOT the same as replicateM n -- (drawWhere f). The drawWhere code makes sure that it -- draws n cards that meet the given criteria (and fails in other cases), -- whereas this function draws the given number then checks how many meet -- the criteria. Therefore this function will only fail if there are -- insufficient cards to draw that many. drawCount :: (card -> Bool) -> Int -> DrawM card Int -- | Draws the given number of identical cards from the deck. -- -- This corresponds to drawing one card from the deck with draw -- and then using drawWhere to make sure the rest of the cards -- match. The card that was drawn is returned (since all of them are -- identical, only a single example is returned rather than a list). drawSame :: Eq card => Int -> DrawM card card -- | Draws the given number of identical (by the given aspect) cards from -- the deck. -- -- This corresponds to drawing one card from the deck with draw -- and then using drawWhere with the given mapping function to -- make sure the rest of the cards match on the aspect specified. The -- card that was drawn is returned (since all of them are identical, only -- a single example is returned rather than a list). The order of the -- returned list is arbitrary. -- -- For example: -- --
--   drawSameOn (map toLower) 5
--   
-- -- will draw 5 cards (where the card type is simply String) that have -- matching names when compared case-insensitive. The return list you get -- might be something like ["a","A","A","a","a"]. drawSameOn :: Eq aspect => (card -> aspect) -> Int -> DrawM card [card] -- | Draws cards in groups of identical cards (but in any order) according -- to the given sizes. -- -- This function is best explained by example: -- -- -- -- It is perhaps helpful to think of this function in terms of poker -- hands. drawGroups [4,1] looks for 4-of-a-kind in a hand of 5, -- drawGroups [3,2] looks for a full house, drawGroups -- [2,2,1] looks for two-pair, while drawGroups [2,1,1,1] -- looks for exactly one pair. -- -- The order of groups requested corresponds to the returns. Thus, for -- example, this code should never fail a pattern match: -- --
--   do [[a1,a2], [b1,b2,b3]] <- drawGroups [2,3]
--   
-- -- The groups have no correspondence to the order in which the cards were -- drawn. So although the groups above and returned together, those 5 -- cards could have been drawn in any order, for example: [b2, a1, -- b3, b2, a2]. This function is intended for cases when you want -- particular identical groups but don't mind about the order. That is -- surprisingly fiddly to write without this helper function. drawGroups :: Ord card => [Int] -> DrawM card [[card]] -- | Like drawGroups, but considers them equal if their given aspect -- is equal. drawGroupsOn :: Ord aspect => (card -> aspect) -> [Int] -> DrawM card [[card]] -- | Calculates the chance of the given draw succeeding (i.e. not failing) -- with the given deck. Note that the return value of the draw is -- ignored; this function is only interested in whether the draw -- succeeds. -- -- Note that if you are only interested in partial aspects of the cards -- (e.g. just the rank in a deck of playing cards), chanceOn is -- much more efficient. See chanceOn for more details. -- -- Examples: -- --
--   chance deck (return ()) == 1
--   chance (makeCards ["a", "a", "b"]) (drawWhere (== "a")) == 2 % 3
--   chance (makeCards ["a", "a", "b"]) (drawSame 2) == 1 % 3
--   
chance :: Ord card => Cards card -> DrawM card a -> Rational -- | Calculates the chance of the given draw succeeding (i.e. not failing) -- with the given deck. Note that the return value of the draw is -- ignored; this function is only interested in whether the draw -- succeeds. -- -- The given function is used to transform the cards for drawing. This -- can make the function much more efficient if the transform maps -- several cards onto the same aspect. Consider if you wanted the -- probability of drawing two aces from a deck of playing cards. If you -- use chance, it will check all 52 distinct cards in the deck -- separately to see if they are aces when you are drawing. However if -- you use chanceOn rank, it can collapse the 52 playing cards -- into 13 distinct cards (one per rank) with frequency 4, and only check -- each of the 13 cards separately. Since this saving is made across -- repeated draws, using chanceOn rather than chance can -- reduce queries from taking many seconds into being instant. This also -- applies to all the other chance..On and event..On variants of -- functions in this module. -- -- Examples: -- --
--   chanceOn id deck m == chance deck m
--   chanceOn (map toLower) (makeCards ["a", "a", "A", "A", "b"]) (drawWhere (== "a")) == 4 % 5
--   
chanceOn :: Ord aspect => (card -> aspect) -> Cards card -> DrawM aspect a -> Rational -- | Calculates the probability of each result of the given draw with the -- given deck. The probabilities will exclude the chance of a failed -- draw; therefore the chance of a failed draw is 1 - sum (elems $ -- chanceMap ..). Alternatively you can incorporate the possibility -- of a failed draw with a Maybe wrapper using chanceMap cards -- (optional m). -- -- Examples: -- --
--   chanceMap (makeCards ["a","b"]) (drawWhere (== "a"))) == singleton "a" (1 % 2)
--   
-- --
--   outcomes (eventDraw (makeCards ["a","a","a","b","b"]) (drawSame 2)
--     == fromList [("a", 3 % 10), ("b", 1 % 5)]
--   
chanceMap :: (Ord card, Ord a) => Cards card -> DrawM card a -> Map a Rational -- | Like chanceMap but can be much more efficient. See -- chanceOn for an explanation of why. chanceMapOn :: (Ord a, Ord aspect) => (card -> aspect) -> Cards card -> DrawM aspect a -> Map a Rational -- | Turns the successful outcomes of the given draw into an EventM -- type, which will return the different values of the successful draw -- with their corresponding relative probabilities. Note that only -- successful draws are included; a failed draw will have a probability -- of zero. To incorporate the possibility of a failed draw, use -- eventDrawMaybe instead. -- -- As with other functions, eventDrawOn can be much more -- efficient; see chanceOn for details. -- -- For example: -- --
--   outcomes (eventDraw (makeCards ["a","b"]) (drawWhere (== "a"))) == [("a", 1)]
--   
-- --
--   outcomes (eventDraw (makeCards ["a","a","a","b","b"]) (drawSame 2)
--     == [("a", 3 % 5), ("b", 2 % 5)]
--   
eventDraw :: (Ord a, Ord card) => Cards card -> DrawM card a -> EventM a -- | Like eventDraw but can be much more efficient. See -- chanceOn for an explanation of why. eventDrawOn :: (Ord a, Ord aspect) => (card -> aspect) -> Cards card -> DrawM aspect a -> EventM a -- | Turns the outcomes of the given draw into an EventM type, which -- will return the different values of the draw with their corresponding -- probabilities. Successful draws are the Just values; Nothing indicates -- an unsuccessful draw, with its corresponding probability. -- -- As with other functions, eventDrawMaybeOn can be much more -- efficient; see chanceOn for details. -- -- For example: -- --
--   outcomes (eventDraw (makeCards ["a","b"]) (drawWhere (== "a"))) == [(Just "a", 1 % 2), (Nothing, 1 % 2)]
--   
-- --
--   outcomes (eventDraw (makeCards ["a","a","a","b","b"]) (drawSame 2)
--     == [(Just "a", 3 % 10), (Just "b", 1 % 5), (Nothing, 1 % 2)]
--   
-- --
--   eventDrawMaybe cards m == eventDraw cards (optional m)
--   
eventDrawMaybe :: (Ord a, Ord card) => Cards card -> DrawM card a -> EventM (Maybe a) -- | Like eventDrawMaybe but can be much more efficient. See -- chanceOn for an explanation of why. eventDrawMaybeOn :: (Ord a, Ord aspect) => (card -> aspect) -> Cards card -> DrawM aspect a -> EventM (Maybe a) instance Alternative (DrawM card) instance Functor (DrawM card) instance Applicative (DrawM card) instance Monad (DrawM card) -- | A module with functions for querying the probabilities of various -- outcomes. module Numeric.Probability.Game.Query -- | Gets the probability that the outcome will satisfy the given -- predicate. For example: -- --
--   chancePred (<= 2) d6 == 1/3   -- The chance of getting 2 or less on a d6
--   chancePred even d6 == 1/2     -- The chance of rolling an event number on a d6
--   
chancePred :: (a -> Bool) -> EventM a -> Rational -- | Gets the probability that the given relation will hold between the two -- events. For example: -- --
--   chanceRel (==) d6 d6 == 1/6   -- The chance of rolling doubles on d6
--   chanceRel (>) (2*d6) d12      -- The chance of beating a d12 with two d6
--   
chanceRel :: (a -> a -> Bool) -> EventM a -> EventM a -> Rational -- | Gets the probability that the given boolean-outcome event will give a -- True outcome. For example: -- --
--   chanceTrue coinToss == 1/2
--   chanceTrue ((== 3) <$> d6) == 1/6
--   
-- -- (For the latter example, chancePred is more concise.) chanceTrue :: EventM Bool -> Rational