-- | This module provides the DieGen data type and functions for using Dice to
--   generate random positive integers via balanced tables randomized by the
--   RealDice data or custom integer tables
module RealDice.Die (DieGen, roll1d, mkDieGen, mkDieGenCustom) where

import RealDice.Generate.BalancedTables (rdIntsPrime)
import RealDice.Manipulate.GetValueFromRNGTable (getIntByIndex)
import RealDice.Manipulate.RandomizeList (randomizeList)

-- | Stores a balanced table of random integers and an index pointing at the
--   next value to return
data DieGen where
  DieGen :: {DieGen -> Int
index :: Int, DieGen -> [Int]
intTable :: [Int]} -> DieGen

-- | Creates a new DieGen with the given index and the default Int table

-- | ==== __Examples__
--   >>> mkDieGen 143
--   {143, rdIntsPrime}
mkDieGen :: Int -> DieGen
mkDieGen :: Int -> DieGen
mkDieGen Int
i = Int -> [Int] -> DieGen
mkDieGenCustom Int
i [Int]
rdIntsPrime

-- | Creates a new DieGen with the given index and Int table

-- | Defaults to the RealDice balanced table of random integers if an empty
--   list is given

-- | ==== __Examples__
--   >>> mkDieGenCustom 143 [1, 0, 4, 3, 2]
--   {143, [1, 0, 4, 3, 2]}
--   >>> mkDieGenCustom 143 []
--   {143, rdIntsPrime}
mkDieGenCustom :: Int -> [Int] -> DieGen
mkDieGenCustom :: Int -> [Int] -> DieGen
mkDieGenCustom Int
i [] = DieGen {index :: Int
index = Int
i, intTable :: [Int]
intTable = [Int]
rdIntsPrime}
mkDieGenCustom Int
i [Int]
table = DieGen {index :: Int
index = Int
i, intTable :: [Int]
intTable = [Int]
table}

-- | Generates a random integer value between 1 and n via a simple table lookup

-- | ==== __Examples__
--   >>> roll1d 20 (mkDieGen 143)
--   (12, {144, rdIntsPrime})
roll1d ::
  -- | The number of sides on the die
  Int ->
  -- | The DieGen to use
  DieGen ->
  --  | The updated index
  ( Int,
    --  | The updated DieGen
    DieGen
  )
roll1d :: Int -> DieGen -> (Int, DieGen)
roll1d Int
n DieGen
die
  | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
1 = (Int
0, DieGen
die)
  | Bool
otherwise =
      ( Int -> [Int] -> Int
getIntByIndex
          (Int -> [Int] -> Int
getIntByIndex (DieGen -> Int
index DieGen
die) (DieGen -> [Int]
intTable DieGen
die))
          ([Int] -> [Int]
randomizeList [Int
1 .. Int
n]),
        DieGen {index :: Int
index = DieGen -> Int
index DieGen
die Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1, intTable :: [Int]
intTable = DieGen -> [Int]
intTable DieGen
die}
      )