Модуль:Data/RatingCalc: различия между версиями
Перейти к навигации
Перейти к поиску
пора уже наконец это всё в единую бандуру вынести, чтобы к ней подключаться |
словарь вынесен в Модуль:Data/RatingCalc |
||
| Строка 1: | Строка 1: | ||
-- ========================================================================= | -- ========================================================================= | ||
-- Модуль: | -- Модуль:StatEngine/RatingCalc | ||
-- | -- Калькулятор рейтинга Чемпионата Третьего Мира (ЧТМ) | ||
-- Чистый движок: не обращается к БД напрямую, всё получает на вход. | |||
-- Совершает ровно один проход по переданной базе данных. | |||
-- ========================================================================= | -- ========================================================================= | ||
local | 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 | return Calc | ||
Версия от 23:51, 2 июня 2026
Для документации этого модуля может быть создана страница Модуль: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