module Collection where
import Probability
(Dist, Probability, uniform, mapD, (??), oneOf, while, random, (~.))
import qualified List (delete)
type Collection a = [a]
-- this is a StateT
selectOne :: Eq a => Collection a -> Dist (a,Collection a)
selectOne c = uniform [(v,List.delete v c) | v <- c]
select1 :: Eq a => Collection a -> Dist a
select1 = mapD fst . selectOne
select2 :: Eq a => Collection a -> Dist (a,a)
select2 c = do (x,c') <- selectOne c
y <- select1 c'
return (x,y)
-- this is a replicateM with respect to StateT
selectMany :: Eq a => Int -> Collection a -> Dist ([a],Collection a)
selectMany 0 c = return ([],c)
selectMany n c = do (x,c1) <- selectOne c
(xs,c2) <- selectMany (n-1) c1
return (x:xs,c2)
select :: Eq a => Int -> Collection a -> Dist [a]
select n = mapD (reverse . fst) . selectMany n
-- * Example collections
-- ** marbles
data Marble = R | G | B deriving (Eq,Ord,Show)
bucket :: Collection Marble
bucket = [R,R,R,R,R, G,G,G, B,B]
jar :: Collection Marble
jar = [R,R,G,G,B]
-- pRGB = prob (just [R,G,B]) (select 3 bucket)
pRGB :: Probability
pRGB = (==[R,G,B]) ?? select 3 jar
pRG :: Probability
pRG = (oneOf [[R,G],[G,R]]) ?? select 2 jar
-- ** cards
data Suit = Club | Spade | Heart | Diamond
deriving (Eq,Ord,Show,Enum)
data Rank = Plain Int | Jack | Queen | King | Ace
deriving (Eq,Ord,Show)
type Card = (Rank,Suit)
plains :: [Rank]
plains = map Plain [2..10]
faces :: [Rank]
faces = [Jack,Queen,King,Ace]
isFace :: Card -> Bool
isFace (r,_) = r `elem` faces
-- isFace = (`elem` faces) . fst
isPlain :: Card -> Bool
isPlain (r,_) = r `elem` plains
ranks :: [Rank]
ranks = plains ++ faces
suits :: [Suit]
suits = [Club,Spade,Heart,Diamond]
deck :: Collection Card
deck = [(r,s) | r <- ranks, s <- suits]
-- * Example
{- | mini-blackjack:
draw 2 cards, and if value is less than 14, continue drawing
until value equals or exceeds 14. if values exceeds 21,
you lose, otherwise you win.
-}
value :: Card -> Int
value ((Plain n),_) = n
value (Ace,_) = 11
value _ = 10
draw :: ([Card], Collection Card) -> Dist ([Card], Collection Card)
draw (cards,cl) = fmap f (selectOne cl)
where
f (c,cl') = ((c:cards),cl')
drawTo16 :: t -> IO ([Card], Collection Card)
drawTo16 _ = while (\(cards,_)->(sum (map value cards) < 16))
(random draw) ([], deck)
win :: ([Card], b) -> Bool
win (cards,_) = sum (map value cards) <= 21
chanceWin :: IO (Dist Bool)
chanceWin = fmap (mapD win) ((100 ~. drawTo16) undefined)