diff --git a/CHANGELOG.md b/CHANGELOG.md index cd44f7bc..d8b97c7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - New game type `170` - Endpoint for getting badge statistics - Added Darts Per Leg `DPL` metric to tournament +- Count badges for all leg types - Lots of new badges #### Changed diff --git a/data/badge.go b/data/badge.go index 582f3c20..fea34d13 100644 --- a/data/badge.go +++ b/data/badge.go @@ -17,6 +17,8 @@ func GetBadges() ([]*models.Badge, error) { b.name, b.description, b.filename, + b.secret, + b.hidden, b.levels FROM badge b`) if err != nil { @@ -27,7 +29,7 @@ func GetBadges() ([]*models.Badge, error) { badges := make([]*models.Badge, 0) for rows.Next() { badge := new(models.Badge) - err := rows.Scan(&badge.ID, &badge.Name, &badge.Description, &badge.Filename, &badge.Levels) + err := rows.Scan(&badge.ID, &badge.Name, &badge.Description, &badge.Filename, &badge.Secret, &badge.Hidden, &badge.Levels) if err != nil { return nil, err } @@ -239,6 +241,7 @@ func CheckLegForBadges(leg *models.Leg, statistics map[int]*models.PlayerBadgeSt } } } + for _, badge := range models.VisitBadges { for _, playerID := range leg.Players { valid, visitID := badge.Validate(playerID, leg.Visits) diff --git a/data/leg.go b/data/leg.go index ae380dd6..368d07ab 100644 --- a/data/leg.go +++ b/data/leg.go @@ -949,7 +949,7 @@ func GetBadgeLegsToRecalculate() ([]int, error) { SELECT l.id FROM leg l JOIN matches m on m.id = l.match_id - WHERE l.has_scores = 1 AND COALESCE(l.leg_type_id, m.match_type_id) = 1 -- X01 + WHERE l.has_scores = 1 AND m.is_abandoned = 0 AND m.is_bye = 0 AND m.is_walkover = 0 AND l.is_finished = 1 GROUP BY l.id diff --git a/data/statistics_x01.go b/data/statistics_x01.go index b65d9386..7246f910 100644 --- a/data/statistics_x01.go +++ b/data/statistics_x01.go @@ -933,7 +933,6 @@ func GetPlayerBadgeStatistics(ids []int, legID *int) (map[int]*models.PlayerBadg LEFT JOIN matches m ON l.match_id = m.id WHERE player_id IN (?) AND l.is_finished = 1 AND m.is_abandoned = 0 AND m.is_walkover = 0 - AND COALESCE(l.leg_type_id, m.match_type_id) = 1 -- X01 AND m.is_bye = 0 AND m.is_walkover = 0 AND leg_id <= COALESCE(?, ~0) -- BIGINT hack GROUP BY player_id ORDER BY player_id DESC`, ids, legID) @@ -979,7 +978,6 @@ func GetPlayerBadgeStatistics(ids []int, legID *int) (map[int]*models.PlayerBadg or (first_dart_multiplier = 2 and second_dart_multiplier = 1 and third_dart_multiplier = 3)) AND is_bust = 0 AND s.player_id IN (?) AND leg_id <= COALESCE(?, ~0) -- BIGINT hack AND l.is_finished = 1 AND m.is_abandoned = 0 AND m.is_walkover = 0 - AND COALESCE(l.leg_type_id, m.match_type_id) = 1 -- X01 AND m.is_bye = 0 AND m.is_walkover = 0 GROUP BY first_dart, player_id`, ids, legID) if err != nil { diff --git a/models/badge.go b/models/badge.go index 9807f861..e45b0c7d 100644 --- a/models/badge.go +++ b/models/badge.go @@ -2,6 +2,7 @@ package models import ( "encoding/json" + "log" "sort" "time" @@ -216,6 +217,9 @@ func (b BadgeJustAQuickie) GetID() int { return b.ID } func (b BadgeJustAQuickie) Validate(match *Match) (bool, []int) { + if !match.IsX01() { + return false, nil + } if len(match.Legs) == 3 && len(match.Players) > 1 { first := match.Legs[0] second := match.Legs[1] @@ -226,6 +230,7 @@ func (b BadgeJustAQuickie) Validate(match *Match) (bool, []int) { return true, []int{int(match.WinnerID.Int64)} } } + return false, nil } @@ -233,6 +238,10 @@ func (b BadgeAroundTheWorld) GetID() int { return b.ID } func (b BadgeAroundTheWorld) Validate(match *Match) (bool, []int) { + if !match.IsX01() { + return false, nil + } + playerHits := make(map[int][]int) for playerID := range match.Players { playerHits[playerID] = make([]int, 0) @@ -308,6 +317,7 @@ var LegBadges = []LegBadge{ BadgeChampagneShot{ID: 42}, BadgeYin{ID: 44}, BadgeYang{ID: 45}, + BadgeZebra{ID: 47}, } type LegBadge interface { @@ -335,11 +345,15 @@ type BadgePerfection struct{ ID int } type BadgeChampagneShot struct{ ID int } type BadgeYin struct{ ID int } type BadgeYang struct{ ID int } +type BadgeZebra struct{ ID int } func (b BadgeDoubleDouble) GetID() int { return b.ID } func (b BadgeDoubleDouble) Validate(leg *Leg) (bool, *int, *int) { + if !leg.IsX01() { + return false, nil, nil + } visit := leg.GetLastVisit() doubles := 0 if visit.ThirdDart.IsDouble() { @@ -358,6 +372,9 @@ func (b BadgeTripleDouble) GetID() int { return b.ID } func (b BadgeTripleDouble) Validate(leg *Leg) (bool, *int, *int) { + if !leg.IsX01() { + return false, nil, nil + } visit := leg.GetLastVisit() return visit.FirstDart.IsDouble() && visit.SecondDart.IsDouble() && visit.ThirdDart.IsDouble(), &visit.PlayerID, &visit.ID } @@ -366,6 +383,9 @@ func (b BadgeMadHouse) GetID() int { return b.ID } func (b BadgeMadHouse) Validate(leg *Leg) (bool, *int, *int) { + if !leg.IsX01() { + return false, nil, nil + } visit := leg.GetLastVisit() last := visit.GetLastDart() return last.IsDouble() && last.ValueRaw() == 1, &visit.PlayerID, &visit.ID @@ -408,6 +428,9 @@ func (b BadgeBullseye) GetID() int { return b.ID } func (b BadgeBullseye) Validate(leg *Leg) (bool, *int, *int) { + if !leg.IsX01() { + return false, nil, nil + } visit := leg.GetLastVisit() last := visit.GetLastDart() return last.ValueRaw() == BULLSEYE && last.Multiplier == DOUBLE, &visit.PlayerID, &visit.ID @@ -417,6 +440,9 @@ func (b BadgeEasyAs123) GetID() int { return b.ID } func (b BadgeEasyAs123) Validate(leg *Leg) (bool, *int, *int) { + if !leg.IsX01() { + return false, nil, nil + } visit := leg.GetLastVisit() last := visit.GetLastDart() return visit.GetScore() == 123 && last.IsDouble(), &visit.PlayerID, &visit.ID @@ -426,6 +452,9 @@ func (b BadgeCloseToPerfect) GetID() int { return b.ID } func (b BadgeCloseToPerfect) Validate(leg *Leg) (bool, *int, *int) { + if !leg.IsX01() { + return false, nil, nil + } visit := leg.GetLastVisit() return leg.StartingScore == 501 && visit.DartsThrown < 15 && visit.DartsThrown > 9, &visit.PlayerID, nil } @@ -445,6 +474,9 @@ func (b BadgeShanghaiCheckout) GetID() int { return b.ID } func (b BadgeShanghaiCheckout) Validate(leg *Leg) (bool, *int, *int) { + if !leg.IsX01() { + return false, nil, nil + } visit := leg.GetLastVisit() return visit.IsShanghai(), &visit.PlayerID, &visit.ID } @@ -453,8 +485,11 @@ func (b BadgeTripleTrouble) GetID() int { return b.ID } func (b BadgeTripleTrouble) Validate(leg *Leg) (bool, *int, *int) { - visit := leg.GetLastVisit() + if !leg.IsX01() { + return false, nil, nil + } + visit := leg.GetLastVisit() if visit.FirstDart.IsDouble() && visit.SecondDart.IsDouble() && visit.ThirdDart.IsDouble() && visit.FirstDart.ValueRaw() == visit.SecondDart.ValueRaw() && visit.FirstDart.ValueRaw() == visit.ThirdDart.ValueRaw() { return true, &visit.PlayerID, &visit.ID @@ -467,6 +502,10 @@ func (b BadgePerfection) GetID() int { return b.ID } func (b BadgePerfection) Validate(leg *Leg) (bool, *int, *int) { + if !leg.IsX01() { + return false, nil, nil + } + visit := leg.GetLastVisit() if leg.StartingScore == 501 && visit.DartsThrown == 9 { return true, &visit.PlayerID, nil @@ -478,6 +517,10 @@ func (b BadgeChampagneShot) GetID() int { return b.ID } func (b BadgeChampagneShot) Validate(leg *Leg) (bool, *int, *int) { + if !leg.IsX01() { + return false, nil, nil + } + visit := leg.GetLastVisit() if visit.FirstDart.IsBull() && visit.FirstDart.IsDouble() && @@ -493,7 +536,9 @@ func (b BadgeYin) GetID() int { return b.ID } func (b BadgeYin) Validate(leg *Leg) (bool, *int, *int) { - black := []int{0, 20, 18, 13, 10, 2, 3, 7, 8, 14, 12} + if !leg.IsX01() { + return false, nil, nil + } completed := true winner := leg.GetLastVisit().PlayerID @@ -501,9 +546,9 @@ func (b BadgeYin) Validate(leg *Leg) (bool, *int, *int) { if visit.PlayerID != winner { continue } - if !containsInt(black, visit.FirstDart.ValueRaw()) || - !containsInt(black, visit.SecondDart.ValueRaw()) || - !containsInt(black, visit.ThirdDart.ValueRaw()) { + if !containsInt(NUMS_BLACK, visit.FirstDart.ValueRaw()) || + !containsInt(NUMS_BLACK, visit.SecondDart.ValueRaw()) || + !containsInt(NUMS_BLACK, visit.ThirdDart.ValueRaw()) { completed = false break @@ -519,16 +564,19 @@ func (b BadgeYang) GetID() int { return b.ID } func (b BadgeYang) Validate(leg *Leg) (bool, *int, *int) { - white := []int{0, 1, 4, 6, 15, 17, 19, 16, 11, 9, 5} + if !leg.IsX01() { + return false, nil, nil + } + completed := true winner := leg.GetLastVisit().PlayerID for _, visit := range leg.Visits { if visit.PlayerID != winner { continue } - if !containsInt(white, visit.FirstDart.ValueRaw()) || - !containsInt(white, visit.SecondDart.ValueRaw()) || - !containsInt(white, visit.ThirdDart.ValueRaw()) { + if !containsInt(NUMS_WHITE, visit.FirstDart.ValueRaw()) || + !containsInt(NUMS_WHITE, visit.SecondDart.ValueRaw()) || + !containsInt(NUMS_WHITE, visit.ThirdDart.ValueRaw()) { completed = false break @@ -540,6 +588,57 @@ func (b BadgeYang) Validate(leg *Leg) (bool, *int, *int) { return false, nil, nil } +func (b BadgeZebra) GetID() int { + return b.ID +} +func (b BadgeZebra) Validate(leg *Leg) (bool, *int, *int) { + if !leg.IsX01() { + return false, nil, nil + } + winner := leg.GetLastVisit().PlayerID + + COLORS := map[int][]int{ + 0: NUMS_BLACK, + 1: NUMS_WHITE, + } + completed := true + + first := leg.GetFirstHitDart(winner) + var color int + if containsInt(COLORS[0], first.ValueRaw()) { + color = 0 + } else { + color = 1 + } + for _, visit := range leg.Visits { + if visit.PlayerID != winner { + continue + } + + for _, dart := range visit.GetDarts() { + if dart.IsMiss() { + log.Printf("Skipping Missed dart") + continue + } + colors := COLORS[color%2] + log.Printf("Checking if %d is %d in %v", dart.ValueRaw(), color, colors) + if !containsInt(colors, dart.ValueRaw()) { + completed = false + break + } + color++ + } + + if !completed { + break + } + } + if completed { + return true, &winner, nil + } + return false, nil, nil +} + var LegPlayerBadges = []LegPlayerBadge{ BadgeImpersonator{ID: 21}, BadgeBotBeaterEasy{ID: 22}, @@ -838,7 +937,7 @@ func (b BadgeSoClose) Validate(playerID int, visits []*Visit) (bool, *int) { func GetLevel(value int, levels []int) int { level := 1 for i, treshold := range levels { - if value > treshold { + if value >= treshold { level = i + 1 } } diff --git a/models/leg.go b/models/leg.go index a175527d..b0a2c393 100644 --- a/models/leg.go +++ b/models/leg.go @@ -411,3 +411,23 @@ func DecorateVisitsScam(players map[int]*Player2Leg, visits []*Visit) { func (leg Leg) GetLastVisit() *Visit { return leg.Visits[len(leg.Visits)-1] } + +// IsX01 returns true if this leg is a X01 leg +func (leg Leg) IsX01() bool { + return leg.LegType.ID == X01 +} + +// GetFirstHitDart will return the first (non-Miss) dart for the given player +func (leg Leg) GetFirstHitDart(playerID int) *Dart { + for _, visit := range leg.Visits { + if visit.PlayerID != playerID { + continue + } + for _, dart := range visit.GetDarts() { + if !dart.IsMiss() { + return &dart + } + } + } + return nil +} diff --git a/models/match.go b/models/match.go index 3963c7ef..1cb2219e 100644 --- a/models/match.go +++ b/models/match.go @@ -329,3 +329,8 @@ type MatchConfigError struct { func (e *MatchConfigError) Error() string { return e.Err.Error() } + +// IsX01 returns true if this match is a X01 match +func (m Match) IsX01() bool { + return m.MatchType.ID == X01 +} diff --git a/models/visit.go b/models/visit.go index 1b48e02c..acb81cbb 100644 --- a/models/visit.go +++ b/models/visit.go @@ -9,6 +9,9 @@ import ( "github.com/guregu/null" ) +var NUMS_WHITE = []int{0, 1, 4, 6, 15, 17, 19, 16, 11, 9, 5} +var NUMS_BLACK = []int{0, 20, 18, 13, 10, 2, 3, 7, 8, 14, 12} + // Visit struct used for storing legs type Visit struct { ID int `json:"id"`