I'm learning Haskell. Average of large number of Dice Rolls in Haskell How shall I make the below code work with multiple dices say 8 dices) instead of repeatedly creating let rolls..3,4,5...etc...
module Dice where
import System.Random
import Data.List
dice = do
g <- getStdGen
b <- newStdGen
let trials = 1000
let rolls1 = take trials (randomRs (1,6) g :: [Int])
let rolls2 = take trials (randomRs (1,6) b::[Int])
let rolls = zipWith (+) rolls1 rolls2
let average = div (foldl' (+) 0 rolls) trials
print average
The System.Random
module offers some basic primitive, but (in my opinion) it lacks a proper monadic interface. More precisely, it offers an IO-based monadic interface, but lacks a State-based monadic interface. The latter is however easy to define , though.
Anyway, if one wants to stick with IO and the standard generator, one could write:
-- (untested code)
rollDie :: Int -> IO Int
rollDie n = randomIO (1,n) -- implicitly uses the global generator
rollManyDice :: Int -> Int -> IO [Int]
rollManyDice howMany n = replicateM howMany (rollDie n)
main :: IO ()
main = do
dice <- rollManyDice 20 6
putStrLn $ "Here's 20 6-sides dice : " ++ show dice
where replicateM
executes howMany
times a monadic action, collecting all the results in a list. Here the monad is IO
, but it could be anything.
This is a fine and simple approach, but the IO
type above is a bit too much, preventing us to call rollDie
from non-IO code. A State
-based solution would not have this limination
type R a = State StdGen a
rollDie :: Int -> R Int
rollDie n = state $ randomR (1,n) -- uses the generator inside the State monad
rollManyDice :: Int -> Int -> R [Int]
rollManyDice howMany n = replicateM howMany (rollDie n)
main :: IO ()
main = do
g <- getStdGen
let dice = evalState (rollManyDice 20 6) g
putStrLn $ "Here's 20 6-sides dice : " ++ show dice