Модуль:Significance

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

Модуль значимости игроков. Работает как для искомой страницы, полностью её собирая, так и как чистый движок в составе других модулей.

Использование

{{#invoke:Significance|drawAll}}


Пожалуйста, добавляйте категории на страницу документации.

-- =====================================================
-- Модуль:Significance (калькулятор значимости игроков)
-- Версия 1.0
-- =====================================================
local Significance = {}

local Config = require('Module:Config')
local StatEngine = require('Module:StatEngine')
local Megarating = require('Module:Megarating')

-- ==========================================
-- РУЧНЫЕ ИСКЛЮЧЕНИЯ (Особые случаи)
-- ==========================================
local SPECIAL_CASES = {
    ["Амид"] = "[[ЦАР-КИР2026|о.случай]]",
    ["Губа"] = "[[Финал ЧТМ-2014#Дополнительное время|о.случай]]",
    ["Другой Очкан"] = "[[ДОМ-АФГ2018#Серия пенальти|о.случай]]",
    ["Рамиз"] = "[[ДОМ-АФГ2018#Серия пенальти|о.случай]]",
}

-- =====================================================
-- API ДВИЖКА (Для использования во внешних "Монстрах")
-- =====================================================

-- 1. Выдает пустую "болванку" (схему) для нового игрока
function Significance.createPlayerStruct(name)
    return {
        name = name, 
        tournaments_played = 0, top5 = 0, top10 = 0,
        matches_total = 0, goals = {total=0}, assists = {total=0}, mvp = {is_mvp=0},
        megarating = {final_goals=0, final_assists=0},
        awards = {
            golden_spheres=0, other_spheres=0, golden_shoes=0, other_shoes=0, 
            golden_assistants=0, other_assistants=0, best_goalies=0, 
            superchamps=0, titles=0, finals_played=0
        }
    }
end

-- 2. Вливает статистику из БД одного года в общую копилку игрока
function Significance.mergeStats(target, source)
    target.matches_total = (target.matches_total or 0) + (source.matches_total or 0)
    target.goals.total = (target.goals.total or 0) + (source.goals.total or 0)
    target.assists.total = (target.assists.total or 0) + (source.assists.total or 0)
    target.mvp.is_mvp = (target.mvp.is_mvp or 0) + (source.mvp.is_mvp or 0)
    
    target.megarating.final_goals = (target.megarating.final_goals or 0) + (source.megarating.final_goals or 0)
    target.megarating.final_assists = (target.megarating.final_assists or 0) + (source.megarating.final_assists or 0)
    
    if source.awards then
        target.awards.golden_spheres = target.awards.golden_spheres + (source.awards.golden_spheres or 0)
        target.awards.other_spheres = target.awards.other_spheres + (source.awards.other_spheres or 0)
        target.awards.golden_shoes = target.awards.golden_shoes + (source.awards.golden_shoes or 0)
        target.awards.other_shoes = target.awards.other_shoes + (source.awards.other_shoes or 0)
        target.awards.golden_assistants = target.awards.golden_assistants + (source.awards.golden_assistants or 0)
        target.awards.other_assistants = target.awards.other_assistants + (source.awards.other_assistants or 0)
        target.awards.best_goalies = target.awards.best_goalies + (source.awards.best_goalies or 0)
        target.awards.superchamps = target.awards.superchamps + (source.awards.superchamps or 0)
        target.awards.titles = target.awards.titles + (source.awards.titles or 0)
        target.awards.finals_played = target.awards.finals_played + (source.awards.finals_played or 0)
    end
end

-- 3. ЧИСТЫЙ КАЛЬКУЛЯТОР. Принимает заполненную структуру, возвращает уровень значимости.
function Significance.evaluatePlayer(p_stats)
    local aw = p_stats.awards
    local sum_awards = aw.other_spheres + aw.golden_shoes + aw.other_shoes + aw.best_goalies
    local any_award_exist = (sum_awards > 0) or (aw.golden_spheres > 0)

    -- 1. МАКСИМАЛЬНАЯ
    local m_c1 = (aw.golden_spheres >= 1)
    local m_c2 = (aw.titles >= 2 or aw.superchamps >= 1)
    local m_c3 = (p_stats.goals.total >= 75 or p_stats.assists.total >= 50 or p_stats.mvp.is_mvp >= 20)
    local m_c4 = (p_stats.megarating.final_goals >= 2 or p_stats.megarating.final_assists >= 3 or aw.finals_played >= 4)
    local m_c5 = (sum_awards >= 4)
    local m_c6 = (p_stats.top5 >= 2 or p_stats.top10 >= 4)
    if m_c1 and ((m_c1 and 1 or 0) + (m_c2 and 1 or 0) + (m_c3 and 1 or 0) + (m_c4 and 1 or 0) + (m_c5 and 1 or 0) + (m_c6 and 1 or 0)) >= 4 then return "Максимальная" end

    -- 2. ВЫСОКАЯ
    local h_score = (any_award_exist and 1 or 0) + ((aw.titles >= 1) and 1 or 0) + ((p_stats.goals.total >= 40 or p_stats.assists.total >= 25 or p_stats.mvp.is_mvp >= 10) and 1 or 0) + ((p_stats.megarating.final_goals >= 1 or p_stats.megarating.final_assists >= 2 or aw.finals_played >= 3) and 1 or 0) + ((p_stats.tournaments_played >= 6) and 1 or 0) + ((p_stats.top5 >= 1 or p_stats.top10 >= 3) and 1 or 0)
    if h_score >= 4 then return "Высокая" end

    -- 3. СРЕДНЯЯ
    local a_score = (any_award_exist and 1 or 0) + ((aw.titles >= 1) and 1 or 0) + ((p_stats.goals.total >= 20 or p_stats.assists.total >= 15 or p_stats.mvp.is_mvp >= 5) and 1 or 0) + ((p_stats.megarating.final_goals >= 1 or p_stats.megarating.final_assists >= 2 or aw.finals_played >= 3) and 1 or 0) + ((p_stats.tournaments_played >= 4) and 1 or 0) + ((p_stats.top5 >= 1 or p_stats.top10 >= 2) and 1 or 0)
    if a_score >= 2 then return "Средняя" end

    -- 4. НИЖЕ СРЕДНЕГО
    local main_score = ((p_stats.goals.total >= 10) and 1 or 0) + ((aw.titles >= 1 or aw.finals_played >= 2) and 1 or 0) + ((p_stats.tournaments_played >= 3) and 1 or 0) + ((p_stats.mvp.is_mvp >= 3) and 1 or 0) + ((p_stats.assists.total >= 8) and 1 or 0) + ((p_stats.megarating.final_goals >= 1 or p_stats.megarating.final_assists >= 1) and 1 or 0) + ((p_stats.top10 >= 1) and 1 or 0)
    local sub_score = ((p_stats.goals.total >= 5) and 1 or 0) + ((aw.finals_played >= 1) and 1 or 0) + ((p_stats.tournaments_played >= 2) and 1 or 0) + ((p_stats.mvp.is_mvp >= 2) and 1 or 0) + ((p_stats.assists.total >= 4) and 1 or 0)
    if main_score >= 1 or sub_score >= 2 then return "Ниже среднего" end

    -- 5. НИЗКАЯ
    local l_score = ((p_stats.goals.total >= 1) and 1 or 0) + ((aw.finals_played >= 1) and 1 or 0) + ((p_stats.tournaments_played >= 3) and 1 or 0) + ((p_stats.mvp.is_mvp >= 1) and 1 or 0) + ((p_stats.assists.total >= 2) and 1 or 0) + ((p_stats.matches_total >= 10) and 1 or 0)
    if l_score >= 1 or SPECIAL_CASES[p_stats.name] then return "Низкая" end

    return "Случайный прохожий"
end


-- =====================================================
-- ВНУТРЕННИЙ ФУНКЦИОНАЛ ДЛЯ ОТРИСОВКИ СТРАНИЦЫ
-- =====================================================

local function get_plural_players(count)
    local d10 = count % 10
    local d100 = count % 100
    if d10 == 1 and d100 ~= 11 then return count .. " игрок"
    elseif d10 >= 2 and d10 <= 4 and (d100 < 10 or d100 >= 20) then return count .. " игрока"
    else return count .. " игроков" end
end

local function cell(text, is_main, is_sub)
    local bg = ""
    if is_main then bg = Config.styles.lightgreen
    elseif is_sub then bg = Config.styles.palegoldenrod end
    return { text = tostring(text), style = Config.styles.center_nowrap .. bg }
end

local _grand_cache = nil

local function build_master_database()
    if _grand_cache then return _grand_cache end
    local GrandStats = {}
    local latest_year = Config.years[#Config.years]
    local is_latest_finished = Config.is_latest_finished

    for _, year in ipairs(Config.years) do
        local is_finished = (year ~= latest_year) or is_latest_finished
        local success, year_db = pcall(require, 'Module:Data/' .. year)
        
        if success and type(year_db) == "table" then
            local year_stats = StatEngine.Harvester.run(year_db, {need_players = true})
            for p_name, p_data in pairs(year_stats.Players) do
                if not GrandStats[p_name] then
                    -- ИСПОЛЬЗУЕМ СОБСТВЕННЫЙ API
                    GrandStats[p_name] = Significance.createPlayerStruct(p_name)
                end
                GrandStats[p_name].tournaments_played = GrandStats[p_name].tournaments_played + 1
                -- ИСПОЛЬЗУЕМ СОБСТВЕННЫЙ API
                Significance.mergeStats(GrandStats[p_name], p_data)
            end
            
            if is_finished then
                local mr_list = Megarating.evaluate_raw_stats(year, year_db, year_stats.Players)
                local spheres = Megarating.get_spheres_from_db(year_db)
                if #mr_list > 0 then
                    table.sort(mr_list, function(a, b) return Megarating.compare_players_public(a, b, year, spheres) end)
                    local current_rank = 1
                    for i, p in ipairs(mr_list) do
                        if i > 1 and p.total_points ~= mr_list[i-1].total_points then current_rank = i end
                        if current_rank <= 10 then
                            if GrandStats[p.name] then
                                GrandStats[p.name].top10 = GrandStats[p.name].top10 + 1
                                if current_rank <= 5 then GrandStats[p.name].top5 = GrandStats[p.name].top5 + 1 end
                            end
                        else break end
                    end
                end
            end
        end
    end
    _grand_cache = GrandStats
    return _grand_cache
end

-- (Тут идёт функция build_tier_table без изменений)
local function build_tier_table(tier, list)
    if #list == 0 then return "" end
    local html

    for _, p in ipairs(list) do
        local aw = p.awards
        local g = p.goals.total
        local a = p.assists.total
        local m = p.mvp.is_mvp
        local fg = p.megarating.final_goals
        local fa = p.megarating.final_assists
        local fp = aw.finals_played
        local tp = p.tournaments_played
        local t5 = p.top5
        local t10 = p.top10
        
        -- Группировка наград для вывода
        local all_shoes = aw.golden_shoes + aw.other_shoes
        local all_spheres = aw.golden_spheres + aw.other_spheres
        local other_spheres = aw.other_spheres
        local goalies = aw.best_goalies
        
        local sum_aw = other_spheres + all_shoes + goalies
        local sum_all_aw = all_spheres + all_shoes + goalies
        
        if tier == "Максимальная" then
            if not html then html = Config.builder.start({"Игрок", "ЗШ", "Ч/СЧ", "Г/П/MVP", "ГФ/ПФ/УФ", "Ш/Б/В", "М5/М10"}) end
            local c1 = aw.golden_spheres >= 1
            local c2 = aw.titles >= 2 or aw.superchamps >= 1
            local c3 = g >= 75 or a >= 50 or m >= 20
            local c4 = fg >= 2 or fa >= 3 or fp >= 4
            local c5 = sum_aw >= 4
            local str_awards = other_spheres .. " / " .. all_shoes .. " / " .. goalies
            local c6 = t5 >= 2 or t10 >= 4
            Config.builder.row(html, {
                "'''[[" .. p.name .. "]]'''",
                cell(aw.golden_spheres, c1), cell(aw.titles .. " / " .. aw.superchamps, c2),
                cell(g .. " / " .. a .. " / " .. m, c3), cell(fg .. " / " .. fa .. " / " .. fp, c4),
                cell(str_awards, c5), cell(t5 .. " / " .. t10, c6)
            })

        elseif tier == "Высокая" or tier == "Средняя" then
            if not html then html = Config.builder.start({"Игрок", "Ш/Б/В", "Ч", "Г/П/MVP", "ГФ/ПФ/УФ", "ФТ", "М5/М10"}) end
            local c1 = sum_all_aw >= 1
            local c2 = aw.titles >= 1
            local c3, c4, c5, c6
            if tier == "Высокая" then
                c3 = g >= 40 or a >= 25 or m >= 10
                c4 = fg >= 1 or fa >= 2 or fp >= 3
                c5 = tp >= 6
                c6 = t5 >= 1 or t10 >= 3
            else
                c3 = g >= 20 or a >= 15 or m >= 5
                c4 = fg >= 1 or fa >= 2 or fp >= 3
                c5 = tp >= 4
                c6 = t5 >= 1 or t10 >= 2
            end
            local str_awards = all_spheres .. " / " .. all_shoes .. " / " .. goalies
            Config.builder.row(html, {
                "'''[[" .. p.name .. "]]'''",
                cell(str_awards, c1), cell(aw.titles, c2),
                cell(g .. " / " .. a .. " / " .. m, c3), cell(fg .. " / " .. fa .. " / " .. fp, c4),
                cell(tp, c5), cell(t5 .. " / " .. t10, c6)
            })

        elseif tier == "Ниже среднего" then
            if not html then html = Config.builder.start({"Игрок", "Г", "Ч/Ф", "ФТ", "MVP", "П", "ГФ/ПФ", "М10"}) end
            local cg_m, cg_s = g >= 10, g >= 5
            local ccf_m, ccf_s = (aw.titles >= 1 or fp >= 2), fp >= 1
            local cft_m, cft_s = tp >= 3, tp >= 2
            local cm_m, cm_s = m >= 3, m >= 2
            local cp_m, cp_s = a >= 8, a >= 4
            local cgf_m = fg >= 1 or fa >= 1
            local ct10_m = t10 >= 1
            Config.builder.row(html, {
                "'''[[" .. p.name .. "]]'''",
                cell(g, cg_m, cg_s and not cg_m), cell(aw.titles .. " / " .. fp, ccf_m, ccf_s and not ccf_m),
                cell(tp, cft_m, cft_s and not cft_m), cell(m, cm_m, cm_s and not cm_m),
                cell(a, cp_m, cp_s and not cp_m), cell(fg .. " / " .. fa, cgf_m), cell(t10, ct10_m)
            })

        elseif tier == "Низкая" then
            if not html then html = Config.builder.start({"Игрок", "Г", "Ф", "ФТ", "MVP", "П", "М", "О.случай"}) end
            local cg = g >= 1; local cf = fp >= 1; local cft = tp >= 3
            local cmvp = m >= 1; local cp = a >= 2; local cm = p.matches_total >= 10
            local sp = SPECIAL_CASES[p.name]
            local csp = sp ~= nil
            Config.builder.row(html, {
                "'''[[" .. p.name .. "]]'''",
                cell(g, cg), cell(fp, cf), cell(tp, cft),
                cell(m, cmvp), cell(a, cp), cell(p.matches_total, cm),
                cell(sp or "", csp)
            })
        end
    end
    return html and tostring(html) or ""
end

-- (Тут идёт таблица TEXT_BLOCKS и ORDER без изменений)
local TEXT_BLOCKS = {
    ["Максимальная"] = [=[== Максимальная значимость ==

Критерии:
# Минимум один [[Золотой Шар]].
# Минимум два титула чемпиона третьего мира или одно [[суперчемпион]]ство.
# [[Голы|Забито]] не менее 75 голов, [[Передачи|отдано]] не менее 50 подтверждённых голевых передач или не менее 20 признаний [[Список лучших игроков всех матчей ЧТМ|игроком матча]].
# Забито хотя бы два гола в финале, отданы хотя бы три голевые передачи в финале или принято участие минимум в четырёх финалах.
# Минимум четыре любых [[Золотой Шар|Шара]] (кроме Золотого), любых [[Золотой Башмак|Башмака]] или титула [[Приз имени Эльнура|лучшего вратаря]] (все призы суммируются).
# Минимум два вхождения в пятёрку [[мегарейтинг]]а или четыре — в десятку.
Для получения максимальной значимости необходимо удовлетворять минимум четырём критериям из шести, при этом '''наличие Золотого Шара является обязательным'''.]=],

    ["Высокая"] = [=[== Высокая значимость ==
Критерии:
# Минимум один любой [[Золотой Шар|Шар]], любой [[Золотой Башмак|Башмак]] или титул [[Приз имени Эльнура|лучшего вратаря]].
# Завоёван хотя бы один титул чемпиона третьего мира.
# [[Голы|Забито]] не менее 40 голов, [[Передачи|отдано]] не менее 25 подтверждённых голевых передач или не менее 10 признаний [[Список лучших игроков всех матчей ЧТМ|игроком матча]].
# Забит хотя бы один гол в финале, отданы хотя бы две голевые передачи в финале или принято участие минимум в трёх финалах.
# Участие минимум в шести [[Список участий в финальных турнирах|финальных турнирах ЧТМ]].
# Минимум одно вхождение в пятёрку [[мегарейтинг]]а или три — в десятку.
Для получения высокой значимости необходимо удовлетворять минимум четырём критериям из шести.]=],

    ["Средняя"] = [=[== Средняя значимость ==
Критерии:
# Минимум один любой [[Золотой Шар|Шар]], любой [[Золотой Башмак|Башмак]] или титул [[Приз имени Эльнура|лучшего вратаря]].
# Завоёван хотя бы один титул чемпиона третьего мира.
# [[Голы|Забито]] не менее 20 голов, [[Передачи|отдано]] не менее 15 подтверждённых голевых передач или не менее 5 признаний [[Список лучших игроков всех матчей ЧТМ|игроком матча]].
# Забит хотя бы один гол в финале, отданы хотя бы две голевые передачи в финале или принято участие минимум в трёх финалах.
# Участие минимум в четырёх [[Список участий в финальных турнирах|финальных турнирах ЧТМ]].
# Минимум одно вхождение в пятёрку [[мегарейтинг]]а или два — в десятку.
Для получения средней значимости необходимо удовлетворять минимум минимум двум критериям из шести.]=],

    ["Ниже среднего"] = [=[== Ниже среднего ==
Критерии:
# [[Голы|Забито]] не менее 10 (основной показатель) или 5 (дополнительный) голов.
# Завоёван хотя бы один титул чемпиона третьего мира или участие минимум в двух (основной) или одном (дополнительный) финале.
# Участие минимум в трёх (основной) или двух (дополнительный) финальных турнирах ЧТМ.
# Признание [[Список лучших игроков всех матчей ЧТМ|игроком матча]] не менее трёх (основной показатель) или двух (дополнительный) раз.
# [[Передачи|Отдано]] не менее 8 (основной показатель) или 4 (дополнительный) подтверждённых голевых передач.
# Забит хотя бы один гол или отдана хотя бы одна голевая передача в финале (основной показатель).
# Минимум одно вхождение в десятку [[мегарейтинг]]а.
Необходимо соответствовать хотя бы одному из критериев в первом случае и хотя бы двум во втором.]=],

    ["Низкая"] = [=[== Низкая ==
Критерии:
# [[Голы|Забит]] хотя бы один гол.
# Участие минимум в одном финале.
# Участие минимум в трёх финальных турнирах ЧТМ.
# Признание [[Список лучших игроков всех матчей ЧТМ|игроком матча]] хотя бы один раз.
# [[Передачи|Отданы]] хотя бы две подтверждённые голевые передачи.
# [[Матчи|Сыграно]] не менее 10 подтверждённых матчей.
Необходимо соответствовать хотя бы одному из критериев. В указанных дополнительно особых случаях возможно присвоение этого уровня случайным игрокам, хоть чем-то запомнившимся.]=],

    ["Случайный прохожий"] = [=[== Случайный прохожий ==
Случайные люди, приходившие играть один-два раза в проходных матчах и абсолютно ничем не запомнившиеся.]=]
}

local ORDER = {"Максимальная", "Высокая", "Средняя", "Ниже среднего", "Низкая", "Случайный прохожий"}

function Significance.drawAll(frame)
    local db = build_master_database()
    local bins = { ["Максимальная"]={}, ["Высокая"]={}, ["Средняя"]={}, ["Ниже среднего"]={}, ["Низкая"]={}, ["Случайный прохожий"]={} }

    for _, p_stats in pairs(db) do
        -- ИСПОЛЬЗУЕМ СОБСТВЕННЫЙ API
        local level = Significance.evaluatePlayer(p_stats)
        table.insert(bins[level], p_stats)
    end
    
    for _, list in pairs(bins) do table.sort(list, function(a, b) return a.name < b.name end) end

    local out = {}
    for _, tier in ipairs(ORDER) do
        local list = bins[tier]
        table.insert(out, TEXT_BLOCKS[tier])
        
        if tier == "Случайный прохожий" then
            table.insert(out, "\n'''Точное число игроков неизвестно:'''")
            local links = {}
            for _, p in ipairs(list) do table.insert(links, "[[" .. p.name .. "]]") end
            table.insert(out, table.concat(links, ", ") .. ".")
        else
            table.insert(out, "\n'''" .. get_plural_players(#list) .. ":'''")
            table.insert(out, build_tier_table(tier, list))
        end
        table.insert(out, "")
    end

    return frame:preprocess(Config.styles.wiki_templates .. "\n" .. table.concat(out, "\n"))
end

return Significance