• Roll.hs

  • ¶

    This language pragma allows GHC to treat string literals as Text

    imports

    {-# LANGUAGE OverloadedStrings #-}
    
    module Plugins.Dice.Roll (roll) where
  • ¶

    Import the pseudorandom number generator used to simulate dice rolls.

    import System.Random
  • ¶

    This contains Core.Types and Core.Cmds. It is mostly used for the type Command along with the privmsg, action, and write functions.

    import Core
  • ¶

    Data.Text supports Unicode, so it's preferred over the base String type. Also joebot2 uses Text extensively.

    import qualified Data.Text as T
  • ¶

    This is mostly used for (<>), which is a generic way of doing concatenation. This way the code will not be littered with T.appends. For example,

    T.append s t
    becomes

    s <> t
    when using Data.Monoid.

    import Data.Monoid
  • ¶

    Attoparsec is a parser combinator library. It is used here to help parse arguments to the roll command, which expects a certain format.

    import qualified Data.Attoparsec.Text as A
  • ¶

    This is a general library that helps compact Attoparsec code, and reduces the use of monads. For more information about this library and Data.Monoid, refer to Brent Yorgey's Typeclassopedia.

    import Control.Applicative
    
    import Control.Monad
  • ¶

    The liftIO function allows the running of IO actions in the Net monad

    The roll command

    import Control.Monad.Trans (liftIO)
  • ¶

    This command is invoked when somebody invokes "!roll" on the channel. The arguments to Command are the command name, the arity, the function to run, and the help message.

    roll = Command "!roll" 1 rollDie "!roll <num_dice>d<die_type>"
  • ¶

    This function is invoked when the roll command is run.

    rollDie n chn args = do
  • ¶

    Take in the first argument and parse it. Arity is checked before this command is executed, so args won't be empty.

        msg <- liftIO $ parseEvalDice (head args)
  • ¶

    Print out the result of parseEvalDice to the IRC server

    parser

        privmsg n chn msg
  • ¶

    Take in a string and try to parse out the number dice and the number of sides per die.

    parseEvalDice s =
  • ¶

    Try to parse either "dX" or "XdX"

        case A.parseOnly (A.try parseDie <|> parseDice) s of
  • ¶

    If the parse fails, return "invalid input"

          Left err -> return "invalid input"
  • ¶

    If it succeeds first check the arguments to see if they're logical. If they are, then roll the dice.

          Right (n, m) -> 
            if n < 0 || m < 1 
            then return "theoretically impossible rolls have left me in despair!"
            else rollDice n m
  • ¶

    A parser for "dX" where X is a number. This results in a 1dX roll.

    parseDie  = (,) <$> (pure 1) <*> (A.char 'd' *> A.decimal)
  • ¶

    A parser for "XdX".

    Dice Rolling

    parseDice = (,) <$> A.decimal <*> (A.char 'd' *> A.decimal)
  • ¶

    The code for rolling the die, the arguments are number of dice and number of sides, respectively.

    rollDice n m = do
  • ¶

    Do n rolls of m sides. replicateM performs a monadic action n number of times.

        rolls <- replicateM n (rollDieN m)
  • ¶

    res holds all the roll results as Text.

        let res = map (T.pack . show) rolls
  • ¶

    total holds the total of all the rolls as Text.

        let total = (T.pack . show . sum) rolls
  • ¶

    Return both res and total with some light formatting.

        return $ T.unwords res <> " | sum: " <> total
  • ¶

    The actual die rolling function. Gets a random Int from 1 to n.

    rollDieN :: Int -> IO Int
    rollDieN n = getStdRandom $ randomR (1,n)