package answer_service import ( "evening_detective_server/internal/modules/game_model" "sort" "evening_detective_server/internal/modules/storytelling" ) // TeamResult — строка итоговой таблицы: команда, баллы по вопросам (в порядке // сценария), сумма баллов и место. type TeamResult struct { Team *game_model.Team Answers []AnswerScore // TotalScore — сумма баллов по всем вопросам сценария; неоценённые // вопросы считаются как 0. TotalScore int // Place — место в итоговой таблице. Команды с одинаковой суммой баллов // и одинаковым числом ходов занимают одно место (следующее место // пропускается); проставляется функцией rankTeams. Place int } // GameResults — итоговая таблица игры: вопросы сценария (шапка таблицы) и // результаты команд, отсортированные по сумме баллов. type GameResults struct { Questions []*storytelling.Question Teams []TeamResult } // buildTeamResults собирает строки итоговой таблицы по командам: баллы в // порядке вопросов сценария (неотвеченный/неоценённый вопрос — с пустым // текстом и Score=nil), сумма баллов (nil считается как 0). Команды в // результате — в порядке переданного списка; сортировку и места делает // rankTeams. func buildTeamResults( questions []*storytelling.Question, teams []*game_model.Team, answerByTeamAndCode map[int]map[string]AnswerScore, ) []TeamResult { res := make([]TeamResult, 0, len(teams)) for _, team := range teams { byCode := answerByTeamAndCode[team.ID] answers := make([]AnswerScore, 0, len(questions)) total := 0 for _, question := range questions { answer, ok := byCode[question.Code] if !ok { answers = append(answers, AnswerScore{QuestionCode: question.Code}) continue } answers = append(answers, answer) if answer.Score != nil { total += *answer.Score } } res = append(res, TeamResult{ Team: team, Answers: answers, TotalScore: total, }) } return res } // rankTeams сортирует строки итоговой таблицы по сумме баллов (убывание); // при равенстве суммы выше команда с меньшим числом ходов. Команды с // одинаковой суммой баллов И одинаковым числом ходов занимают одно место // (следующее место пропускается — схема 1,2,2,4 по ключу (сумма, ходы)). // id команды влияет только на порядок показа внутри группы (меньший id // первым), не на место. Входной срез не изменяется. func rankTeams(rows []TeamResult) []TeamResult { sorted := make([]TeamResult, len(rows)) copy(sorted, rows) sort.SliceStable(sorted, func(i, j int) bool { a, b := sorted[i], sorted[j] if a.TotalScore != b.TotalScore { return a.TotalScore > b.TotalScore } if a.Team.ActionsCount != b.Team.ActionsCount { return a.Team.ActionsCount < b.Team.ActionsCount } return a.Team.ID < b.Team.ID }) place := 0 for i := range sorted { row := sorted[i] if i == 0 || row.TotalScore != sorted[i-1].TotalScore || row.Team.ActionsCount != sorted[i-1].Team.ActionsCount { place = i + 1 } sorted[i].Place = place } return sorted }