diff options
Diffstat (limited to 'ride/climb.go')
| -rw-r--r-- | ride/climb.go | 43 |
1 files changed, 41 insertions, 2 deletions
diff --git a/ride/climb.go b/ride/climb.go index 29a4dcf..88b9d3b 100644 --- a/ride/climb.go +++ b/ride/climb.go @@ -221,8 +221,47 @@ func climbsBetween(r Ride, start, end int) []Climb { return climbs } -func (ride *Ride) AllClimbs() []Climb { - climbs := climbsBetween(*ride, 0, ride.Len()-1) +type Range struct { + Start int + End int +} + +func isValidClaim(claim Range, routeLen int) bool { + return claim.Start >= 0 && claim.End < routeLen && claim.Start < claim.End +} + +// freeRegions returns the maximal sub-ranges of [rStart, rEnd] that are not +// covered by any claimed range, after discarding claims that fall outside. +func freeRegions(rStart, rEnd int, claimed []Range) []Range { + regions := make([]Range, 0, len(claimed)+1) + current := rStart + for _, claim := range claimed { + if !isValidClaim(claim, rEnd+1) { + continue + } + if claim.End < current { + continue + } + if claim.Start > current { + regions = append(regions, Range{Start: current, End: claim.Start - 1}) + } + if claim.End+1 > current { + current = claim.End + 1 + } + } + if current <= rEnd { + regions = append(regions, Range{Start: current, End: rEnd}) + } + return regions +} + +// returns the detected climbs of the whole ride, but never a climb that overlaps +// a claimed range. +func (ride *Ride) DetectClimbs(claimed []Range) []Climb { + climbs := make([]Climb, 0) + for _, region := range freeRegions(0, ride.Len()-1, claimed) { + climbs = append(climbs, climbsBetween(*ride, region.Start, region.End)...) + } slices.SortFunc(climbs, climbCmpStart) return climbs } |
