Refactor trip planning: DAG router + trip options UI + simplified itinerary
- Replace O(2^n) GeographicRouteExplorer with O(n) GameDAGRouter using DAG + beam search - Add geographic diversity to route selection (returns routes from distinct regions) - Add trip options selector UI (TripOptionsView, TripOptionCard) to choose between routes - Simplify itinerary display: separate games and travel segments by date - Remove complex ItineraryDay bundling, query games/travel directly per day - Update ScenarioA/B/C planners to use GameDAGRouter - Add new test suites for planners and travel estimator 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -385,52 +385,7 @@ struct DuplicateGameIdTests {
|
||||
)
|
||||
}
|
||||
|
||||
@Test("GameCandidate array with duplicate game IDs can build dictionary without crashing")
|
||||
func candidateMap_HandlesDuplicateGameIds() {
|
||||
// This test reproduces the bug: Dictionary(uniqueKeysWithValues:) crashes on duplicate keys
|
||||
// Fix: Use reduce(into:) to handle duplicates gracefully
|
||||
|
||||
let stadium = makeStadium()
|
||||
let homeTeam = makeTeam(stadiumId: stadium.id)
|
||||
let awayTeam = makeTeam(stadiumId: UUID())
|
||||
let gameId = UUID() // Same ID for both candidates (simulates duplicate in JSON)
|
||||
let dateTime = Date()
|
||||
|
||||
let game = makeGame(id: gameId, homeTeamId: homeTeam.id, awayTeamId: awayTeam.id, stadiumId: stadium.id, dateTime: dateTime)
|
||||
|
||||
// Create two candidates with the same game ID (simulating duplicate JSON data)
|
||||
let candidate1 = GameCandidate(
|
||||
id: gameId,
|
||||
game: game,
|
||||
stadium: stadium,
|
||||
homeTeam: homeTeam,
|
||||
awayTeam: awayTeam,
|
||||
detourDistance: 0,
|
||||
score: 1.0
|
||||
)
|
||||
let candidate2 = GameCandidate(
|
||||
id: gameId,
|
||||
game: game,
|
||||
stadium: stadium,
|
||||
homeTeam: homeTeam,
|
||||
awayTeam: awayTeam,
|
||||
detourDistance: 0,
|
||||
score: 2.0
|
||||
)
|
||||
|
||||
let candidates = [candidate1, candidate2]
|
||||
|
||||
// This is the fix pattern - should not crash
|
||||
let candidateMap = candidates.reduce(into: [UUID: GameCandidate]()) { dict, candidate in
|
||||
if dict[candidate.game.id] == nil {
|
||||
dict[candidate.game.id] = candidate
|
||||
}
|
||||
}
|
||||
|
||||
// Should only have one entry (first one wins)
|
||||
#expect(candidateMap.count == 1)
|
||||
#expect(candidateMap[gameId]?.score == 1.0, "First candidate should be kept")
|
||||
}
|
||||
// Note: GameCandidate test removed - type no longer exists after planning engine refactor
|
||||
|
||||
@Test("Duplicate games are deduplicated at load time")
|
||||
func gamesArray_DeduplicatesById() {
|
||||
|
||||
Reference in New Issue
Block a user