Files
evening_detective_server/internal/services/game_service/results.go
T
2026-09-02 23:35:15 +07:00

94 lines
3.7 KiB
Go

package game_service
import (
"sort"
"evening_detective_server/internal/modules/storytelling"
)
// TeamResult — строка итоговой таблицы: команда, баллы по вопросам (в порядке
// сценария), сумма баллов и место.
type TeamResult struct {
Team *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 []*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
}