packages feed

roguestar-engine-0.4.0.0: src/AttributeGeneration.hs

-- | Generates random lists of specific data points "attributes" of any data type.
-- The attributes themselves aren't random, only their arrangement and frequency within the list.
--
module AttributeGeneration
    where

import Data.Ratio
import Data.List
import Control.Monad.Random
import Data.Monoid
import Control.Monad

-- | Description of the random data to be generated.
data AttributeGenerator a = 
      AttributeAlways {
          attribute_actual :: a,
          attribute_min_max :: (Integer,Integer) }
    | AttributeChoice {
          attribute_frequency :: Rational,
          attribute_yes :: [AttributeGenerator a],
          attribute_no :: [AttributeGenerator a] }

instance Monoid (AttributeGenerator a) where
    mempty = AttributeChoice {
        attribute_frequency = 0,
        attribute_yes = [],
        attribute_no = [] }
    mappend a b = mconcat [a,b]
    mconcat as = AttributeChoice {
        attribute_frequency = 1,
        attribute_yes = as,
        attribute_no = [] }

-- | Generate exactly n copies of an attribute.
attributeStatic :: Integer -> a -> AttributeGenerator a
attributeStatic n a =attributeMinMax (n,n) a

-- | Generates between a random number of copies of an attribute between a lower and upper bound.
attributeMinMax :: (Integer,Integer) -> a -> AttributeGenerator a
attributeMinMax min_max a = AttributeAlways {
    attribute_actual = a,
    attribute_min_max = min_max }

-- | Generates the first class of attributes some fraction of the time, and the other list the remainder of the time.
-- For example 'attributeChoice (1%3) [attributeStatic 1 True] [attributeStatic 1 False]' would generate 'True' 33% of the time.
attributeChoice :: Rational -> [AttributeGenerator a] -> [AttributeGenerator a] -> AttributeGenerator a
attributeChoice freq yes no = AttributeChoice {
    attribute_frequency = freq,
    attribute_yes = yes,
    attribute_no = no }

-- | A set of mutually-exclusive choices, with Integer probability weights.
attributeChoices :: [(Integer,[AttributeGenerator a])] -> AttributeGenerator a
attributeChoices [] = mempty
attributeChoices (x:xs) = attributeChoice (fst x % (sum $ map fst $ x:xs)) (snd x) [attributeChoices xs]

-- | Run the 'AttributeGenerator'.
generateAttributes :: (MonadRandom m) => AttributeGenerator a -> m [a]
generateAttributes (AttributeAlways { attribute_actual = a, attribute_min_max = min_max }) =
    do n <- getRandomR min_max
       return $ genericReplicate n a
generateAttributes (AttributeChoice { attribute_frequency = l, attribute_yes = yes, attribute_no = no }) =
    do n <- getRandomR (1,denominator l)
       case () of
           () | n <= numerator l -> liftM concat $ mapM generateAttributes yes
           () | otherwise -> liftM concat $ mapM generateAttributes no