package logic import ( "sort" ) // CalculateStandings computes the stats for each team in a group given the match results. func CalculateStandings(teams []string, matches []Match) []*TeamStats { statsMap := make(map[string]*TeamStats) for _, t := range teams { statsMap[t] = &TeamStats{TeamName: t} } for _, m := range matches { if !m.IsCompleted { continue } sa, sb := statsMap[m.TeamA], statsMap[m.TeamB] if sa == nil || sb == nil { continue } sa.GoalsScored += m.ScoreA sa.GoalDifference += m.ScoreA - m.ScoreB sb.GoalsScored += m.ScoreB sb.GoalDifference += m.ScoreB - m.ScoreA if m.ScoreA > m.ScoreB { sa.Points += 3 } else if m.ScoreA < m.ScoreB { sb.Points += 3 } else { sa.Points += 1 sb.Points += 1 } } res := make([]*TeamStats, 0, len(teams)) for _, s := range statsMap { res = append(res, s) } // Sort the standings SortGroupStandings(res, matches) return res } // SortGroupStandings sorts the teams in a group based on the tie-breaking rules. func SortGroupStandings(teams []*TeamStats, matches []Match) { sort.Slice(teams, func(i, j int) bool { ti, tj := teams[i], teams[j] // 1. Points if ti.Points != tj.Points { return ti.Points > tj.Points } // 2. Goal Difference if ti.GoalDifference != tj.GoalDifference { return ti.GoalDifference > tj.GoalDifference } // 3. Goals Scored if ti.GoalsScored != tj.GoalsScored { return ti.GoalsScored > tj.GoalsScored } // 4-6. Head-to-Head (H2H) // We dynamically compute H2H stats for all teams that are tied on Points, GD, and GS. // Since we are sorting, we can check the relationship between ti and tj. // First find all teams tied with ti and tj. tiedTeams := []string{} for _, t := range teams { if t.Points == ti.Points && t.GoalDifference == ti.GoalDifference && t.GoalsScored == ti.GoalsScored { tiedTeams = append(tiedTeams, t.TeamName) } } if len(tiedTeams) > 1 { h2hStats := computeH2H(tiedTeams, matches) h2hi := h2hStats[ti.TeamName] h2hj := h2hStats[tj.TeamName] if h2hi != nil && h2hj != nil { // H2H Points if h2hi.Points != h2hj.Points { return h2hi.Points > h2hj.Points } // H2H GD if h2hi.GoalDifference != h2hj.GoalDifference { return h2hi.GoalDifference > h2hj.GoalDifference } // H2H GS if h2hi.GoalsScored != h2hj.GoalsScored { return h2hi.GoalsScored > h2hj.GoalsScored } } } // 7. No fallback (retains the shuffled input order to randomize tie-breaks in Monte Carlo) return false }) } // computeH2H calculates Points, GD, and GS only considering matches between the specified tied teams. func computeH2H(tiedTeams []string, matches []Match) map[string]*TeamStats { statsMap := make(map[string]*TeamStats) isTied := make(map[string]bool) for _, t := range tiedTeams { statsMap[t] = &TeamStats{TeamName: t} isTied[t] = true } for _, m := range matches { if !m.IsCompleted { continue } if isTied[m.TeamA] && isTied[m.TeamB] { sa, sb := statsMap[m.TeamA], statsMap[m.TeamB] sa.GoalsScored += m.ScoreA sa.GoalDifference += m.ScoreA - m.ScoreB sb.GoalsScored += m.ScoreB sb.GoalDifference += m.ScoreB - m.ScoreA if m.ScoreA > m.ScoreB { sa.Points += 3 } else if m.ScoreA < m.ScoreB { sb.Points += 3 } else { sa.Points += 1 sb.Points += 1 } } } return statsMap } // CompareThirdPlaced ranks the third-placed teams across groups. func CompareThirdPlaced(thirdTeams []*TeamStats) { sort.Slice(thirdTeams, func(i, j int) bool { ti, tj := thirdTeams[i], thirdTeams[j] // 1. Points if ti.Points != tj.Points { return ti.Points > tj.Points } // 2. Goal Difference if ti.GoalDifference != tj.GoalDifference { return ti.GoalDifference > tj.GoalDifference } // 3. Goals Scored if ti.GoalsScored != tj.GoalsScored { return ti.GoalsScored > tj.GoalsScored } // 4. No fallback (retains the shuffled input order to randomize tie-breaks in Monte Carlo) return false }) }