Модуль:Data/RatingCalc

Материал из ЧТМ
Перейти к навигации Перейти к поиску

Для документации этого модуля может быть создана страница Модуль:Data/RatingCalc/doc

-- =========================================================================
-- Модуль:StatEngine/RatingCalc
-- Калькулятор рейтинга Чемпионата Третьего Мира (ЧТМ)
-- Чистый движок: не обращается к БД напрямую, всё получает на вход.
-- Совершает ровно один проход по переданной базе данных.
-- =========================================================================

local Calc = {}

local teams_db = require('Модуль:Data/Teams')
local RatingData = require('Модуль:Data/RatingCalc')

local old_system = RatingData.old_system
local old_africa_exceptions = RatingData.old_africa_exceptions
local new_system = RatingData.new_system
local new_africa_exceptions = RatingData.new_africa_exceptions
local hist_system = RatingData.hist_system

-- =========================================================
-- УТИЛИТЫ
-- =========================================================

-- Проверка, относится ли турнир к региональным
local is_regional = { ["КАм"] = true, ["КАф"] = true, ["КЕв"] = true, ["КОк"] = true }

-- Инициализация заготовки для команды
local function init_team(code)
    return {
        code = code,
        is_ctm_finalist = false,
        hist = { total = 0 },
        old = {
            total = 0,
            ctm = { total = 0, years = {} },
            reg = { total = 0, years = {} },
            lna = { total = 0, years = {} },
            cups = { total = 0, years = {} }, -- ККо + КФе
            ccm = { total = 0, years = {} }
        },
        new = {
            total = 0,
            ctm = { total = 0, years = {} },
            reg = { total = 0, years = {} },
            lna = { total = 0, years = {} },
            cups = { total = 0, years = {} },
            ccm = { total = 0, years = {} }
        }
    }
end

-- =========================================================
-- ОСНОВНАЯ ФУНКЦИЯ РАСЧЁТА
-- =========================================================
function Calc.process(db, target_year)
    local teams = {}
    local all_years_set = {}

    -- Проход 1: Собираем только уникальные годы (с учётом лимита по году)
    for tourney, years_data in pairs(db) do
        for year_str, data in pairs(years_data) do
            local y = tonumber(year_str)
            if not target_year or y <= target_year then
                all_years_set[y] = true
            end
        end
    end

    -- Формируем отсортированный по убыванию массив годов (нужен для тайбрейкера)
    local years_desc = {}
    for y, _ in pairs(all_years_set) do table.insert(years_desc, y) end
    table.sort(years_desc, function(a, b) return a > b end)

    -- Проход 2: Главный расчёт
    for tourney, years_data in pairs(db) do
        for year_str, teams_data in pairs(years_data) do
            local y = tonumber(year_str)

            for team, result in pairs(teams_data) do
                if not teams[team] then teams[team] = init_team(team) end
                local t_data = teams[team]

                -- Спрашиваем Data/Teams, из какой конфедерации команда
                local team_info = teams_db.getTeam(team)
                local is_africa = team_info and team_info.conf == "Африка"

                -- Подготовка ключа результата
                local res_key = result
                if res_key == "о-3г-no" or res_key == "о1-3г-no" or res_key == "о3г-no" then
                    -- Если в словаре нет ключа с -no, то считаем как обычный 3г, но потом выдадим 0
                end

                -- ====== ИСТОРИЧЕСКИЙ РЕЙТИНГ ======
                if tourney == "ЧТМ" and hist_system[res_key] then
                    t_data.is_ctm_finalist = true
                    t_data.hist.total = t_data.hist.total + hist_system[res_key]
                end

                -- Функция начисления
                local function add_points(sys_dict, exceptions, sys_obj, category)
                    local t_dict = nil
                    if is_regional[tourney] then t_dict = sys_dict["Reg"]
                    else t_dict = sys_dict[tourney] end

                    if not t_dict or not t_dict[y] then return end

                    local pts = t_dict[y][res_key]

                    -- Если -no, а очков нет, значит 0
                    if not pts and string.match(res_key, "%-no$") then pts = 0 end
                    -- Обычный фоллбек
                    if not pts then pts = 0 end

                    -- Исключения для Африки в отборе ЧТМ
                    if tourney == "ЧТМ" and is_africa and exceptions and exceptions[y] and exceptions[y][res_key] then
                        pts = exceptions[y][res_key]
                    end

                    -- Исключения для Евразии в региональных кубках 2048 и 2052
                    if tourney == "КЕв" and (y == 2048 or y == 2052) and sys_dict == new_system then
                        if res_key == "в" then pts = (y == 2048) and 14.4 or 19.2 end
                    end

                    if pts > 0 then
                        sys_obj.total = sys_obj.total + pts
                        sys_obj[category].total = sys_obj[category].total + pts
                        sys_obj[category].years[y] = (sys_obj[category].years[y] or 0) + pts
                    end
                end

                -- Определение категории для турнира
                local cat = "ccm"
                if tourney == "ЧТМ" then cat = "ctm"
                elseif is_regional[tourney] then cat = "reg"
                elseif tourney == "ЛНа" then cat = "lna"
                elseif tourney == "ККо" or tourney == "КФе" then cat = "cups"
                end

                -- Применяем начисления
                add_points(old_system, old_africa_exceptions, t_data.old, cat)
                add_points(new_system, new_africa_exceptions, t_data.new, cat)
            end
        end
    end

    -- =========================================================
    -- ЛОГИКА ТАЙБРЕЙКЕРА
    -- =========================================================
    local function compare_recent(y_list, catA, catB)
        for _, y in ipairs(y_list) do
            local pA = catA.years[y] or 0
            local pB = catB.years[y] or 0
            if pA ~= pB then return pA > pB end
        end
        return nil
    end

    -- Генератор функции сортировки для Старой и Новой системы
    local function create_sorter(sys_key)
        return function(a, b)
            local dA = teams[a][sys_key]
            local dB = teams[b][sys_key]

            if dA.total ~= dB.total then return dA.total > dB.total end

            -- 1. ЧТМ
            if dA.ctm.total ~= dB.ctm.total then return dA.ctm.total > dB.ctm.total end
            local c_ctm = compare_recent(years_desc, dA.ctm, dB.ctm)
            if c_ctm ~= nil then return c_ctm end

            -- 3. Региональные
            if dA.reg.total ~= dB.reg.total then return dA.reg.total > dB.reg.total end
            local c_reg = compare_recent(years_desc, dA.reg, dB.reg)
            if c_reg ~= nil then return c_reg end

            -- 5. Лига Наций
            if dA.lna.total ~= dB.lna.total then return dA.lna.total > dB.lna.total end
            local c_lna = compare_recent(years_desc, dA.lna, dB.lna)
            if c_lna ~= nil then return c_lna end

            -- 7. Кубки (ККо + КФе)
            if dA.cups.total ~= dB.cups.total then return dA.cups.total > dB.cups.total end
            local c_cups = compare_recent(years_desc, dA.cups, dB.cups)
            if c_cups ~= nil then return c_cups end

            -- 9. ЧЧМ
            if dA.ccm.total ~= dB.ccm.total then return dA.ccm.total > dB.ccm.total end
            local c_ccm = compare_recent(years_desc, dA.ccm, dB.ccm)
            if c_ccm ~= nil then return c_ccm end

            -- 11. Алфавит
            return a < b
        end
    end

    local sort_old = create_sorter("old")
    local sort_new = create_sorter("new")

    local function sort_hist(a, b)
        local dA = teams[a].hist.total
        local dB = teams[b].hist.total
        if dA ~= dB then return dA > dB end
        return a < b
    end

    -- =========================================================
    -- ПОДГОТОВКА ИТОГОВЫХ МАССИВОВ
    -- =========================================================
    local lists = { old = {}, new = {}, hist = {} }

    for code, t_data in pairs(teams) do
        if t_data.old.total > 0 then table.insert(lists.old, code) end
        if t_data.new.total > 0 then table.insert(lists.new, code) end
        if t_data.is_ctm_finalist then table.insert(lists.hist, code) end
    end

    table.sort(lists.old, sort_old)
    table.sort(lists.new, sort_new)
    table.sort(lists.hist, sort_hist)

    return {
        raw_data = teams,          
        years_desc = years_desc,   
        ranking_old = lists.old,   
        ranking_new = lists.new,   
        ranking_hist = lists.hist  
    }
end

return Calc