Skip to content

Commit

Permalink
Documentation, Refactoring, Deprecation
Browse files Browse the repository at this point in the history
Added documentation generated from LDoc
Added example files to show code usage

Changed refactored code to make it cleaner

Removed dice.chance()
  • Loading branch information
timothymtorres committed Oct 28, 2016
1 parent 06d7733 commit 6a568c6
Show file tree
Hide file tree
Showing 34 changed files with 8,374 additions and 147 deletions.
310 changes: 163 additions & 147 deletions dice.lua
Original file line number Diff line number Diff line change
@@ -1,45 +1,175 @@
dice = {}
dice.__index = dice
dice.minimum = 1 -- class default lowest possible roll is 1 (can set to nil to allow negative rolls)

--- A library used to roll and manipulate roguelike based dice
-- @classmod dice
-- @author Timothy Torres
-- @copyright 2013
-- @license MIT/X11

local dice = {}
dice.minimum = 1 -- class default lowest possible roll is 1 (can set to nil to allow negative rolls)
local random, max, abs, sort, match, format = math.random, math.max, math.abs, table.sort, string.match, string.format

--[[-- DICE INDEX -----
{x}d{y}+{s}{z}^+{s}{r}x{v}
x - number of dice being rolled
y - faces of the dice
z - a value to be added to the result (can be negative)
r - rerolls, if + then remove lowest rolls, if - then remove highest rolls
s - if double sign (++ or --) adds {z} value to all dice rolls (default is last roll) or {r} rerolls to all dice rolls
v - sets of dice used
Examples:
--- Creates a new dice instance
-- @tparam ?int|string dice_notation Can be either a dice string, or int
-- @tparam[opt] int minimum Sets dice instance roll's minimum result boundaries
-- @treturn dice
function dice:new(dice_notation, minimum)
-- If dice_notation is a number, we must convert it into the proper dice string format
if type(dice_notation) == 'number' then dice_notation = '1d'..dice_notation end

local dice_pattern = '[(]?%d+[d]%d+[+-]?[+-]?%d*[%^]?[+-]?[+-]?%d*[)]?[x]?%d*'
assert(dice_notation == match(dice_notation, dice_pattern), "Dice string incorrectly formatted.")

local dice_INST = {}

dice_INST.num = tonumber(match(dice_notation, '%d+'))
dice_INST.faces = tonumber(match(dice_notation, '[d](%d+)'))

local double_bonus, bonus = match(dice_notation, '[^%^+-]([+-][+-]?)(%d+)')
dice_INST.is_bonus_plural = double_bonus == '++' or double_bonus == '--'
dice_INST.bonus = tonumber(bonus) or 0

local double_reroll, reroll = match(dice_notation, '[%^]([+-][+-]?)(%d+)')
dice_INST.is_reroll_plural = double_reroll == '++' or double_reroll == '--'
dice_INST.rerolls = tonumber(reroll) or 0

dice_INST.sets = tonumber(match(dice_notation, '[x](%d+)')) or 1

dice_INST.minimum = minimum

self.__index = self
return setmetatable(dice_INST, self)
end

--- Sets class global for dice minimum
--@tparam[opt] int value Sets dice class minimum result boundaries (if nil, no minimum result)
function dice:setMin(value) self.minimum = value end

--- Number of total dice
-- @treturn int
function dice:getNum() return self.num end

--- Number of total faces on a dice
-- @treturn int
function dice:getFaces() return self.faces end

--- Bonus to be added to the dice total
-- @treturn int
function dice:getBonus() return self.bonus end

--- Rerolls to be added to the dice
-- @treturn int
function dice:getRerolls() return self.rerolls end

--- Number of total dice sets
-- @treturn int
function dice:getSets() return self.sets end

--- Bonus to be added to all dice (if double bonus enabled) otherwise regular bonus
-- @treturn int
function dice:getTotalBonus() return (self.is_bonus_plural and self.bonus*self.num) or self.bonus end

--- Rerolls to be added to all dice (if double reroll enabled) otherwise regular reroll
-- @treturn int
function dice:getTotalRerolls() return (self.is_reroll_plural and self.rerolls*self.num) or self.rerolls end

--- Determines if all dice are to be rerolled together or individually
-- @treturn bool
function dice:isDoubleReroll() return self.is_reroll_plural end

--- Determines if all dice are to apply a bonus together or individually
-- @treturn bool
function dice:isDoubleBonus() return self.is_bonus_plural end

--- Modifies bonus
-- @tparam int value
-- @treturn dice dice
function dice:__add(value) self.bonus = self.bonus + value return self end

--- Modifies bonus
-- @tparam int value
-- @treturn dice
function dice:__sub(value) self.bonus = self.bonus - value return self end

--- Modifies number of dice
-- @tparam int value
-- @treturn dice
function dice:__mul(value) self.num = self.num + value return self end

--- Modifies amount of dice faces
-- @tparam int value
-- @treturn dice
function dice:__div(value) self.faces = self.faces + value return self end

--- Modifies rerolls
-- @tparam int value
-- @treturn dice
function dice:__pow(value) self.rerolls = self.rerolls + value return self end

--- Modifies dice sets
-- @tparam int value
-- @treturn dice
function dice:__mod(value) self.sets = self.sets + value return self end

--- Gets a formatted dice string in roguelike notation
-- @treturn string
function dice:__tostring()
local num_dice, dice_faces, bonus, is_bonus_plural, rerolls, is_reroll_plural, sets = self.num, self.faces, self.bonus, self.is_bonus_plural, self.rerolls, self.is_reroll_plural, self.sets

-- num_dice & dice_faces default to 1 if negative or 0!
sets, num_dice, dice_faces = max(sets, 1), max(num_dice, 1), max(dice_faces, 1)

local double_bonus = is_bonus_plural and (bonus >= 0 and '+' or '-') or ''
bonus = (bonus ~= 0 and double_bonus..format('%+d', bonus)) or ''

1d6 = Roll 1 six sided die
3d4 = Roll 3 dice with four sides
2d4+1 = Roll 2 dice with four sides, add +1 to last roll
3d3++1 = Roll 3 dice with three sides, add +1 to all rolls
3d3--1 = Roll 3 dice with three sides, add -1 to all rolls
2d6^+2 = Roll 4 dice with six sides, remove the two lowest rolls
2d4^++1 = Roll 4 dice with four sides, remove the two lowest rolls
3d4-2^-1 = Roll 3 dice with four sides, remove the highest roll, add -1 to last roll
------ FINISH --]]--

local function determine(num_dice, dice_faces, bonus, double_sign_bonus, rerolls, double_sign_rerolls, sets, minimum)
local double_reroll = is_reroll_plural and (rerolls >= 0 and '+' or '-') or ''
rerolls = (rerolls ~= 0 and '^'..double_reroll..format('%+d', rerolls)) or ''

if sets > 1 then return '('..num_dice..'d'..dice_faces..bonus..rerolls..')x'..sets
else return num_dice..'d'..dice_faces..bonus..rerolls
end
end

--- Modifies whether reroll or bonus applies to individual dice or all of them
-- @tparam str pluralism_notation String must be one of the following operators `- + ^` The operator may be double signed to indicate pluralism.
-- @treturn dice
function dice:__concat(pluralism_notation)
local str_b = match(pluralism_notation, '[+-][+-]?') or ''
local bonus = ((str_b == '++' or str_b == '--') and 'double') or ((str_b == '+' or str_b == '-') and 'single') or nil

local str_r = match(pluralism_notation, '[%^][%^]?') or ''
local reroll = (str_r == '^^' and 'double') or (str_r == '^' and 'single') or nil

if bonus == 'double' then self.is_bonus_plural = true
elseif bonus == 'single' then self.is_bonus_plural = false end

if reroll == 'double' then self.is_reroll_plural = true
elseif reroll == 'single' then self.is_reroll_plural = false end
return self
end

--- Rolls the dice
-- @tparam ?int|dice|str self
-- @tparam[opt] int minimum
function dice.roll(self, minimum)
if type(self) ~= 'table' then self = dice:new(self, minimum) end
local num_dice, dice_faces = self.num, self.faces
local bonus, rerolls = self.bonus, self.rerolls
local is_bonus_plural, is_reroll_plural = self.is_bonus_plural, self.is_reroll_plural
local sets, minimum = self.sets, self.minimum

sets = max(sets, 1) -- Minimum of 1 needed
local set_rolls = {}

local bonus_all = double_sign_bonus and bonus or 0
rerolls = rerolls or 0
rerolls = double_sign_rerolls and rerolls*num_dice or rerolls
local bonus_all = is_bonus_plural and bonus or 0
rerolls = is_reroll_plural and rerolls*num_dice or rerolls

-- num_dice & dice_faces CANNOT be negative!
num_dice, dice_faces = max(num_dice, 1), max(dice_faces, 1)

for i=1, sets do
local rolls = {}
for ii=1, num_dice + abs(rerolls) do
rolls[ii] = random(1, dice_faces) + bonus_all
rolls[ii] = random(1, dice_faces) + bonus_all -- if is_bonus_plural then bonus_all gets added to every roll, otherwise bonus_all = 0
end

if rerolls ~= 0 then
Expand All @@ -48,8 +178,8 @@ local function determine(num_dice, dice_faces, bonus, double_sign_bonus, rerolls
for index=num_dice + 1, #rolls do rolls[index] = nil end
end

-- adds bonus to last roll by default
if not double_sign_bonus and bonus then rolls[#rolls] = rolls[#rolls] + bonus end
-- bonus gets added to the last roll if it is not plural
if not is_bonus_plural then rolls[#rolls] = rolls[#rolls] + bonus end

local total = 0
for _, number in ipairs(rolls) do total = total + number end
Expand All @@ -65,121 +195,7 @@ local function determine(num_dice, dice_faces, bonus, double_sign_bonus, rerolls
end
end

return unpack(set_rolls)
end

--[[
dice = {
num = (+)number,
faces = (+)number,
sets = (+)number, -- optional
bonus = (+ or -)number, -- optional
double_b = binary, -- optional (requires bonus)
rerolls = (+ or -)number -- optional
double_r = binary, -- optional (requires rerolls)
minimum = number/false/nil -- optional (set false for no minimum, set to nil to use dice class default)
}
--]]

function dice:new(roll, minimum)
roll = (type(roll) == 'table' and roll) or (type(roll) == 'string' and dice.getDice(roll)) or (type(roll) == 'number' and dice.getDice('1d'..roll))
roll.minimum = minimum
self.__index = self
return setmetatable(roll, self)
end

function dice:getNum() return self.num end
function dice:getFaces() return self.faces end
function dice:getBonus() return self.bonus end
function dice:getRerolls() return self.rerolls end
function dice:getSets() return self.sets end
function dice:getTotalBonus() return (self.double_b and self.bonus*self.num) or self.bonus end
function dice:getTotalRerolls() return (self.double_r and self.rerolls*self.num) or self.rerolls end
function dice:isDoubleReroll() return self.double_r end
function dice:isDoubleBonus() return self.double_b end

function dice.__add(roll, value) roll.bonus = roll:getBonus() + value return dice:new(roll) end

function dice.__sub(roll, value) roll.bonus = roll:getBonus() - value return dice:new(roll) end

function dice.__mul(roll, value) roll.num = roll:getNum() + value return dice:new(roll) end

function dice.__div(roll, value) roll.faces = roll:getFaces() + value return dice:new(roll) end

function dice.__pow(roll, value) roll.rerolls = roll:getRerolls() + value return dice:new(roll) end

function dice.__mod(roll, value) roll.sets = roll:getSets() + value return dice:new(roll) end

function dice.__tostring(self) return self:getString() end

function dice.__concat(roll, str)
local str_b = match(str, '[+-][+-]?') or ''
local bonus = ((str_b == '++' or str_b == '--') and 'double') or ((str_b == '+' or str_b == '-') and 'single') or nil

local str_r = match(str, '[%^][%^]?') or ''
local reroll = (str_r == '^^' and 'double') or (str_r == '^' and 'single') or nil

if bonus == 'double' then roll.double_b = true
elseif bonus == 'single' then roll.double_b = false end

if reroll == 'double' then roll.double_r = true
elseif reroll == 'single' then roll.double_r = false end
return dice:new(roll)
end

function dice:roll()
if type(self) == 'string' then
local roll = dice.getDice(self)
return determine(roll.num, roll.faces, roll.bonus, roll.double_b, roll.rerolls, roll.double_r, roll.sets, roll.minimum)
elseif type(self) == 'number' then
return max(random(1, self), dice.minimum)
elseif type(self) == 'table' then
return determine(self.num, self.faces, self.bonus, self.double_b, self.rerolls, self.double_r, self.sets, self.minimum)
end
end

-- percent must be a decimal (ie. .75 = 75%)
function dice.chance(percent) return percent >= random() end

function dice.getDice(str)
local dice_pattern = '[(]?%d+[d]%d+[+-]?[+-]?%d*[%^]?[+-]?[+-]?%d*[)]?[x]?%d*'
if str ~= match(str, dice_pattern) then return error("Dice string incorrectly formatted.") end
local dice = {}

dice.num = tonumber(match(str, '%d+'))
dice.faces = tonumber(match(str, '[d](%d+)'))

local double_bonus, bonus = match(str, '[^%^+-]([+-][+-]?)(%d+)')
dice.double_b = double_bonus == '++' or double_bonus == '--'
dice.bonus = tonumber(bonus) or 0

local double_reroll, reroll = match(str, '[%^]([+-][+-]?)(%d+)')
dice.double_r = double_reroll == '++' or double_reroll == '--'
dice.rerolls = tonumber(reroll) or 0

dice.sets = tonumber(match(str, '[x](%d+)')) or 1
return dice
end

function dice:getString()
local num_dice, dice_faces, bonus, double_sign_bonus, rerolls, double_sign_reroll, sets = self.num, self.faces, self.bonus, self.double_b, self.rerolls, self.double_r, self.sets

-- num_dice & dice_faces default to 1 if negative or 0!
num_dice, dice_faces = max(num_dice, 1), max(dice_faces, 1)

local double_b = double_sign_bonus and (bonus >= 0 and '+' or '-') or ''
bonus = (bonus ~= 0 and double_b..format('%+d', bonus)) or ''

local double_r = double_sign_reroll and (rerolls >= 0 and '+' or '-') or ''
rerolls = (rerolls ~= 0 and '^'..double_r..format('%+d', rerolls)) or ''

if sets > 1 then
return '('..num_dice..'d'..dice_faces..bonus..rerolls..')x'..sets
else
return num_dice..'d'..dice_faces..bonus..rerolls
end
return unpack(set_rolls)
end

function dice.setMin(value) dice.minimum = value end

return dice
return dice
Loading

0 comments on commit 6a568c6

Please sign in to comment.