Focusing solely on the randomness aspect of the problem here, we see that the System.Random module in its version 1.2 provides an uniformR function that can produce one pseudo-random integer value from a specified range. Typically, we would need 5*5=25 values from the (0,3) range.
Side Note: Beware the changes between Random v1.1 and v1.2 are massive ones.
Alternatively, the same module provides a randomRs function, but that one provides an infinite list of integer values, hence cannot return an updated value of the generator. That might not be what you require.
We can use as our workhorse a function that returns a finite count of random integer values, together with an updated generator:
import System.Random
getManyInts :: RandomGen g => g -> Int -> (Int,Int) -> ([Int],g)
getManyInts g0 count range =
if (count <= 0) then ([], g0)
else let
(v,g1) = uniformR range g0
(vs,gf) = getManyInts g1 (count-1) range
in
(v:vs, gf)
In order to navigate from integer to Enum values, we will use a slightly improved Content type:
data Content = Bomb | One | Two | Three
deriving (Eq, Show, Enum, Bounded)
At that point, we can write a small test program that provides 5*5=25 random values of Content type:
main :: IO ()
main = do
g0 <- newStdGen
let count = 5*5
(loC, hiC) = (minBound :: Content, maxBound :: Content)
intRange = (fromEnum loC, fromEnum hiC)
(xs, g1) = getManyInts g0 count intRange
cs = (map toEnum xs) :: [Content]
putStrLn $ "contents: " ++ (show cs)
Test program output:
contents: [Two,Three,One,One,One,Two,One,Two,One,Bomb,Two,Two,One,Three,Bomb,Two,Two,Bomb,Three,One,One,Bomb,One,One,Bomb]
Addendum:
A better, polymorphic function:
Next, we can provide a function that returns a list of random values for any similar type:
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ExplicitForAll #-}
getManyEnums :: forall g e. (RandomGen g, Bounded e, Enum e) => g -> Int -> ([e],g)
getManyEnums g0 count = (map toEnum xs, g1)
where
intRange = (fromEnum (minBound :: e), fromEnum (maxBound :: e))
(xs, g1) = getManyInts g0 count intRange
This function can be specialized as required. For example:
Testing under ghci:
$ ghci
GHCi, version 8.10.5: https://www.haskell.org/ghc/ :? for help
...
λ>
λ> :load q73268595.hs
[1 of 1] Compiling Main ( q73268595.hs, interpreted )
Ok, one module loaded.
λ>
λ> getManyContents = getManyEnums :: StdGen -> Int -> ([Content],StdGen)
λ>
λ> g0 <- newStdGen
λ>
λ> (cs,gf) = getManyContents g0 10
λ>
λ> cs
[One,One,Two,One,One,Three,Three,Three,Bomb,One]
λ>
λ> :q
Leaving GHCi.
$