Модуль:StatEngine/RatingBuilder: различия между версиями
Перейти к навигации
Перейти к поиску
Документация
Пожалуйста, добавляйте категории на страницу документации.
Подбырин (обсуждение | вклад) Нет описания правки |
словарь вынесен в Модуль:Data/RatingCalc |
||
| (не показано 8 промежуточных версий 3 участников) | |||
| Строка 5: | Строка 5: | ||
local Builder = {} | local Builder = {} | ||
local TeamsDB = require('Модуль:Data/Teams') | local TeamsDB = require('Модуль:Data/Teams') | ||
local RatingData = require('Модуль:Data/RatingCalc') | |||
local ranks = RatingData.ranks | |||
local manual_overrides = RatingData.manual_overrides | |||
local ranks = | |||
-- ========================================================= | -- ========================================================= | ||
-- УТИЛИТЫ | |||
-- УТИЛИТЫ | |||
-- ========================================================= | -- ========================================================= | ||
local function get_match_result(m, rounds) | local function get_match_result(m, rounds) | ||
| Строка 110: | Строка 23: | ||
if p2 > p1 then return t2, t1 end | if p2 > p1 then return t2, t1 end | ||
else | else | ||
-- 1. | -- 1. Сравниваем общую сумму голов | ||
local g1_agg = (m[3] or 0) + (m[5] or 0) | local g1_agg = (m[3] or 0) + (m[5] or 0) | ||
local g2_agg = (m[4] or 0) + (m[6] or 0) | local g2_agg = (m[4] or 0) + (m[6] or 0) | ||
if g1_agg > g2_agg then return t1, t2 end | if g1_agg > g2_agg then return t1, t2 end | ||
if g2_agg > g1_agg then return t2, t1 end | if g2_agg > g1_agg then return t2, t1 end | ||
-- 2. | -- 2. Пенальти (Индексы 8 и 9) | ||
local p1, p2 = m[8] or 0, m[9] or 0 | local p1, p2 = m[8] or 0, m[9] or 0 | ||
if p1 > p2 then return t1, t2 end | if p1 > p2 then return t1, t2 end | ||
if p2 > p1 then return t2, t1 end | if p2 > p1 then return t2, t1 end | ||
-- 3. | -- 3. ПРАВИЛО ГОСТЕВЫХ ГОЛОВ (Индексы 5 и 4) | ||
local t1_away = m[5] or 0 | local t1_away = m[5] or 0 | ||
local t2_away = m[4] or 0 | local t2_away = m[4] or 0 | ||
| Строка 189: | Строка 102: | ||
local function process_lna(stages) | local function process_lna(stages) | ||
local team_results = {} | local team_results = {} | ||
-- 1. ГРУППЫ (1R) | |||
for stage_name, s_data in pairs(stages) do | for stage_name, s_data in pairs(stages) do | ||
local r, div, reg = stage_name:match("^(%d)R_([A-D])_([^_]+)") | |||
local r, div, reg = stage_name:match("^(%d)R_([A- | |||
if r == "1" and s_data.type == "group" then | if r == "1" and s_data.type == "group" then | ||
local num_teams = #s_data.standings | local num_teams = #s_data.standings | ||
for place, row in ipairs(s_data.standings) do | for place, row in ipairs(s_data.standings) do | ||
local team = row[1] | local team = row[1] | ||
local key = place .. "г" .. div | local key = place .. "г" .. div | ||
-- Халявщики в низших дивизионах (D везде, C в Океании) | |||
if num_teams == 3 and place == 3 and (div == "D" or (div == "C" and reg == "Oceania")) then | if num_teams == 3 and place == 3 and (div == "D" or (div == "C" and reg == "Oceania")) then | ||
local pts = get_group_stats(team, {[stage_name] = s_data}) | local pts = get_group_stats(team, {[stage_name] = s_data}) | ||
| Строка 211: | Строка 122: | ||
end | end | ||
end | end | ||
-- 2. ПЛЕЙ-ОФФ ДИВИЗИОНОВ (2R) | |||
for stage_name, s_data in pairs(stages) do | for stage_name, s_data in pairs(stages) do | ||
local r, div = stage_name:match("^(%d)R_([A- | local r, div = stage_name:match("^(%d)R_([A-D])_") | ||
if r == "2" and s_data.type == "knockout" then | if r == "2" and s_data.type == "knockout" then | ||
for _, match in ipairs(s_data.matches) do | for _, match in ipairs(s_data.matches) do | ||
local winner, loser = get_match_result(match, s_data.number_of_rounds) | local winner, loser = get_match_result(match, s_data.number_of_rounds) | ||
| Строка 231: | Строка 140: | ||
end | end | ||
end | end | ||
-- 3. ГЛОБАЛЬНЫЙ ПЛЕЙ-ОФФ ЗА МЕДАЛИ (3R) | |||
for stage_name, s_data in pairs(stages) do | for stage_name, s_data in pairs(stages) do | ||
local r = stage_name:match("^(%d)R_") | local r = stage_name:match("^(%d)R_") | ||
| Строка 239: | Строка 150: | ||
if stage_name:match("3rdPlace") then update_result(team_results, winner, "б"); update_result(team_results, loser, "д") | if stage_name:match("3rdPlace") then update_result(team_results, winner, "б"); update_result(team_results, loser, "д") | ||
elseif stage_name:match("Final") then update_result(team_results, winner, "ч"); update_result(team_results, loser, "ф") | elseif stage_name:match("Final") then update_result(team_results, winner, "ч"); update_result(team_results, loser, "ф") | ||
elseif stage_name:match("Semifinal") then update_result(team_results, loser, "б") end | elseif stage_name:match("Semifinal") then update_result(team_results, loser, "б") -- Если нет 3-го места | ||
end | |||
end | end | ||
end | end | ||
end | end | ||
end | end | ||
return team_results | return team_results | ||
end | end | ||
| Строка 253: | Строка 166: | ||
local team_results = {} | local team_results = {} | ||
local is_qual = string.match(full_name, "Qual") ~= nil | local is_qual = string.match(full_name, "Qual") ~= nil | ||
local max_r_by_region = {} | local max_r_by_region = {} | ||
for stage_name in pairs(stages) do | for stage_name in pairs(stages) do | ||
| Строка 260: | Строка 173: | ||
if r then | if r then | ||
r = tonumber(r) | r = tonumber(r) | ||
if reg == "GroupA" or reg:match("Group") or reg | if reg == "GroupA" or reg:match("Group") or reg == "Playoffs" then reg = "Global" end | ||
max_r_by_region[reg] = math.max(max_r_by_region[reg] or 0, r) | max_r_by_region[reg] = math.max(max_r_by_region[reg] or 0, r) | ||
end | end | ||
| Строка 269: | Строка 182: | ||
if not r then r = stage_name:match("^(%d+)R_"); reg = "Global" end | if not r then r = stage_name:match("^(%d+)R_"); reg = "Global" end | ||
if reg == "GroupA" or (reg and reg:match("Group")) then reg = "Global" end | if reg == "GroupA" or (reg and reg:match("Group")) then reg = "Global" end | ||
if not r then return "о" end | if not r then return string.match(stage_name, "Playoff") and "ос" or "о" end | ||
r = tonumber(r) | r = tonumber(r) | ||
if r == 0 then return "оп" end | if r == 0 then return "оп" end | ||
-- ФИКС 2034/2038: Если в глобале раундов больше, чем в регионе, значит отбор многоступенчатый для всех | |||
local max_r = max_r_by_region[reg] or 1 | local max_r = max_r_by_region[reg] or 1 | ||
if max_r_by_region["Global"] and max_r_by_region["Global"] > max_r then | if max_r_by_region["Global"] and max_r_by_region["Global"] > max_r then | ||
max_r = max_r_by_region["Global"] | max_r = max_r_by_region["Global"] | ||
end | end | ||
local prefix = (max_r <= 1) and "о-" or ("о" .. r .. "-") | local prefix = (max_r <= 1) and "о-" or ("о" .. r .. "-") | ||
if place then return prefix .. place .. "г" end | if place then return prefix .. place .. "г" end | ||
if string.match(stage_name, "Playoff") then return "ос" end | |||
return "о" .. r | return "о" .. r | ||
end | end | ||
for stage_name, s_data in pairs(stages) do | for stage_name, s_data in pairs(stages) do | ||
if s_data.type == "group" and string.match(stage_name, "Group") then | if s_data.type == "group" and string.match(stage_name, "Group") then | ||
for place, row in ipairs(s_data.standings) do | for place, row in ipairs(s_data.standings) do | ||
| Строка 302: | Строка 216: | ||
end | end | ||
end | end | ||
local has_3rd_place_match = stages["FR_3rdPlace"] ~= nil | local has_3rd_place_match = stages["FR_3rdPlace"] ~= nil | ||
for stage_name, s_data in pairs(stages) do | for stage_name, s_data in pairs(stages) do | ||
if s_data.type == "knockout" then | if s_data.type == "knockout" then | ||
local is_playoff = string.match(stage_name, "Playoff") ~= nil | local is_playoff = string.match(stage_name, "Playoff") ~= nil | ||
for _, match in ipairs(s_data.matches) do | for _, match in ipairs(s_data.matches) do | ||
| Строка 323: | Строка 236: | ||
if stage_name == "FR_Last 16" or stage_name == "FR_Last16" then update_result(team_results, loser, "в") | if stage_name == "FR_Last 16" or stage_name == "FR_Last16" then update_result(team_results, loser, "в") | ||
elseif stage_name == "FR_Quarterfinal" then update_result(team_results, loser, "чф") | elseif stage_name == "FR_Quarterfinal" then update_result(team_results, loser, "чф") | ||
elseif stage_name == "FR_3rdPlace" then update_result(team_results, winner, "б"); update_result(team_results, loser, "д") | elseif stage_name == "FR_3rdPlace" then update_result(team_results, winner, "б"); update_result(team_results, loser, "д") | ||
elseif stage_name == "FR_Final" then update_result(team_results, winner, "ч"); update_result(team_results, loser, "ф") | elseif stage_name == "FR_Final" then update_result(team_results, winner, "ч"); update_result(team_results, loser, "ф") | ||
| Строка 334: | Строка 245: | ||
end | end | ||
end | end | ||
for t in pairs(global_playoffs) do | for t in pairs(global_playoffs) do | ||
if team_results[t] then | if team_results[t] then | ||
| Строка 347: | Строка 258: | ||
end | end | ||
end | end | ||
return team_results | return team_results | ||
end | end | ||
| Строка 357: | Строка 268: | ||
local output = {} | local output = {} | ||
local global_playoff_participants = {} | local global_playoff_participants = {} | ||
for full_tourney_name, stages in pairs(db_tournaments) do | for full_tourney_name, stages in pairs(db_tournaments) do | ||
local base_tourney = string.match(full_tourney_name, "^([^_]+)") | local base_tourney = string.match(full_tourney_name, "^([^_]+)") | ||
if not output[base_tourney] then output[base_tourney] = {} end | if not output[base_tourney] then output[base_tourney] = {} end | ||
if not global_playoff_participants[base_tourney] then global_playoff_participants[base_tourney] = {} end | if not global_playoff_participants[base_tourney] then global_playoff_participants[base_tourney] = {} end | ||
local team_results = {} | local team_results = {} | ||
if base_tourney == "ЛНа" then | |||
if | |||
team_results = process_lna(stages) | team_results = process_lna(stages) | ||
else | else | ||
team_results = process_tournament(full_tourney_name, stages, global_playoff_participants[base_tourney]) | team_results = process_tournament(full_tourney_name, stages, global_playoff_participants[base_tourney]) | ||
end | end | ||
-- ХУКИ | -- ХУКИ | ||
if year == 2009 and base_tourney == "КОк" then assign_places_by_stats(team_results, "2г", {"б", "д"}, stages) end | if year == 2009 and base_tourney == "КОк" then assign_places_by_stats(team_results, "2г", {"б", "д"}, stages) end | ||
| Строка 411: | Строка 321: | ||
for t, res in pairs(team_results) do if res == "3г" then team_results[t] = "б" end end | for t, res in pairs(team_results) do if res == "3г" then team_results[t] = "б" end end | ||
end | end | ||
for t, k in pairs(team_results) do update_result(output[base_tourney], t, k) end | for t, k in pairs(team_results) do update_result(output[base_tourney], t, k) end | ||
end | end | ||
-- ИМПОРТ ОТБОРОВ | |||
if year == 2009 or year == 2013 or year == 2017 then | if year == 2009 or year == 2013 or year == 2017 then | ||
local next_year = year + 1 | local next_year = year + 1 | ||
| Строка 422: | Строка 333: | ||
local ctm_results = process_tournament("ЧТМ_"..next_year.."_Qual", next_db["ЧТМ_"..next_year.."_Qual"], dummy_playoffs) | local ctm_results = process_tournament("ЧТМ_"..next_year.."_Qual", next_db["ЧТМ_"..next_year.."_Qual"], dummy_playoffs) | ||
local conf_to_cup = { ["Америка"] = "КАм", ["Африка"] = "КАф", ["Евразия"] = "КЕв" } | local conf_to_cup = { ["Америка"] = "КАм", ["Африка"] = "КАф", ["Евразия"] = "КЕв" } | ||
for team, res_key in pairs(ctm_results) do | for team, res_key in pairs(ctm_results) do | ||
local t_info = TeamsDB.getTeam(team) | local t_info = TeamsDB.getTeam(team) | ||
| Строка 447: | Строка 358: | ||
end | end | ||
end | end | ||
return output | return output | ||
end | end | ||
return Builder | return Builder | ||
Текущая версия от 23:50, 2 июня 2026
[просмотр] [просмотр кода] [история] [обновить]
Сейчас проводятся усиленные тесты на странице RatingBuilder. Пока модуль категорически не готов к использованию, поскольку требует очень серьёзной отладки, но потенциал у него колоссальный.
Скрипты для отладки:
- StatEngine/Tester — для сравнения со старой БД Data/Rating.
- Вызов:
{{#invoke:StatEngine/Tester|test_year|ГОД}}
- Вызов:
- StatEngine/RatingBuilder/Debug — для тупого вывода всех результатов за указанный год.
- Вызов:
{{#invoke:StatEngine/RatingBuilder/Debug|run|ГОД}}
- Вызов:
--- Промежуточные результаты --- Уникальные значения из фрагмента 1 (только в ""): ['1гB', '1гC', '1гD', '2г', '2гA', '2гB', '2гC', '2гD', '3г', '3гA', '3гB', '3гC', '3гC-no', '3гD', '3гD-no', '4г', '4гA', '4гB', '4гC', '4гD', '5', '5г', '6', '6г', '7', '8', 'б', 'бпмв', 'в', 'д', 'дпмв', 'о-2г', 'о-3г', 'о-3г-1', 'о-3г-no', 'о-4г', 'о-4г-1', 'о-4г-no', 'о-5г', 'о-5г-1', 'о-5г-no', 'о-6г', 'о-6г-1', 'о-6г-2', 'о-6г-no', 'о-7г', 'о-7г-2', 'о-7г-no', 'о-8г-no', 'о1', 'о1-1г', 'о1-2г', 'о1-3г', 'о1-3г-no', 'о1-4г', 'о1-5г', 'о2', 'о2-2г', 'о2-3г', 'о2-4г', 'о2-5г', 'о3', 'о3-3г', 'о3-4г', 'оп', 'ос', 'ос-1г', 'ос-2г', 'ос-3г', 'осп-2г', 'осп-3г', 'осп-4г', 'осп-5г', 'осф-1г', 'осф-2г', 'осф-3г', 'осф-4г', 'осчф-3г', 'осчф-4г', 'пA', 'пB', 'пC', 'пD', 'пмв', 'пмп', 'побB', 'побC', 'побD', 'пол', 'смв-2г', 'смв-3г', 'смв-4г', 'смп-2г', 'смп-3г', 'смп-4г', 'ф', 'фA', 'фB', 'фC', 'фD', 'фпмп', 'ч', 'чф', 'чфC', 'чфD', 'чфпм', 'чфпмв', 'чфпмп', '—'] Уникальные значения из фрагмента 2 (только в [""]): ['1г', '1гA', '1гB', '1гC', '1гD', '2г', '2гA', '2гB', '2гC', '2гD', '3г', '3гA', '3гA-no', '3гB', '3гB-no', '3гC', '3гC-no', '3гD', '3гD-no', '4г', '4гA', '4гB', '4гC', '4гD', '5', '5г', '6', '6г', '7', '8', 'б', 'в', 'д', 'о', 'о-1г', 'о-2г', 'о-3г', 'о-3г-no', 'о-4г', 'о-4г-no', 'о-5г', 'о-5г-no', 'о-6г', 'о-6г-no', 'о-7г', 'о-7г-no', 'о-8г', 'о-8г-no', 'о1', 'о1-1г', 'о1-2г', 'о1-3г', 'о1-3г-no', 'о1-4г', 'о1-5г', 'о1-6г', 'о2', 'о2-1г', 'о2-2г', 'о2-3г', 'о2-4г', 'о2-5г', 'о2-6г', 'о3', 'о3-1г', 'о3-2г', 'о3-3г', 'о3-4г', 'о3-5г', 'о3-6г', 'оп', 'ос', 'ос-1г', 'ос-2г', 'ос-3г', 'ос-4г', 'ос-5г', 'пA', 'пB', 'пC', 'пD', 'побB', 'побC', 'побD', 'ф', 'фA', 'фB', 'фC', 'фD', 'ч', 'чф', 'чфA', 'чфB', 'чфC', 'чфD'] --- Финальный результат --- Найдено значений: 32 1. бпмв 2. дпмв 3. о-3г-1 4. о-4г-1 5. о-5г-1 6. о-6г-1 7. о-6г-2 8. о-7г-2 9. осп-2г 10. осп-3г 11. осп-4г 12. осп-5г 13. осф-1г 14. осф-2г 15. осф-3г 16. осф-4г 17. осчф-3г 18. осчф-4г 19. пмв 20. пмп 21. пол 22. смв-2г 23. смв-3г 24. смв-4г 25. смп-2г 26. смп-3г 27. смп-4г 28. фпмп 29. чфпм 30. чфпмв 31. чфпмп 32. —
Пожалуйста, добавляйте категории на страницу документации.
-- =========================================================================
-- Модуль:StatEngine/RatingBuilder
-- =========================================================================
local Builder = {}
local TeamsDB = require('Модуль:Data/Teams')
local RatingData = require('Модуль:Data/RatingCalc')
local ranks = RatingData.ranks
local manual_overrides = RatingData.manual_overrides
-- =========================================================
-- УТИЛИТЫ
-- =========================================================
local function get_match_result(m, rounds)
local t1, t2 = m[1], m[2]
if rounds == 1 then
local g1, g2 = m[3] or 0, m[4] or 0
if g1 > g2 then return t1, t2 end
if g2 > g1 then return t2, t1 end
local p1, p2 = m[6] or 0, m[7] or 0
if p1 > p2 then return t1, t2 end
if p2 > p1 then return t2, t1 end
else
-- 1. Сравниваем общую сумму голов
local g1_agg = (m[3] or 0) + (m[5] or 0)
local g2_agg = (m[4] or 0) + (m[6] or 0)
if g1_agg > g2_agg then return t1, t2 end
if g2_agg > g1_agg then return t2, t1 end
-- 2. Пенальти (Индексы 8 и 9)
local p1, p2 = m[8] or 0, m[9] or 0
if p1 > p2 then return t1, t2 end
if p2 > p1 then return t2, t1 end
-- 3. ПРАВИЛО ГОСТЕВЫХ ГОЛОВ (Индексы 5 и 4)
local t1_away = m[5] or 0
local t2_away = m[4] or 0
if t1_away > t2_away then return t1, t2 end
if t2_away > t1_away then return t2, t1 end
end
return nil, nil
end
local function get_group_stats(team, stages)
local pts, gf, ga = 0, 0, 0
for stage_name, s_data in pairs(stages) do
if s_data.type == "group" and string.match(stage_name, "Group") then
for _, m in ipairs(s_data.matches) do
if m[1] == team then
local g1, g2 = m[3] or 0, m[4] or 0
gf = gf + g1; ga = ga + g2
if g1 > g2 then pts = pts + 3 elseif g1 == g2 then pts = pts + 1 end
elseif m[2] == team then
local g1, g2 = m[4] or 0, m[3] or 0
gf = gf + g1; ga = ga + g2
if g1 > g2 then pts = pts + 3 elseif g1 == g2 then pts = pts + 1 end
end
end
end
end
return pts, gf - ga, gf
end
local function assign_places_by_stats(team_results, source_key, target_keys, stages)
local teams = {}
for t, res in pairs(team_results) do
if res == source_key then
local pts, gd, gf = get_group_stats(t, stages)
table.insert(teams, {name = t, pts = pts, gd = gd, gf = gf})
end
end
if #teams == 0 then return end
table.sort(teams, function(a, b)
if a.pts ~= b.pts then return a.pts > b.pts end
if a.gd ~= b.gd then return a.gd > b.gd end
if a.gf ~= b.gf then return a.gf > b.gf end
return a.name < b.name
end)
local current_idx = 1
for i, t_info in ipairs(teams) do
if i > 1 then
local prev = teams[i-1]
if t_info.pts == prev.pts and t_info.gd == prev.gd and t_info.gf == prev.gf then
else current_idx = current_idx + 1 end
end
local key = target_keys[current_idx] or target_keys[#target_keys]
team_results[t_info.name] = key
end
end
local function update_result(tab, team, new_key)
if not new_key then return end
local cur_rank = tab[team] and ranks[tab[team]] or 0
local new_rank = ranks[new_key] or 0
if new_rank > cur_rank then tab[team] = new_key end
end
-- =========================================================
-- СПЕЦ-ОБРАБОТЧИК ЛИГИ НАЦИЙ (ЛНа)
-- =========================================================
local function process_lna(stages)
local team_results = {}
-- 1. ГРУППЫ (1R)
for stage_name, s_data in pairs(stages) do
local r, div, reg = stage_name:match("^(%d)R_([A-D])_([^_]+)")
if r == "1" and s_data.type == "group" then
local num_teams = #s_data.standings
for place, row in ipairs(s_data.standings) do
local team = row[1]
local key = place .. "г" .. div
-- Халявщики в низших дивизионах (D везде, C в Океании)
if num_teams == 3 and place == 3 and (div == "D" or (div == "C" and reg == "Oceania")) then
local pts = get_group_stats(team, {[stage_name] = s_data})
local req_pts = (s_data.number_of_rounds == 1) and 2 or 4
if pts < req_pts then key = key .. "-no" end
end
update_result(team_results, team, key)
end
end
end
-- 2. ПЛЕЙ-ОФФ ДИВИЗИОНОВ (2R)
for stage_name, s_data in pairs(stages) do
local r, div = stage_name:match("^(%d)R_([A-D])_")
if r == "2" and s_data.type == "knockout" then
for _, match in ipairs(s_data.matches) do
local winner, loser = get_match_result(match, s_data.number_of_rounds)
if winner and loser then
if stage_name:match("Quarterfinal") then update_result(team_results, loser, "чф" .. div)
elseif stage_name:match("Semifinal") then update_result(team_results, loser, "п" .. div)
elseif stage_name:match("Final") then
update_result(team_results, loser, "ф" .. div)
if div ~= "A" then update_result(team_results, winner, "поб" .. div) end
end
end
end
end
end
-- 3. ГЛОБАЛЬНЫЙ ПЛЕЙ-ОФФ ЗА МЕДАЛИ (3R)
for stage_name, s_data in pairs(stages) do
local r = stage_name:match("^(%d)R_")
if r == "3" and s_data.type == "knockout" then
for _, match in ipairs(s_data.matches) do
local winner, loser = get_match_result(match, s_data.number_of_rounds)
if winner and loser then
if stage_name:match("3rdPlace") then update_result(team_results, winner, "б"); update_result(team_results, loser, "д")
elseif stage_name:match("Final") then update_result(team_results, winner, "ч"); update_result(team_results, loser, "ф")
elseif stage_name:match("Semifinal") then update_result(team_results, loser, "б") -- Если нет 3-го места
end
end
end
end
end
return team_results
end
-- =========================================================
-- ОБРАБОТЧИК СТАНДАРТНОГО ТУРНИРА
-- =========================================================
local function process_tournament(full_name, stages, global_playoffs)
local team_results = {}
local is_qual = string.match(full_name, "Qual") ~= nil
local max_r_by_region = {}
for stage_name in pairs(stages) do
local r, reg = stage_name:match("^(%d+)R_([^_]+)")
if not r then r = stage_name:match("^(%d+)R_"); reg = "Global" end
if r then
r = tonumber(r)
if reg == "GroupA" or reg:match("Group") or reg == "Playoffs" then reg = "Global" end
max_r_by_region[reg] = math.max(max_r_by_region[reg] or 0, r)
end
end
local function get_qual_key(stage_name, place)
local r, reg = stage_name:match("^(%d+)R_([^_]+)")
if not r then r = stage_name:match("^(%d+)R_"); reg = "Global" end
if reg == "GroupA" or (reg and reg:match("Group")) then reg = "Global" end
if not r then return string.match(stage_name, "Playoff") and "ос" or "о" end
r = tonumber(r)
if r == 0 then return "оп" end
-- ФИКС 2034/2038: Если в глобале раундов больше, чем в регионе, значит отбор многоступенчатый для всех
local max_r = max_r_by_region[reg] or 1
if max_r_by_region["Global"] and max_r_by_region["Global"] > max_r then
max_r = max_r_by_region["Global"]
end
local prefix = (max_r <= 1) and "о-" or ("о" .. r .. "-")
if place then return prefix .. place .. "г" end
if string.match(stage_name, "Playoff") then return "ос" end
return "о" .. r
end
for stage_name, s_data in pairs(stages) do
if s_data.type == "group" and string.match(stage_name, "Group") then
for place, row in ipairs(s_data.standings) do
local team = row[1]
local key = ""
if not is_qual then key = place .. "г"
else
key = get_qual_key(stage_name, place)
if key and key ~= "оп" and #s_data.standings == 3 and place == 3 then
local pts = get_group_stats(team, {[stage_name] = s_data})
local req_pts = (s_data.number_of_rounds == 1) and 2 or 4
if pts < req_pts then key = key .. "-no" end
end
end
update_result(team_results, team, key)
end
end
end
local has_3rd_place_match = stages["FR_3rdPlace"] ~= nil
for stage_name, s_data in pairs(stages) do
if s_data.type == "knockout" then
local is_playoff = string.match(stage_name, "Playoff") ~= nil
for _, match in ipairs(s_data.matches) do
local winner, loser = get_match_result(match, s_data.number_of_rounds)
if winner and loser then
if is_qual then
if is_playoff then
global_playoffs[winner] = true
global_playoffs[loser] = true
if not team_results[loser] then update_result(team_results, loser, "ос") end
else
local key = get_qual_key(stage_name, nil)
if key then update_result(team_results, loser, key) end
end
else
if stage_name == "FR_Last 16" or stage_name == "FR_Last16" then update_result(team_results, loser, "в")
elseif stage_name == "FR_Quarterfinal" then update_result(team_results, loser, "чф")
elseif stage_name == "FR_3rdPlace" then update_result(team_results, winner, "б"); update_result(team_results, loser, "д")
elseif stage_name == "FR_Final" then update_result(team_results, winner, "ч"); update_result(team_results, loser, "ф")
elseif stage_name == "FR_Semifinal" and not has_3rd_place_match then update_result(team_results, loser, "б")
end
end
end
end
end
end
for t in pairs(global_playoffs) do
if team_results[t] then
if team_results[t] == "о" or team_results[t] == "оп" or string.match(team_results[t], "^о%d$") then
update_result(team_results, t, "ос")
else
local new_key = string.gsub(team_results[t], "^о%d?%-", "ос-")
update_result(team_results, t, new_key)
end
else
update_result(team_results, t, "ос")
end
end
return team_results
end
-- =========================================================
-- ГЛАВНАЯ ФУНКЦИЯ
-- =========================================================
function Builder.build_year(year, db_tournaments)
local output = {}
local global_playoff_participants = {}
for full_tourney_name, stages in pairs(db_tournaments) do
local base_tourney = string.match(full_tourney_name, "^([^_]+)")
if not output[base_tourney] then output[base_tourney] = {} end
if not global_playoff_participants[base_tourney] then global_playoff_participants[base_tourney] = {} end
local team_results = {}
if base_tourney == "ЛНа" then
team_results = process_lna(stages)
else
team_results = process_tournament(full_tourney_name, stages, global_playoff_participants[base_tourney])
end
-- ХУКИ
if year == 2009 and base_tourney == "КОк" then assign_places_by_stats(team_results, "2г", {"б", "д"}, stages) end
if base_tourney == "ККо" and (year == 2009 or year == 2017 or year == 2025 or year == 2029) then
assign_places_by_stats(team_results, "3г", {"5", "6"}, stages)
assign_places_by_stats(team_results, "4г", {"7", "8"}, stages)
end
if year == 2013 and base_tourney == "ККо" then
if stages["FR_Playoffs"] then
for _, m in ipairs(stages["FR_Playoffs"].matches) do
local _, loser = get_match_result(m, stages["FR_Playoffs"].number_of_rounds)
if loser then team_results[loser] = "5" end
end
end
assign_places_by_stats(team_results, "2г", {"6"}, stages)
assign_places_by_stats(team_results, "3г", {"7", "8"}, stages)
end
if year == 2021 and base_tourney == "ККо" then
for t, res in pairs(team_results) do
if res == "3г" then team_results[t] = "б" elseif res == "4г" then team_results[t] = "д"
elseif res == "5г" then team_results[t] = "5" elseif res == "6г" then team_results[t] = "6" end
end
end
if year == 2015 and base_tourney == "КФе" then
if stages["FR_Stage2_Losers"] then
for _, m in ipairs(stages["FR_Stage2_Losers"].matches) do
local _, loser = get_match_result(m, stages["FR_Stage2_Losers"].number_of_rounds)
if loser then team_results[loser] = "д" end
end
end
if stages["FR_Semifinal"] then
for _, m in ipairs(stages["FR_Semifinal"].matches) do
local _, loser = get_match_result(m, stages["FR_Semifinal"].number_of_rounds)
if loser then team_results[loser] = "б" end
end
end
end
if year == 2013 and base_tourney == "КЮжАм" then
for t, res in pairs(team_results) do if res == "3г" then team_results[t] = "б" end end
end
for t, k in pairs(team_results) do update_result(output[base_tourney], t, k) end
end
-- ИМПОРТ ОТБОРОВ
if year == 2009 or year == 2013 or year == 2017 then
local next_year = year + 1
local success, next_db = pcall(require, 'Модуль:Data/Tournaments/' .. next_year)
if success and next_db["ЧТМ_"..next_year.."_Qual"] then
local dummy_playoffs = {}
local ctm_results = process_tournament("ЧТМ_"..next_year.."_Qual", next_db["ЧТМ_"..next_year.."_Qual"], dummy_playoffs)
local conf_to_cup = { ["Америка"] = "КАм", ["Африка"] = "КАф", ["Евразия"] = "КЕв" }
for team, res_key in pairs(ctm_results) do
local t_info = TeamsDB.getTeam(team)
if t_info and t_info.conf then
local base_tourney = conf_to_cup[t_info.conf]
if base_tourney then
if not output[base_tourney] then output[base_tourney] = {} end
local final_key = res_key
if global_playoff_participants[base_tourney] and global_playoff_participants[base_tourney][team] then
if res_key == "о" or res_key == "оп" or string.match(res_key, "^о%d$") then final_key = "ос"
else final_key = string.gsub(res_key, "^о%d?%-", "ос-") end
end
update_result(output[base_tourney], team, final_key)
end
end
end
end
end
if manual_overrides[year] then
for tourney, teams in pairs(manual_overrides[year]) do
if not output[tourney] then output[tourney] = {} end
for team, key in pairs(teams) do output[tourney][team] = key end
end
end
return output
end
return Builder