mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-14 13:22:03 +00:00
feat: derive authoritative ranked tiers
This commit is contained in:
@@ -65,6 +65,62 @@ type RankedProfile struct {
|
||||
SeasonHistory []string
|
||||
}
|
||||
|
||||
type RankTier string
|
||||
|
||||
const (
|
||||
RankTierProvisional RankTier = "PROVISIONAL"
|
||||
RankTierBronze RankTier = "BRONZE"
|
||||
RankTierSilver RankTier = "SILVER"
|
||||
RankTierGold RankTier = "GOLD"
|
||||
RankTierPlatinum RankTier = "PLATINUM"
|
||||
RankTierDiamond RankTier = "DIAMOND"
|
||||
)
|
||||
|
||||
// TierBand is backend configuration, not client input. Bands are evaluated in
|
||||
// ascending minimum-rating order and the highest matching band wins.
|
||||
type TierBand struct {
|
||||
Tier RankTier
|
||||
MinRating float64
|
||||
}
|
||||
|
||||
type TierPolicy struct {
|
||||
bands []TierBand
|
||||
}
|
||||
|
||||
func NewTierPolicy(bands []TierBand) (TierPolicy, error) {
|
||||
if len(bands) == 0 || bands[0].MinRating > 0 {
|
||||
return TierPolicy{}, fmt.Errorf("tier policy must start at or below zero")
|
||||
}
|
||||
copyBands := append([]TierBand(nil), bands...)
|
||||
for i, band := range copyBands {
|
||||
if band.Tier == "" || math.IsNaN(band.MinRating) || math.IsInf(band.MinRating, 0) || (i > 0 && band.MinRating <= copyBands[i-1].MinRating) {
|
||||
return TierPolicy{}, fmt.Errorf("tier bands must have unique ascending finite thresholds")
|
||||
}
|
||||
}
|
||||
return TierPolicy{bands: copyBands}, nil
|
||||
}
|
||||
|
||||
// RankedTier is the only tier derivation entry point. It deliberately accepts
|
||||
// RankedProfile rather than Rating, so a casual rating cannot be accidentally
|
||||
// exposed as a ranked tier. The caller serializes this result from the
|
||||
// authoritative backend response; clients do not reproduce these thresholds.
|
||||
func RankedTier(profile RankedProfile, policy TierPolicy) (RankTier, error) {
|
||||
if profile.RankedGames < 0 || len(policy.bands) == 0 || math.IsNaN(profile.Value) || math.IsInf(profile.Value, 0) {
|
||||
return "", fmt.Errorf("invalid ranked tier input")
|
||||
}
|
||||
if RankedIsProvisional(profile) {
|
||||
return RankTierProvisional, nil
|
||||
}
|
||||
tier := policy.bands[0].Tier
|
||||
for _, band := range policy.bands {
|
||||
if profile.Value < band.MinRating {
|
||||
break
|
||||
}
|
||||
tier = band.Tier
|
||||
}
|
||||
return tier, nil
|
||||
}
|
||||
|
||||
type RankedSeason struct {
|
||||
SeasonID string
|
||||
StartsAt time.Time
|
||||
|
||||
Reference in New Issue
Block a user