mirror of
https://github.com/AmanTahiliani/box-box.git
synced 2026-10-06 18:51:56 -04:00
fix(chapters): sync race-story titles after conflict trim
resolveConflicts already clips a still-open safety car so finish can keep the last laps, but Title and EndTime stayed on the pre-trim range. Rebuild range-bearing titles from the final laps and point EndTime at the trimmed lap-index end. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -72,7 +72,7 @@ func Detect(rc []RaceControl, positions []PositionSample, laps []Lap, totalLaps
|
||||
chapters = append(chapters, detectDecisiveSwings(positions, lapIndex, totalLaps)...)
|
||||
chapters = append(chapters, detectFinish(rc, lapIndex, totalLaps))
|
||||
|
||||
return resolveConflicts(chapters)
|
||||
return resolveConflicts(chapters, lapIndex)
|
||||
}
|
||||
|
||||
func normalizeTotalLaps(totalLaps int, laps []Lap, rc []RaceControl) int {
|
||||
@@ -321,7 +321,7 @@ func detectPitPhases(laps []Lap, idx lapIndex) []Chapter {
|
||||
}
|
||||
ch := Chapter{
|
||||
Kind: KindPitPhase,
|
||||
Title: fmt.Sprintf("Pit phase (L%d-L%d)", start, end),
|
||||
Title: pitPhaseTitle(start, end),
|
||||
StartLap: start,
|
||||
EndLap: end,
|
||||
StartTime: idx.lapStart(start),
|
||||
@@ -339,7 +339,7 @@ func detectPitPhases(laps []Lap, idx lapIndex) []Chapter {
|
||||
drivers[driver] = true
|
||||
}
|
||||
last.DriverNumbers = sortedDriverNumbers(drivers)
|
||||
last.Title = fmt.Sprintf("Pit phase (L%d-L%d)", last.StartLap, last.EndLap)
|
||||
last.Title = pitPhaseTitle(last.StartLap, last.EndLap)
|
||||
continue
|
||||
}
|
||||
windows = append(windows, ch)
|
||||
@@ -479,7 +479,7 @@ func detectFinish(rc []RaceControl, idx lapIndex, totalLaps int) Chapter {
|
||||
}
|
||||
return Chapter{
|
||||
Kind: KindFinish,
|
||||
Title: fmt.Sprintf("Finish (L%d-L%d)", startLap, finishLap),
|
||||
Title: finishTitle(startLap, finishLap),
|
||||
StartLap: startLap,
|
||||
EndLap: finishLap,
|
||||
StartTime: idx.lapStart(startLap),
|
||||
@@ -487,7 +487,31 @@ func detectFinish(rc []RaceControl, idx lapIndex, totalLaps int) Chapter {
|
||||
}
|
||||
}
|
||||
|
||||
func resolveConflicts(chapters []Chapter) []Chapter {
|
||||
func pitPhaseTitle(startLap, endLap int) string {
|
||||
return fmt.Sprintf("Pit phase (L%d-L%d)", startLap, endLap)
|
||||
}
|
||||
|
||||
func finishTitle(startLap, endLap int) string {
|
||||
return fmt.Sprintf("Finish (L%d-L%d)", startLap, endLap)
|
||||
}
|
||||
|
||||
// titleForResolved rebuilds range-bearing titles from the final StartLap/EndLap
|
||||
// after conflict resolution. Flag and pit-phase formatters are reused so stale
|
||||
// pre-trim strings are never parsed.
|
||||
func titleForResolved(ch Chapter) string {
|
||||
switch ch.Kind {
|
||||
case KindSafetyCar, KindVirtualSafetyCar, KindRedFlag:
|
||||
return flagTitle(ch.Kind, ch.StartLap, ch.EndLap)
|
||||
case KindPitPhase:
|
||||
return pitPhaseTitle(ch.StartLap, ch.EndLap)
|
||||
case KindFinish:
|
||||
return finishTitle(ch.StartLap, ch.EndLap)
|
||||
default:
|
||||
return ch.Title
|
||||
}
|
||||
}
|
||||
|
||||
func resolveConflicts(chapters []Chapter, idx lapIndex) []Chapter {
|
||||
normalized := make([]Chapter, 0, len(chapters))
|
||||
for _, ch := range chapters {
|
||||
if ch.StartLap <= 0 {
|
||||
@@ -528,6 +552,7 @@ func resolveConflicts(chapters []Chapter) []Chapter {
|
||||
if priority(ch.Kind) > priority(last.Kind) {
|
||||
if last.StartLap < ch.StartLap {
|
||||
last.EndLap = ch.StartLap - 1
|
||||
last.EndTime = idx.lapEnd(last.EndLap)
|
||||
out = append(out, ch)
|
||||
} else {
|
||||
*last = ch
|
||||
@@ -541,6 +566,9 @@ func resolveConflicts(chapters []Chapter) []Chapter {
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range out {
|
||||
out[i].Title = titleForResolved(out[i])
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
|
||||
@@ -125,6 +125,42 @@ func TestDetectDecisiveSwingPersistsToFinish(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectSafetyCarTrimmedForFinishRewritesTitle(t *testing.T) {
|
||||
rcs := []RaceControl{
|
||||
rc(3, models.CategorySafetyCar, "", "SAFETY CAR DEPLOYED"),
|
||||
rc(72, models.CategoryFlag, models.FlagChequered, "CHEQUERED FLAG"),
|
||||
}
|
||||
|
||||
got := Detect(rcs, nil, testLaps(72, nil), 72)
|
||||
|
||||
sc := findKind(got, KindSafetyCar)
|
||||
if sc == nil {
|
||||
t.Fatalf("chapters = %+v, want safety car", got)
|
||||
}
|
||||
if sc.StartLap != 3 || sc.EndLap != 70 || sc.Title != "Safety Car (L3-L70)" {
|
||||
t.Fatalf("safety car = %+v, want StartLap=3 EndLap=70 Title=%q", *sc, "Safety Car (L3-L70)")
|
||||
}
|
||||
if wantEnd := lapTime(71); sc.EndTime != wantEnd {
|
||||
t.Fatalf("safety car EndTime = %q, want lap-index end of L70 (%q)", sc.EndTime, wantEnd)
|
||||
}
|
||||
|
||||
fin := findKind(got, KindFinish)
|
||||
if fin == nil || fin.StartLap != 71 || fin.EndLap != 72 {
|
||||
t.Fatalf("finish = %+v, want L71-L72", got)
|
||||
}
|
||||
|
||||
for i := 1; i < len(got); i++ {
|
||||
if got[i].StartLap <= got[i-1].EndLap {
|
||||
t.Fatalf("chapters overlap at %d: %+v then %+v", i, got[i-1], got[i])
|
||||
}
|
||||
}
|
||||
|
||||
headed := ApplyHeadlines(got, nil, rcs, 0)
|
||||
if scH := findKind(headed, KindSafetyCar); scH == nil || scH.Headline != "Safety Car (L3-L70)" {
|
||||
t.Fatalf("headline = %+v, want fallback to resolved title Safety Car (L3-L70)", headed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectFlagPeriodsWinOverConflictingChapters(t *testing.T) {
|
||||
pitOuts := map[int][]int{
|
||||
12: {1, 2},
|
||||
|
||||
Reference in New Issue
Block a user