Editing Module:Sandbox/Vergir

Warning: You are not logged in. Once you make an edit, a temporary account will be created for you. Learn more. Log in or create an account to continue receiving notifications after this account expires, and to access other features.
The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
Latest revision Your text
Line 1: Line 1:
-- Test harness for the proposed Module:GameData prop-matching change.
-- Renders with {{#invoke:Sandbox/Vergir|test}}
--
-- Holds a copy of both the current and the proposed matching code, runs the
-- proposed one against Data:AbilityData.json, and checks that every existing
-- list in Module:AbilityTable/Lists still selects exactly the same abilities.
local p = {}
local p = {}
local Lists = require("Module:AbilityTable/Lists")
-- ============================================================
-- Current implementation (unchanged, still used for plain targets)
-- ============================================================
local function record_matches_prop(record, prop)
    for k, v in pairs(record) do
        if k == prop then
            if type(v) == "table" then
                if v["Value"] ~= nil then
                    v = v["Value"]
                else
                    for _ in pairs(v) do return true end
                    return false
                end
            end
            if v ~= nil and v ~= 0 and v ~= "" and v ~= "0" and v ~= "0m" then
                return true
            end
        end
        if type(v) == "table" and k ~= "DisabledStateMask" then
            if record_matches_prop(v, prop) then return true end
        elseif type(v) == "string" then
            if v == prop then return true end
        end
    end
    return false
end
local function entity_matches_old(record, properties)
    for _, prop in ipairs(properties) do
        if record_matches_prop(record, prop) then return true end
    end
    return false
end
-- ============================================================
-- Proposed additions
-- ============================================================
local function value_is_meaningful(v)
    if type(v) == "table" then
        if v["Value"] ~= nil then
            v = v["Value"]
        else
            for _ in pairs(v) do return true end
            return false
        end
    end
    return v ~= nil and v ~= 0 and v ~= "" and v ~= "0" and v ~= "0m"
end
local function value_equals(v, expected)
    if type(v) == "table" then v = v["Value"] end
    if v == nil or type(v) == "table" then return false end
    return mw.ustring.lower(tostring(v)) == expected
end
local function follow_path(node, segments, index, value)
    if index > #segments then
        if value ~= nil then return value_equals(node, value) end
        return value_is_meaningful(node)
    end
    if type(node) ~= "table" then return false end
    local segment = segments[index]
    local next_node = node[segment]
    if next_node == nil then
        local num = tonumber(segment)
        if num then next_node = node[num] end
    end
    if next_node ~= nil and follow_path(next_node, segments, index + 1, value) then
        return true
    end
    for _, element in ipairs(node) do
        if type(element) == "table" and follow_path(element, segments, index, value) then
            return true
        end
    end
    return false
end
local function record_matches_path(record, segments, value)
    for k, v in pairs(record) do
        if k == segments[1] and follow_path(v, segments, 2, value) then
            return true
        end
        if type(v) == "table" and k ~= "DisabledStateMask" then
            if record_matches_path(v, segments, value) then return true end
        end
    end
    return false
end
local function parse_target(prop)
    if type(prop) ~= "string" then return nil end
    local keys, value = prop, nil
    local colon = prop:find(":", 1, true)
    if colon then
        keys  = mw.text.trim(prop:sub(1, colon - 1))
        value = mw.text.trim(prop:sub(colon + 1))
        if keys == "" or value == "" then return nil end
        value = mw.ustring.lower(value)
    end
    local segments = {}
    for segment in keys:gmatch("[^%.]+") do table.insert(segments, segment) end
    if #segments == 0 then return nil end
    if #segments == 1 and value == nil then return nil end
    return segments, value
end
local function entity_matches_new(record, properties)
    for _, prop in ipairs(properties) do
        local segments, value = parse_target(prop)
        if segments and record_matches_path(record, segments, value) then
            return true
        elseif record_matches_prop(record, prop) then
            return true
        end
    end
    return false
end
-- ============================================================
-- Cases
-- ============================================================
local cases = {
    { target = "Type:spirit",                        expected = 219, note = "key found at any depth" },
    { target = "Type:SPIRIT",                        expected = 219, note = "case-insensitive value" },
    { target = "Damage:spirit",                      expected = 0,  note = "nested value must not match" },
    { target = "Damage:90",                          expected = 5,  note = ".Value unwrapped" },
    { target = "AbilityCharges:2",                  expected = 18,  note = "number compared as string" },
    { target = "AbilityCooldown.Scale.Type",        expected = 300, note = "path, no value" },
    { target = "AbilityCooldown.Scale.Type:cooldown",expected = 299, note = "Parry scales on parry_cd" },
    { target = "AbilityCooldown.Scale.Type:spirit",  expected = 0,  note = "wrong value on a valid path" },
    { target = "Radius.Scale.Value:1",              expected = 67,  note = "explicit .Value segment" },
    { target = "Upgrades.Damage",                    expected = 63,  note = "steps through array entries" },
    { target = "Upgrades.1.AbilityCooldown",        expected = 79,  note = "numeric index segment" },
    { target = "38.1 50.8",                          expected = 1,  note = "dotted value falls back" },
}
local function select_names(data, properties, matcher)
    local names = {}
    for key, record in pairs(data) do
        if type(record) == "table" and matcher(record, properties) then
            table.insert(names, record["Name"] or key)
        end
    end
    table.sort(names)
    return names
end
local function ok_cell(pass)
    if pass then
        return 'style="background:#d5f5d5" | pass'
    end
    return 'style="background:#f5d5d5" | <b>FAIL</b>'
end
function p.test(frame)
    local data = mw.loadJsonData("Data:AbilityData.json")
    local out = {}
    local failures = 0
    -- --- new target forms ---
    table.insert(out, "=== Proposed target forms ===")
    table.insert(out, '{| class="wikitable"')
    table.insert(out, "! Target !! Expected !! Actual !! Result !! Sample !! Note")
    for _, case in ipairs(cases) do
        local names = select_names(data, { case.target }, entity_matches_new)
        local pass  = (#names == case.expected)
        if not pass then failures = failures + 1 end
        local sample = {}
        for i = 1, math.min(4, #names) do table.insert(sample, names[i]) end
        local sample_text = #sample > 0 and table.concat(sample, ", ") or "—"
        if #names > #sample then sample_text = sample_text .. ", …" end
        table.insert(out, "|-")
        table.insert(out, "| <code>" .. mw.text.nowiki(case.target) .. "</code>"
            .. " || " .. case.expected
            .. " || " .. #names
            .. " || " .. ok_cell(pass)
            .. " || " .. sample_text
            .. " || " .. case.note)
    end
    table.insert(out, "|}")
    -- --- regression across every existing list ---
    table.insert(out, "=== Existing lists: old vs new ===")
    table.insert(out, '{| class="wikitable sortable"')
    table.insert(out, "! List !! Old !! New !! Result")
    local list_names = {}
    for name in pairs(Lists.lists) do table.insert(list_names, name) end
    table.sort(list_names)
    for _, name in ipairs(list_names) do
        local props = Lists.lists[name]
        local old_names = select_names(data, props, entity_matches_old)
        local new_names = select_names(data, props, entity_matches_new)
        local same = (#old_names == #new_names)
        if same then
            for i = 1, #old_names do
                if old_names[i] ~= new_names[i] then same = false break end
            end
        end
        if not same then failures = failures + 1 end
        table.insert(out, "|-")
        table.insert(out, "| " .. name
            .. " || " .. #old_names
            .. " || " .. #new_names
            .. " || " .. ok_cell(same))
    end
    table.insert(out, "|}")
    local summary
    if failures == 0 then
        summary = "'''All checks passed.'''"
    else
        summary = "'''" .. failures .. " check(s) failed.'''"
    end
    return summary .. "\n" .. table.concat(out, "\n")
end
return p
return p
Please note that all contributions to The Deadlock Wiki are considered to be released under the Creative Commons Attribution-NonCommercial-ShareAlike (see Deadlock:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!
Cancel Editing help (opens in new window)
Preview page with this template

Page included on this page: