1

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
Curious
  • 921
  • 1
  • 9
  • 25

3 Answers3

4

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
chi
  • 111,837
  • 3
  • 133
  • 218
0

Currently as it is formulated, you only need to sum up k*N individual die rolls and divide them by N, where k is amount of dice you want to roll "simultaneously", N amount of trials. Summing all dice in a single roll then summing all rolls is the same as summing all dice overall.

If you want it complicated, well, make single multiroll a list of random values of specified length, sum it (you can use plain sum even with no optimization when k is not too large), compute the average of results.

bipll
  • 11,747
  • 1
  • 18
  • 32
0
module Dice where
import System.Random
import Data.List
import Control.Applicative
import Control.Monad

rollDie :: Int -> IO Int
rollDie n = randomRIO (1,n)

rollManyDice :: Int -> Int -> IO [Int]
rollManyDice howMany n = replicateM howMany (rollDie n)

dice2 = do
    dice <- rollManyDice 8 6
    putStrLn $ "Here's 20 -6 sided dice: " ++show dice
Curious
  • 921
  • 1
  • 9
  • 25