{-# LANGUAGE OverloadedStrings #-}
module Plugins.Dice.Roll (roll) where{-# LANGUAGE OverloadedStrings #-}
module Plugins.Dice.Roll (roll) whereImport the pseudorandom number generator used to simulate dice rolls.
import System.RandomThis contains
Core.Types
and
Core.Cmds.
It is mostly used for the type Command
along with the
privmsg,
action, and
write functions.
import CoreData.Text
supports Unicode,
so it's preferred
over the base String type.
Also joebot2 uses
Text extensively.
import qualified Data.Text as TThis 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.MonoidAttoparsec 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 AThis 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.Monadimport 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 = doTake 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) privmsg n chn msgTake 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 ofIf 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 mA parser for "dX" where X is a number. This results in a 1dX roll.
parseDie = (,) <$> (pure 1) <*> (A.char 'd' *> A.decimal)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 = doDo 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) rollstotal holds the total of all the rolls as Text.
let total = (T.pack . show . sum) rollsReturn both res and total with some
light formatting.
return $ T.unwords res <> " | sum: " <> totalThe actual die rolling function. Gets a random Int
from 1 to n.
rollDieN :: Int -> IO Int
rollDieN n = getStdRandom $ randomR (1,n)