Types.hs 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. {-# LANGUAGE OverloadedStrings #-}
  2. module Types where
  3. import qualified Data.Map.Strict as Map
  4. import qualified Data.Text as Text
  5. import qualified Data.Text.IO as Text
  6. import qualified System.Random as Rand
  7. data Range = Range { rFrom :: Int, rTo :: Int }
  8. deriving (Eq, Show)
  9. type TableMap = Map.Map Text.Text Table
  10. data Table = Table
  11. { tableName :: Text.Text
  12. , tableChoices :: [(Range, Result)]
  13. } deriving (Eq, Show)
  14. data Fragment
  15. = FragText Text.Text
  16. | FragRoll Text.Text
  17. deriving (Eq, Show)
  18. data Result = Result { fromResult :: [Fragment] }
  19. deriving (Eq, Show)
  20. data Value = Value
  21. { valueMsg :: Text.Text
  22. } deriving (Eq, Show)
  23. stripValue :: Value -> Value
  24. stripValue = Value . Text.strip . valueMsg
  25. data Context = Context
  26. { ctxMap :: TableMap
  27. , ctxRoll :: Int
  28. , ctxSelf :: Text.Text
  29. }
  30. findTable :: Text.Text -> Context -> Maybe Table
  31. findTable name ctx = Map.lookup name (ctxMap ctx)
  32. computeFragments :: Context -> Fragment -> IO Value
  33. computeFragments _ (FragText msg) = pure (Value msg)
  34. computeFragments ctx (FragRoll name) =
  35. let absolute = case Text.stripPrefix "self" name of
  36. Just rest -> ctxSelf ctx <> rest
  37. Nothing -> name
  38. in case findTable absolute ctx of
  39. Just t -> rollTable (ctxMap ctx) t
  40. Nothing -> error ("no such table: " ++ show absolute)
  41. computeResult :: Context -> Result -> IO Value
  42. computeResult ctx (Result msgs) = do
  43. vs <- mapM (computeFragments ctx) msgs
  44. pure (Value (foldMap valueMsg vs))
  45. tableDie :: Table -> Int
  46. tableDie t = maximum [ x | (Range _ x, _) <- tableChoices t ]
  47. rollTable :: TableMap -> Table -> IO Value
  48. rollTable tables t = do
  49. roll <- Rand.randomRIO (1, tableDie t)
  50. let ctx = Context
  51. { ctxMap = tables
  52. , ctxRoll = roll
  53. , ctxSelf = tableName t
  54. }
  55. case [ result
  56. | (range, result) <- tableChoices t
  57. , roll >= rFrom range && roll <= rTo range
  58. ] of
  59. [choice] -> stripValue <$> computeResult ctx choice
  60. _ -> error $ unwords
  61. [ "bad table "
  62. , Text.unpack (tableName t)
  63. , "(roll of"
  64. , show roll
  65. , "has no matching result)"
  66. ]