Module:YYSandbox

local public = {}

local private = {}

-- Removes leading and trailing spaces from strings

function private.trimWhitespace(str)

local whitespace = { [" "]=true, ["\n"]=true, ["\r"]=true }

local _start = 1

while (whitespace[str:sub(_start, _start)]) do

_start = _start + 1

end

local _end = str:len()

while (whitespace[str:sub(_end, _end)]) do

_end = _end - 1

end

return str:sub(_start, _end)

end

-- Checks if a table has a specific key

function private.hasKey(_table, key)

return _table[key] ~= nil

end

-- Gets a specific item from a table, with a fallback if the table doesn't have that key

function private.getValue(_table, key, fallback)

if private.hasKey(_table, key) then

return _table[key]

else

return fallback

end

end

-- Processes a csv row into a table and returns it

function private.processCsv(csv)

local retval = {}

for arg in csv:gmatch("([^,]+)") do

table.insert(retval, private.trimWhitespace(arg))

end

return retval

end

-- Get the arguments from a frame

function private.parseArgs(frame)

if private.getValue(frame.args, "external_args", false) then

return frame:getParent().args

end

return frame.args

end

-- Converts a difficulty number into a format that handles sorting better for + difficulties

function public.SortableDifficulty(frame)

local args = private.parseArgs(frame)

if private.hasKey(args, 1) == false then

return ''

end

local diff = args[1]

if type(diff) == 'number' then

return diff

end

diff = private.trimWhitespace(diff)

if diff:sub(#diff, #diff) == '+' then

return tonumber(diff:sub(1, #diff - 1) .. '.5')

end

return diff

end

-- Converts a version number into a format that handles sorting better

function public.SortableVersion(frame)

local args = private.parseArgs(frame)

if private.hasKey(args, 1) == false then

return ''

end

local retval = ''

for vpart in args[1]:gmatch('[^.]+') do

while #vpart < 4 do

vpart = '0' .. vpart

end

retval = retval .. vpart

end

return retval

end

function private.startsWith(String, Start)

return (string.sub(String, 1, string.len(Start)) == Start)

end

function private.toSet(list)

local out = {}

for i, k in ipairs(list) do

out[k] = true

end

return out

end

local normed_stats = {

0.00000000e+00,

5.83175390e-04,

4.66540312e-03,

1.57457355e-02,

3.73232250e-02,

7.28969237e-02,

1.25965884e-01,

2.00029159e-01,

2.98585800e-01,

4.25134859e-01,

5.74865141e-01,

7.01414200e-01,

7.99970841e-01,

8.74034116e-01,

9.27103076e-01,

9.62676775e-01,

9.84254264e-01,

9.95334597e-01,

9.99416825e-01,

1.00000000e+00

}

-- Calculates Lv 1-30 FRAG/STEP/OVER given partner data from partnerStats.json

function private.calculateStat(ps, stat)

local statIncStr = 'awakened' .. (stat:gsub("^%l", string.upper)) .. 'Increment'

local statAllLv = {}

local stat1 = ps[stat][1]

local stat20 = ps[stat][2]

local statInc = 0

if ps['hasAwakening'] then

statInc = ps[statIncStr]

end

for lv = 1, 20, 1 do

statAllLv[lv] = stat1 + normed_stats[lv] * (stat20 - stat1)

end

for lv = 21, 30, 1 do

statAllLv[lv] = stat20 + (lv - 20) * statInc

end

return statAllLv

end

-- Calculates Lv 1-30 PROG given partner and data from partnerStats.json

function private.calculateProg(ps, stat)

local overAllLv = private.calculateStat(ps, 'over')

local progAllLv = {}

if stat == 'progstep' then

local stepAllLv = private.calculateStat(ps, 'step')

for lv = 1, 30, 1 do

progAllLv[lv] = overAllLv[lv] + stepAllLv[lv]/2

end

elseif stat == 'progfrag' then

local fragAllLv = private.calculateStat(ps, 'frag')

for lv = 1, 30, 1 do

progAllLv[lv] = overAllLv[lv] * fragAllLv[lv]/50

end

elseif stat == 'progweaker' then

for lv = 1, 9, 1 do

progAllLv[lv] = overAllLv[lv] * (2 - 0.1*lv)

end

for lv = 10, 30, 1 do

progAllLv[lv] = overAllLv[lv]

end

elseif stat == 'progabsolute' then

local stepAllLv = private.calculateStat(ps, 'step')

local fragAllLv = private.calculateStat(ps, 'frag')

for lv = 1, 30, 1 do

local frag = fragAllLv[lv]

local step = stepAllLv[lv]

local over = overAllLv[lv]

progAllLv[lv] = math.max(0, over - math.abs( math.abs(over-frag) - math.abs(over-step) ))

end

end

return progAllLv

end

-- Generates rows (Template:ExactStatTableRow) for use by Template:ExactStatTable

-- Stats implemented: frag, step, over, progstep, progfrag, progweaker, progabsolute

function public.ExactStatTableRows(frame)

local args = private.parseArgs(frame)

-- local partnerStats = JsonUtils.jsonToObj("User:GKWS/partnerStats")

local partnerStats_ = mw.loadJsonData("Module:YYSandbox/partnerStats.json")

local partnerStats = {}

for partner, ps in pairs(partnerStats_) do

partnerStats[partner] = ps

end

local stat = private.trimWhitespace(private.getValue(args, 'stat', false))

local isProgStat = private.startsWith(stat, 'prog')

local playRating = tonumber(private.trimWhitespace(private.getValue(args, 'playrating', '10')))

local condense = private.trimWhitespace(private.getValue(args, 'condense', )) ~=

local condenseLvs = private.toSet({1, 5, 10, 15, 20, 25, 30})

-- partners with (non-random) stat-varying abilities

partnerStats['Tairitsu (Tempest) [max]'] = { link='Tairitsu (Tempest)',

frag={27.5,50}, step={130,160}, over={27.5,50}, hasAwakening=false }

partnerStats['Hikari (Fatalis) [max]'] = { link='Hikari (Fatalis)',

frag={27.5,50}, step={230,310}, over={220,300}, hasAwakening=false }

partnerStats['Vita [max bonus]'] = { link='Vita',

frag={34,51}, step={50.5,70.5}, over={77,110}, hasAwakening=false }

partnerStats['Mika Yurisaki [max bonus]'] = { link='Mika Yurisaki',

frag={53,103}, step={53,103}, over={53,103}, hasAwakening=false }

partnerStats['Ilith [awakened bonus]'] = { link='Ilith',

frag={50,50}, step={111.5,111.5}, over={50,50}, hasAwakening=true,

awakenedFragIncrement=0, awakenedStepIncrement=0.5, awakenedOverIncrement=0 }

-- partners that fail BYD challenge

bydFailPartners = private.toSet({

'Tairitsu (Tempest)',

'Tairitsu (Tempest) [max]',

'Hikari (Fatalis)',

'Hikari (Fatalis) [max]',

'DORO*C',

'Pandora Nemesis (MTA-XXX)',

'Toa Kozukata',

'Nami (Twilight)'

})

-- partners that use double stamina

doubleStaminaPartners = private.toSet({

'Hikari (Fatalis)', 'Hikari (Fatalis) [max]'

})

-- partners with min level 20

minLv20Partners = private.toSet({

'Hikari & Tairitsu (Reunion)', 'Ilith [awakened bonus]'

})

local rows = {}

for partner, ps in pairs(partnerStats) do

local ps = partnerStats[partner]

local minLv = 1

local maxLv = 20

if minLv20Partners[partner] then

minLv = 20

end

if ps['hasAwakening'] then

maxLv = 30

end

local staminaFactor = 1

if (doubleStaminaPartners[partner] and stat ~= 'frag') then

staminaFactor = 0.5

end

local trackLostFactor = 1

if (isProgStat or stat == 'over') and bydFailPartners[partner] then

trackLostFactor = (2.45*math.sqrt(playRating) + 2.5) / (2.45*math.sqrt(playRating) + 7.5)

end

local row_args = {

partner = partner,

condense = ''

}

if ps['link'] then

row_args['link'] = ps['link']

end

if condense then

row_args['condense'] = 'y'

end

local statAllLv = {}

if isProgStat then

statAllLv = private.calculateProg(ps, stat)

else

statAllLv = private.calculateStat(ps, stat)

end

for lv = minLv, maxLv, 1 do

if (not condense) or condenseLvs[lv] then

exactStat = statAllLv[lv] * staminaFactor * trackLostFactor

row_args['stat' .. lv] = string.format("%.3f", exactStat)

end

end

local row = frame:expandTemplate{title = 'YYExactStatTableRow', args=row_args}

rows[#rows+1] = row

end

return table.concat(rows, "\n")

end

return public