do dataFileName <- getDataFileName
text <- readFile dataFileName
let track = mkTrack text
- print track
+ -- print track
let costsFromStart = costsFrom track (M.singleton track.start 0) (M.singleton track.start 0)
let costsFromGoal = costsFrom track (M.singleton track.goal 0) (M.singleton track.goal 0)
-- print costsFromStart
-- print costsFromGoal
- -- print $ fmap (\h -> (h, pathCostWithCheat track costsFromStart costsFromGoal h)) $ S.toList track.walls
- -- print $ allCheatedCosts track costsFromStart costsFromGoal
- print $ findSavings track costsFromStart costsFromGoal
+ -- print $ fmap (\h -> (h, pathCostWithCheat 2 track costsFromStart costsFromGoal h)) $ M.keys costsFromStart
+ -- print $ allCheatedCosts 2 track costsFromStart costsFromGoal
+ -- print $ findSavings 2 track costsFromStart costsFromGoal
+ -- print $ findSavings 20 track costsFromStart costsFromGoal
print $ part1 track costsFromStart costsFromGoal
+ print $ part2 track costsFromStart costsFromGoal
-- let allTrails = allRoutesFrom maze
-- -- print allTrails
-- print $ part1 allTrails
-- part1, part2 :: [Agendum] -> Int
-- part1 trails = cost $ head trails
-findSavings track costsFromStart costsFromGoal = fmap (\g -> (length g, head g)) $ group $ sort savings
+findSavings cheatLen track costsFromStart costsFromGoal = fmap (\g -> (length g, head g)) $ group $ sort savings
where fullCost = costsFromStart M.! track.goal
- cheatCosts = allCheatedCosts track costsFromStart costsFromGoal
+ cheatCosts = allCheatedCosts cheatLen track costsFromStart costsFromGoal
savings = filter (> 0) $ fmap (\c -> fullCost - c) cheatCosts
part1 track costsFromStart costsFromGoal = length savings
where fullCost = costsFromStart M.! track.goal
- cheatCosts = allCheatedCosts track costsFromStart costsFromGoal
+ cheatCosts = allCheatedCosts 2 track costsFromStart costsFromGoal
+ savings = filter (>= 100) $ fmap (\c -> fullCost - c) cheatCosts
+part2 track costsFromStart costsFromGoal = length savings
+ where fullCost = costsFromStart M.! track.goal
+ cheatCosts = allCheatedCosts 20 track costsFromStart costsFromGoal
savings = filter (>= 100) $ fmap (\c -> fullCost - c) cheatCosts
+
mkTrack :: String -> Track
mkTrack text = Track { walls = walls, start = start, goal = goal }
where rows = lines text
cMax = maximum $ fmap (\(V2 _ c) -> c) $ S.toList $ walls track
showRow r = [ showCell (V2 r c) | c <- [0..cMax] ]
showCell pos
- | pos == here = '@'
+ | pos == here = '□'
| pos == start track = 'S'
| pos == goal track = 'E'
- | pos `S.member` walls track = 'â\98\90'
+ | pos `S.member` walls track = 'â\96 '
| otherwise = '.'
costsFrom :: Track -> TrackCost -> TrackCost -> TrackCost
here ^+^ V2 1 0, here ^+^ V2 (-1) 0 ]
-allCheatedCosts track costsFromStart costsFromGoal =
- catMaybes [ pathCostWithCheat track costsFromStart costsFromGoal h
- | h <- S.toList track.walls
+allCheatedCosts cheatLen track costsFromStart costsFromGoal =
+ concat [ pathCostWithCheat cheatLen track costsFromStart costsFromGoal h
+ | h <- M.keys costsFromStart
]
-pathCostWithCheat :: Track -> TrackCost -> TrackCost -> Position -> Maybe Int
-pathCostWithCheat track costsFromStart costsFromGoal here
- | (not $ null costsToStart) && (not $ null costsToGoal) = Just $ minimum costsToStart + minimum costsToGoal + 2
- | otherwise = Nothing
+pathCostWithCheat :: Int -> Track -> TrackCost -> TrackCost -> Position -> [Int]
+pathCostWithCheat cheatLen track costsFromStart costsFromGoal here =
+ fmap (+ costsFromStart M.! here) continueCosts
where
- nbrs = neighbours track here
- costsToStart = catMaybes $ fmap (`M.lookup` costsFromStart) nbrs
- costsToGoal = catMaybes $ fmap (`M.lookup` costsFromGoal) nbrs
+ nbrs = [ here ^+^ (V2 dr dc)
+ | dr <- [-cheatLen .. cheatLen]
+ , dc <- [-cheatLen .. cheatLen]
+ , abs dr + abs dc <= cheatLen
+ ]
+ continueCosts = catMaybes $ fmap contCost nbrs
+ contCost :: Position -> Maybe Int
+ contCost nbr = do gc <- M.lookup nbr costsFromGoal
+ let sc = l2Dist nbr here
+ return $ gc + sc
+
+l2Dist :: Position -> Position -> Int
+l2Dist (V2 r1 c1) (V2 r2 c2) = abs (r1 - r2) + abs (c1 - c2)