Jump to content

Module:Build bracket/Logic

Permanently protected module
From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Pbrks (talk | contribs) at 02:25, 13 August 2025 (Created page with 'local Logic = {} -- ================ -- updateMaxLegs -- ================ function Logic.updateMaxLegs(state, config, Helpers) state.maxlegs = state.maxlegs or {} for j = config.minc, config.c do state.maxlegs[j] = state.rlegs[j] for i = 1, config.r do local e = state.entries[j] and state.entries[j][i] if e then if Helpers.notempty(e.legs) then state.maxlegs[j] = math.max(s...'). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
(diff) ← Previous revision | Latest revision (diff) | Newer revision → (diff)
local Logic = {}

-- ================
-- updateMaxLegs
-- ================
function Logic.updateMaxLegs(state, config, Helpers)
    state.maxlegs = state.maxlegs or {}
    for j = config.minc, config.c do
        state.maxlegs[j] = state.rlegs[j]
        for i = 1, config.r do
            local e = state.entries[j] and state.entries[j][i]
            if e then
                if Helpers.notempty(e.legs) then
                    state.maxlegs[j] = math.max(state.rlegs[j], e.legs)
                end
                if config.autolegs and e.score then
                    local l = 1
                    repeat l = l + 1
                    until Helpers.isempty(e.score) or Helpers.isempty(e.score[l])
                    state.maxlegs[j] = math.max(state.maxlegs[j], l - 1)
                end
            end
        end
    end
end

-- ================
-- matchGroups
-- ================
function Logic.matchGroups(state, config)
    state.matchgroup = state.matchgroup or {}
    for j = config.minc, config.c do
        state.matchgroup[j] = {}
        local mgj = state.matchgroup[j]
        local tpm = tonumber(state.teamsPerMatch and state.teamsPerMatch[j]) or 2
        if tpm < 1 then tpm = 2 end

        local col = state.entries[j]
        if col then
            for i = 1, config.r do
                local e = col[i]
                if e and e.ctype == 'team' then
                    local idx = tonumber(e.index) or tonumber(e.altindex) or i
                    local g = math.ceil(idx / tpm)
                    mgj[i] = g
                    e.group = g
                end
            end
        end
    end
end

-- =================
-- computeAggregate
-- =================
function Logic.computeAggregate(state, config, Helpers, StateChecks)
    if config.aggregate_mode == 'off' or config.aggregate_mode == 'manual' then return end

    local modeLow = (config.boldwinner_mode == 'low')
    local function numlead(s)
        if not s or s == '' then return nil end
        return tonumber((s):match('^%d+'))
    end

    local function buildGroupsForRound(j)
        local groups = {}
        local mg = state.matchgroup[j] or {}
        for i = 1, config.r do
            local e = state.entries[j] and state.entries[j][i]
            if e and e.ctype == 'team' then
                local gid = mg[i]
                if gid ~= nil then
                    groups[gid] = groups[gid] or {}
                    table.insert(groups[gid], i)
                end
            end
        end
        return groups
    end

    local function preparseLegs(j)
        local legNums = {}
        for i = 1, config.r do
            local e = state.entries[j] and state.entries[j][i]
            if e and e.ctype == 'team' then
                local L = StateChecks.teamLegs(state, config, j, i)
                if config.aggregate and L > 1 and e.score and e.score.agg ~= nil then
                    legNums[i] = {}
                    for l = 1, L do
                        legNums[i][l] = numlead(e.score[l])
                    end
                end
            end
        end
        return legNums
    end

    for j = config.minc, config.c do
        local groups  = buildGroupsForRound(j)
        local legNums = preparseLegs(j)

        if config.aggregate_mode == 'score' then
            for _, members in pairs(groups) do
                for _, i in ipairs(members) do
                    local e = state.entries[j][i]
                    if e and e.ctype == 'team' and config.aggregate and StateChecks.teamLegs(state, config, j, i) > 1 then
                        if Helpers.isempty(e.score.agg) then
                            local sum, nums = 0, legNums[i]
                            if nums then
                                for _, v in ipairs(nums) do if v then sum = sum + v end end
                                e.score.agg = tostring(sum)
                            end
                        end
                    end
                end
            end
        else
            -- 'sets'/'legs': count leg wins (ties give no win)
            for _, members in pairs(groups) do
                local wins = {}
                local commonLegs = math.huge
                for _, i in ipairs(members) do
                    local nums = legNums[i]
                    local L = (nums and #nums) or 0
                    if L == 0 then commonLegs = 0; break end
                    if L < commonLegs then commonLegs = L end
                end

                for l = 1, commonLegs do
                    local allNumeric = true
                    for _, i in ipairs(members) do
                        if not (legNums[i] and legNums[i][l] ~= nil) then
                            allNumeric = false; break
                        end
                    end
                    if allNumeric then
                        local best, bestIndex, tie = nil, nil, false
                        for _, i in ipairs(members) do
                            local v = legNums[i][l]
                            if best == nil then
                                best, bestIndex, tie = v, i, false
                            else
                                if (modeLow and v < best) or (not modeLow and v > best) then
                                    best, bestIndex, tie = v, i, false
                                elseif v == best then
                                    tie = true
                                end
                            end
                        end
                        if not tie and bestIndex then
                            wins[bestIndex] = (wins[bestIndex] or 0) + 1
                        end
                    end
                end

                for _, i in ipairs(members) do
                    local e = state.entries[j][i]
                    if e and e.ctype == 'team' and config.aggregate and StateChecks.teamLegs(state, config, j, i) > 1 then
                        if Helpers.isempty(e.score.agg) then
                            e.score.agg = tostring(wins[i] or 0)
                        end
                    end
                end
            end
        end
    end
end

-- ==========
-- boldWinner
-- ==========
function Logic.boldWinner(state, config, Helpers, StateChecks)
    local modeLow  = (config.boldwinner_mode == 'low')
    local aggOnly  = config.boldwinner_aggonly

    local function isWin(mine, theirs)
        if modeLow then return mine < theirs else return mine > theirs end
    end
    local function isAggWin(mine, theirs, l)
        if l == 'agg' and config.aggregate_mode == 'sets' then
            return mine > theirs
        else
            return isWin(mine, theirs)
        end
    end

    local function boldScore(j, i, l)
        local e = state.entries[j][i]
        if not e or e.ctype ~= 'team' then return 'normal' end
        local raw = e.score[l] or ''
        local mine = tonumber((raw):match('^%d+'))
        if not mine then return 'normal' end

        local comps = {}
        for oppIndex, groupId in pairs(state.matchgroup[j]) do
            if groupId == state.matchgroup[j][i] and oppIndex ~= i then
                local theirraw = state.entries[j][oppIndex].score[l] or ''
                local theirs = tonumber((theirraw):match('^%d+'))
                if not theirs then return 'normal' end
                table.insert(comps, theirs)
            end
        end
        for _, v in ipairs(comps) do
            if not isAggWin(mine, v, l) then return 'normal' end
        end

        if l ~= 'agg' then
            e.wins = (e.wins or 0) + 1
        else
            e.aggwins = 1
        end
        return 'bold'
    end

    local function boldTeam(j, i, useAggregate)
        local e = state.entries[j][i]
        local winsKey = useAggregate and 'aggwins' or 'wins'
        local legs = StateChecks.teamLegs(state, config, j, i)

        if not useAggregate then
            if (e[winsKey] or 0) > legs / 2 then return 'bold' end
            local checkFn = config.autolegs and Helpers.notempty or function(val) return not Helpers.isempty(val) end
            for l = 1, legs do
                local sv = e.score[l]
                if not checkFn(sv) or (type(sv) == 'string' and sv:find('nbsp', 1, true)) then
                    return 'normal'
                end
            end
        end

        for oppIndex, groupId in pairs(state.matchgroup[j]) do
            if groupId == state.matchgroup[j][i] and oppIndex ~= i then
                if (e[winsKey] or 0) <= tonumber(state.entries[j][oppIndex][winsKey] or 0) then
                    return 'normal'
                end
            end
        end
        return 'bold'
    end

    for j = config.minc, config.c do
        for i = 1, config.r do
            local e = state.entries[j] and state.entries[j][i]
            if e and e.ctype == 'team' then
                e.wins, e.aggwins = 0, 0
            end
        end
        for i = 1, config.r do
            local e = state.entries[j] and state.entries[j][i]
            if e and e.ctype == 'team' then
                local legs = StateChecks.teamLegs(state, config, j, i)
                if not aggOnly then
                    for l = 1, legs do
                        e.score.weight[l] = boldScore(j, i, l)
                    end
                end
                if config.aggregate and legs > 1 then
                    e.score.weight.agg = boldScore(j, i, 'agg')
                end
            end
        end
        if not aggOnly then
            for i = 1, config.r do
                local e = state.entries[j] and state.entries[j][i]
                if e and e.ctype == 'team' then
                    local useAggregate = config.aggregate and StateChecks.teamLegs(state, config, j, i) > 1
                    e.weight = boldTeam(j, i, useAggregate)
                end
            end
        end
    end
end

return Logic