Add region-based filtering and route length diversity
- Add RegionMapSelector UI for geographic trip filtering (East/Central/West) - Add RouteFilters module for allowRepeatCities preference - Improve GameDAGRouter to preserve route length diversity - Routes now grouped by city count before scoring - Ensures 2-city trips appear alongside longer trips - Increased beam width and max options for better coverage - Add TripOptionsView filters (max cities slider, pace filter) - Remove TravelStyle section from trip creation (replaced by region selector) - Clean up debug logging from DataProvider and ScenarioAPlanner 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,7 @@ struct PlanningFailure: Error {
|
||||
case travelSegmentMissing
|
||||
case constraintsUnsatisfiable
|
||||
case geographicBacktracking
|
||||
case repeatCityViolation(cities: [String])
|
||||
|
||||
static func == (lhs: FailureReason, rhs: FailureReason) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
@@ -50,6 +51,8 @@ struct PlanningFailure: Error {
|
||||
return true
|
||||
case (.dateRangeViolation(let g1), .dateRangeViolation(let g2)):
|
||||
return g1.map { $0.id } == g2.map { $0.id }
|
||||
case (.repeatCityViolation(let c1), .repeatCityViolation(let c2)):
|
||||
return c1 == c2
|
||||
default:
|
||||
return false
|
||||
}
|
||||
@@ -74,6 +77,10 @@ struct PlanningFailure: Error {
|
||||
case .travelSegmentMissing: return "Travel segment could not be created"
|
||||
case .constraintsUnsatisfiable: return "Cannot satisfy all trip constraints"
|
||||
case .geographicBacktracking: return "Route requires excessive backtracking"
|
||||
case .repeatCityViolation(let cities):
|
||||
let cityList = cities.prefix(3).joined(separator: ", ")
|
||||
let suffix = cities.count > 3 ? " and \(cities.count - 3) more" : ""
|
||||
return "Cannot visit cities on multiple days: \(cityList)\(suffix)"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,8 +189,7 @@ struct ItineraryOption: Identifiable {
|
||||
/// - Parameters:
|
||||
/// - options: The itinerary options to sort
|
||||
/// - leisureLevel: The user's leisure preference
|
||||
/// - limit: Maximum number of options to return (default 10)
|
||||
/// - Returns: Sorted and ranked options
|
||||
/// - Returns: Sorted and ranked options (all options, no limit)
|
||||
///
|
||||
/// Sorting behavior:
|
||||
/// - Packed: Most games first, then least driving
|
||||
@@ -191,8 +197,7 @@ struct ItineraryOption: Identifiable {
|
||||
/// - Relaxed: Least driving first, then fewer games
|
||||
static func sortByLeisure(
|
||||
_ options: [ItineraryOption],
|
||||
leisureLevel: LeisureLevel,
|
||||
limit: Int = 10
|
||||
leisureLevel: LeisureLevel
|
||||
) -> [ItineraryOption] {
|
||||
let sorted = options.sorted { a, b in
|
||||
let aGames = a.totalGames
|
||||
@@ -220,8 +225,8 @@ struct ItineraryOption: Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
// Re-rank after sorting
|
||||
return Array(sorted.prefix(limit)).enumerated().map { index, option in
|
||||
// Re-rank after sorting (no limit - return all options)
|
||||
return sorted.enumerated().map { index, option in
|
||||
ItineraryOption(
|
||||
rank: index + 1,
|
||||
stops: option.stops,
|
||||
|
||||
Reference in New Issue
Block a user