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