fix: 12 planning engine bugs + App Store preview export at 886x1920
Planning engine fixes (from adversarial code review): - Bug #1: sortByLeisure tie-breaking uses totalDrivingHours - Bug #2: allDates/calculateRestDays guard-let-break prevents infinite loop - Bug #3: same-day trip no longer rejected (>= in dateRange guard) - Bug #4: ScenarioD rationale shows game count not stop count - Bug #5: ScenarioD departureDate advanced to next day after last game - Bug #6: ScenarioC date range boundary uses <= instead of < - Bug #7: DrivingConstraints clamps maxHoursPerDriverPerDay via max(1.0,...) - Bug #8: effectiveTripDuration uses inclusive day counting (+1) - Bug #9: TripWizardViewModel validates endDate >= startDate - Bug #10: allDates() uses min/max instead of first/last for robustness - Bug #12: arrivalBeforeGameStart accounts for game end time at departure - Bug #15: ScenarioBPlanner replaces force unwraps with safe unwrapping Tests: 16 regression test suites + updated existing test expectations Marketing: Remotion canvas set to 886x1920 for App Store preview spec Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -317,7 +317,7 @@ struct DrivingConstraints {
|
||||
|
||||
init(from preferences: TripPreferences) {
|
||||
self.numberOfDrivers = max(1, preferences.numberOfDrivers)
|
||||
self.maxHoursPerDriverPerDay = preferences.maxDrivingHoursPerDriver ?? 8.0
|
||||
self.maxHoursPerDriverPerDay = max(1.0, preferences.maxDrivingHoursPerDriver ?? 8.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,7 +472,8 @@ extension ItineraryOption {
|
||||
)
|
||||
restDays.append(restDay)
|
||||
}
|
||||
currentDay = calendar.date(byAdding: .day, value: 1, to: currentDay) ?? currentDay
|
||||
guard let nextDay = calendar.date(byAdding: .day, value: 1, to: currentDay) else { break }
|
||||
currentDay = nextDay
|
||||
}
|
||||
|
||||
return restDays
|
||||
@@ -497,16 +498,17 @@ extension ItineraryOption {
|
||||
/// All dates covered by the itinerary.
|
||||
func allDates() -> [Date] {
|
||||
let calendar = Calendar.current
|
||||
guard let firstStop = stops.first,
|
||||
let lastStop = stops.last else { return [] }
|
||||
guard let earliestArrival = stops.map(\.arrivalDate).min(),
|
||||
let latestDeparture = stops.map(\.departureDate).max() else { return [] }
|
||||
|
||||
var dates: [Date] = []
|
||||
var currentDate = calendar.startOfDay(for: firstStop.arrivalDate)
|
||||
let endDate = calendar.startOfDay(for: lastStop.departureDate)
|
||||
var currentDate = calendar.startOfDay(for: earliestArrival)
|
||||
let endDate = calendar.startOfDay(for: latestDeparture)
|
||||
|
||||
while currentDate <= endDate {
|
||||
dates.append(currentDate)
|
||||
currentDate = calendar.date(byAdding: .day, value: 1, to: currentDate) ?? currentDate
|
||||
guard let nextDate = calendar.date(byAdding: .day, value: 1, to: currentDate) else { break }
|
||||
currentDate = nextDate
|
||||
}
|
||||
|
||||
return dates
|
||||
@@ -548,7 +550,7 @@ struct PlanningRequest {
|
||||
/// Note: End date is extended to end-of-day to include all games on the last day,
|
||||
/// since DateInterval.contains() uses exclusive end boundary.
|
||||
var dateRange: DateInterval? {
|
||||
guard preferences.endDate > preferences.startDate else { return nil }
|
||||
guard preferences.endDate >= preferences.startDate else { return nil }
|
||||
// Extend end date to end of day (23:59:59) to include games on the last day
|
||||
let calendar = Calendar.current
|
||||
let endOfDay = calendar.date(bySettingHour: 23, minute: 59, second: 59, of: preferences.endDate)
|
||||
|
||||
Reference in New Issue
Block a user