Odin SolutionsOdin Solutions

🏆

FIFA World Cup 2026

Week 23, 2026

All Solutions

Events, groups, teams, matches etc. | greenya | Odin Solutions

package main import "core:encoding/json" import "core:fmt" import "core:slice" INPUT_TYPE :: "fifa" // "fifa", "uefa" INPUT_GROUPS_JSON := #load("groups_" + INPUT_TYPE + ".json") INPUT_RANKINGS_JSON := #load("rankings_" + INPUT_TYPE + ".json") INPUT_QUALIFIERS_JSON := #load("qualifiers.json") // The third place qualifiers table INPUT_KNOCKOUT_32_JSON := #load("knockout_32.json") // The matches played in Knockout stage with 32 teams when INPUT_TYPE == "fifa" do input_ruler := Input_Match_Ruler { 100, 250, 500 } else when INPUT_TYPE == "uefa" do input_ruler := Input_Match_Ruler { 20, 40, 80 } else do #panic("input_ruler is undefined for `" + INPUT_TYPE + "`") main :: proc () { input_groups: Input_Groups json.unmarshal(INPUT_GROUPS_JSON, &input_groups) input_rankings: Input_Rankings json.unmarshal(INPUT_RANKINGS_JSON, &input_rankings) input_qualifiers: Input_Qualifiers json.unmarshal(INPUT_QUALIFIERS_JSON, &input_qualifiers) input_knockout_32: Input_Knockout_32 json.unmarshal(INPUT_KNOCKOUT_32_JSON, &input_knockout_32) fmt.ensuref(len(input_knockout_32) == 32, "Expecting 32 teams (16 pairs) in knockout_32, got %i", len(input_knockout_32)) fmt.ensuref(len(input_groups) == 12, "Expecting 12 groups, got %i", len(input_groups)) for group, i in input_groups { fmt.ensuref(len(group) == 4, "Expecting 4 teams, got %i in group #%i", len(group), i) for team in group do fmt.ensuref(team in input_rankings, "Missing ranking for team `%s`", team) } event: Event event_init(&event, input_groups, input_rankings, input_ruler) event_do_groups(&event) event_do_third_place_qualifiers_and_eliminations(&event) event_do_knockout_32(&event, input_qualifiers, input_knockout_32) event_do_knockout_bracket(&event, from=event.knockout.teams_16[:], to=event.knockout.teams_8[:]) event_do_knockout_bracket(&event, from=event.knockout.teams_8[:], to=event.knockout.teams_4[:]) event_do_knockout_bracket(&event, from=event.knockout.teams_4[:], to=event.knockout.teams_2[:]) event_do_knockout_bracket(&event, from=event.knockout.teams_2[:], to=event.knockout.teams_1[:]) } Input_Groups :: [] [] string Input_Rankings :: map [string] f32 Input_Qualifiers :: map [string] [] string Input_Knockout_32 :: [] string Input_Match_Ruler :: [3] f32 // 0..<[0] draw/penalties, [0]..<[1] 1 goal, [1]..<[2] 2 goals, [2]+ 3 goals Event :: struct { teams : [48] Event_Team, groups : [12] Event_Group, ruler : Input_Match_Ruler, knockout: struct { teams_32: [32] ^Event_Team, // Round of 32: [0..23] 12 winners and 12 runnings, [24..31] top 8 3rd places teams_16: [16] ^Event_Team, // Round of 16 teams_8 : [8] ^Event_Team, // Quarter Final teams_4 : [4] ^Event_Team, // Semi Final teams_2 : [2] ^Event_Team, // Final teams_1 : [1] ^Event_Team, // Champion }, } Event_Group :: struct { name : byte, teams : [4] ^Event_Team, } Event_Team :: struct { name : string, group : ^Event_Group, points : int, goals : int, ranking : f32, } event_init :: proc (e: ^Event, input_groups: Input_Groups, input_rankings: Input_Rankings, input_ruler: Input_Match_Ruler) { e^ = {} e.ruler = input_ruler for group, i in input_groups { e.groups[i].name = 'A' + byte(i) for team, j in group { fmt.ensuref(team in input_rankings, "Missing ranking for team `%s`", team) e.teams[i*4+j] = { name = team, group = &e.groups[i], ranking = input_rankings[team], } e.groups[i].teams[j] = &e.teams[i*4+j] } } } event_do_groups :: proc (e: ^Event) { for &group in e.groups { fmt.printfln("--- Group %c Matches ---", group.name) for i:=0; i<4; i+=1 do for j:=i+1; j<4; j+=1 { event_teams_match(group.teams[i], group.teams[j], e.ruler, draw_allowed=true) } fmt.println() event_teams_sort(group.teams[:]) fmt.printfln("--- Group %c Table ---", group.name) for t, i in group.teams { fmt.printfln("%i. %s %i %i", i+1, t.name, t.points, t.goals) } fmt.println() } } event_do_third_place_qualifiers_and_eliminations :: proc (e: ^Event) { third_place_teams: [12] ^Event_Team for group, i in e.groups { e.knockout.teams_32[i*2+0] = group.teams[0] // group winner e.knockout.teams_32[i*2+1] = group.teams[1] // group runner third_place_teams[i] = group.teams[2] // third place candidate } event_teams_sort(third_place_teams[:]) for team, i in third_place_teams { if i < 8 do e.knockout.teams_32[12*2+i] = team if i == 0 do fmt.println("--- Third Place Qualifiers ---") if i == 8 do fmt.println("--- Third Place Eliminations ---") fmt.printfln("%i. %s %i %i", i+1, team.name, team.points, team.goals) } fmt.println() } event_do_knockout_32 :: proc (e: ^Event, input_qualifiers: Input_Qualifiers, input_knockout_32: Input_Knockout_32) { fmt.println("--- Round of 32 ---") q_row := event_qualifiers_row(e, input_qualifiers) for &winner, i in e.knockout.teams_16 { pair: [2] ^Event_Team for &team, j in pair { group_name: byte team_pos: int key := input_knockout_32[i*2+j] if key == "?" { assert(1 == j) // The "?" can only be the second in a pair assert(1 == event_team_group_pos(pair[0])) // and 1st team must be a winner, e.g. 1E not 2E group_name = q_row[pair[0].group.name-'A'] team_pos = 3 } else { group_name = key[1] team_pos = int(key[0]-'0') } team = event_team_in_group(e.knockout.teams_32[:], group_name, team_pos) } fmt.printf("%i. ", i+1) winner = event_teams_match(pair[0], pair[1], e.ruler, draw_allowed=false) } fmt.println() } event_do_knockout_bracket :: proc (e: ^Event, from, to: [] ^Event_Team) { assert(len(from) == 2 * len(to)) switch len(from) { case 16 : fmt.println("--- Round of 16 ---") case 8 : fmt.println("--- Quarter Final ---") case 4 : fmt.println("--- Semi Final ---") case 2 : fmt.println("--- Final ---") case : panic("Unexpected bracket size") } for &winner, i in to { fmt.printf("%i. ", i+1) winner = event_teams_match(from[i*2+0], from[i*2+1], e.ruler, draw_allowed=false) } fmt.println() } event_qualifiers_row :: proc (e: ^Event, input_qualifiers: Input_Qualifiers) -> [12] byte { q_key: [8] byte for &b, i in q_key do b = e.knockout.teams_32[i+24].group.name slice.sort(q_key[:]) q_key_str := string(q_key[:]) fmt.ensuref(q_key_str in input_qualifiers, "Missing qualifier key `%s`", q_key_str) q: [8] byte for s, i in input_qualifiers[q_key_str] do q[i] = s[0] // 1A 1B 1C 1D 1E 1F 1G 1H 1I 1J 1K 1L return { q[0], q[1], 0, q[2], q[3], 0, q[4], 0, q[5], 0, q[6], q[7] } } event_teams_match :: proc (team1, team2: ^Event_Team, ruler: Input_Match_Ruler, draw_allowed := true) -> (winner: ^Event_Team) { ranking_diff := team1.ranking - team2.ranking ranking_diff_abs := abs(ranking_diff) if ranking_diff_abs < ruler[0] && draw_allowed { fmt.printfln("%s drew with %s", team1.name, team2.name) team1.points += 1 team2.points += 1 return } goals: int beat_order: [2] ^Event_Team = ranking_diff > 0 ? { team1, team2 } : { team2, team1 } beat_order[0].points += 3 switch { case ranking_diff_abs < ruler[0]: fmt.printfln("%s beat %s on penalties", beat_order[0].name, beat_order[1].name) // ignore goals counter for penalties case ranking_diff_abs < ruler[1]: fmt.printfln("%s beat %s by 1 goal", beat_order[0].name, beat_order[1].name) goals = 1 case ranking_diff_abs < ruler[2]: fmt.printfln("%s beat %s by 2 goals", beat_order[0].name, beat_order[1].name) goals = 2 case: fmt.printfln("%s beat %s by 3 goals", beat_order[0].name, beat_order[1].name) goals = 3 } beat_order[0].goals += goals beat_order[1].goals -= goals return beat_order[0] } event_teams_sort :: proc (teams: [] ^Event_Team) { slice.sort_by(teams[:], less = proc (t1, t2: ^Event_Team) -> bool { switch { case t1.points != t2.points : return t1.points > t2.points case t1.goals != t2.goals : return t1.goals > t2.goals case : return t1.ranking > t2.ranking } }) } event_team_in_group :: proc (teams: [] ^Event_Team, group_name: byte, team_pos: int) -> ^Event_Team { Args :: struct { group_name: byte, team_pos: int } context.user_ptr = &Args { group_name, team_pos } i, ok := slice.linear_search_proc(teams, proc (t: ^Event_Team) -> bool { args := cast (^Args) context.user_ptr return t.group.name == args.group_name && event_team_group_pos(t) == args.team_pos }) assert(ok) return teams[i] } event_team_group_pos :: proc (team: ^Event_Team) -> int { i, ok := slice.linear_search(team.group.teams[:], team) assert(ok) return 1 + i }