- data Cards a
- cardsMap :: Cards a -> Map a Int
- sortedCards :: Cards a -> [a]
- cardCount :: Cards a -> Int
- makeCards :: Ord a => [a] -> Cards a
- makeCardsMap :: Ord a => Map a Int -> Cards a
- addCard :: Ord a => (a, Int) -> Cards a -> Cards a
- removeArbitrary :: Ord a => (a -> Bool) -> Int -> Cards a -> Cards a
- removeOneCard :: Ord a => a -> Cards a -> Cards a
- mapCards :: Ord b => (a -> b) -> Cards a -> Cards b
- minusCards :: Ord a => Cards a -> Cards a -> Cards a
- drawOne :: Ord a => Cards a -> EventM (a, Cards a)
- drawReplace :: Ord a => Int -> Cards a -> EventM (Cards a)
- drawNoReplace :: Ord a => Int -> Cards a -> EventM (Cards a, Cards a)

# Cards datatype

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.

## Accessing

sortedCards :: Cards a -> [a]Source

Gets a sorted list of cards. For example:

["a","a","a","b","c","c"] == sortedCards (makeCardsMap (fromList [("c", 2), ("b", 1), ("a", 3)]))

cardCount :: Cards a -> IntSource

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`

.

## Creating

makeCards :: Ord a => [a] -> Cards aSource

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".

## Modifying

addCard :: Ord a => (a, Int) -> Cards a -> Cards aSource

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"]

removeArbitrary :: Ord a => (a -> Bool) -> Int -> Cards a -> Cards aSource

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.

removeOneCard :: Ord a => a -> Cards a -> Cards aSource

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"]

mapCards :: Ord b => (a -> b) -> Cards a -> Cards bSource

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)])

minusCards :: Ord a => Cards a -> Cards a -> Cards aSource

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"]

# Drawing Cards

drawOne :: Ord a => Cards a -> EventM (a, Cards a)Source

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)]

drawReplace :: Ord a => Int -> Cards a -> EventM (Cards a)Source

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)]

drawNoReplace :: Ord a => Int -> Cards a -> EventM (Cards a, Cards a)Source

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)]