Module:Sandbox/Vergir

From The Deadlock Wiki
Revision as of 13:32, 11 August 2026 by Vergir (talk | contribs) (Parity test for get_hero_scaling_data refactored onto get_scalings (with help from vergir-bot LLM))
Jump to navigation Jump to search

Documentation for this module may be created at Module:Sandbox/Vergir/doc

-- TEMPORARY test module.
-- {{#invoke:Sandbox/Vergir|test}}
local p = {}
local heroes_data = mw.loadJsonData("Data:HeroData.json")
local old_module = require('Module:HeroData')

local function raw_scaling(hero_data, scaling_type, stat)
	local scaling = hero_data and hero_data[scaling_type .. "Scaling"]
	if (scaling == nil or stat == nil) then return nil end
	return scaling[stat]
end

function p.get_scalings(hero_data, stat, include_zero)
	local scalings = {}
	for _, scaling_type in ipairs({"Level", "Spirit"}) do
		local value = raw_scaling(hero_data, scaling_type, stat)
		if (value ~= nil and (include_zero or value ~= 0)) then
			table.insert(scalings, { type = scaling_type, value = value })
		end
	end
	return scalings
end

function p.get_hero_scaling_data(hero_data, hero_stat_key)
	local scaling_data_returned = {}
	-- Walked backwards so Level is written last and wins a value collision,
	-- matching the original Spirit-then-Level assignment order.
	local scalings = p.get_scalings(hero_data, hero_stat_key, true)
	for i = #scalings, 1, -1 do
		scaling_data_returned[scalings[i].value] = scalings[i].type
	end
	return scaling_data_returned
end

local function repr(v)
	if type(v) ~= 'table' then return tostring(v) end
	local entries = {}
	for k, val in pairs(v) do
		table.insert(entries, tostring(k) .. '=' .. tostring(val))
	end
	table.sort(entries)
	return '{' .. table.concat(entries, ',') .. '}'
end

function p.test(frame)
	local hero_keys, stat_keys, seen = {}, {}, {}
	for hero_key in pairs(heroes_data) do table.insert(hero_keys, hero_key) end
	table.sort(hero_keys)
	for _, hero in pairs(heroes_data) do
		local sources = {
			hero,
			hero.Weapon or {},
			(hero.Weapon and hero.Weapon.AltFire) or {},
			hero.LevelScaling or {},
			hero.SpiritScaling or {},
		}
		for _, source in ipairs(sources) do
			for stat in pairs(source) do
				if type(stat) == 'string' then seen[stat] = true end
			end
		end
	end
	for stat in pairs(seen) do table.insert(stat_keys, stat) end
	table.sort(stat_keys)

	local checked, mismatches = 0, {}
	local zero_cases, collision_cases = 0, 0

	for _, hero_key in ipairs(hero_keys) do
		local hero_data = heroes_data[hero_key]
		for _, stat in ipairs(stat_keys) do
			local a = repr(old_module.get_hero_scaling_data(hero_data, stat))
			local b = repr(p.get_hero_scaling_data(hero_data, stat))
			checked = checked + 1
			if a ~= b then
				table.insert(mismatches, hero_key .. ' / ' .. stat .. ': ' .. a .. ' vs ' .. b)
			end
			-- Coverage: does the data actually exercise the tricky paths?
			local with_zero = p.get_scalings(hero_data, stat, true)
			local without_zero = p.get_scalings(hero_data, stat)
			if #with_zero ~= #without_zero then zero_cases = zero_cases + 1 end
			if #with_zero == 2 and with_zero[1].value == with_zero[2].value then
				collision_cases = collision_cases + 1
			end
		end
	end

	local out = {
		'Comparisons: ' .. checked,
		'Mismatches: ' .. #mismatches,
		'Cases with a zero scaling (shim must keep them): ' .. zero_cases,
		'Cases where Level and Spirit share a value (collision order): ' .. collision_cases,
		'',
	}
	for i, m in ipairs(mismatches) do
		if i > 30 then table.insert(out, '... and ' .. (#mismatches - 30) .. ' more') break end
		table.insert(out, '* <code>' .. m .. '</code>')
	end
	if #mismatches == 0 then table.insert(out, 'No differences.') end
	return table.concat(out, '\n')
end

return p