-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathday18.hs
More file actions
70 lines (56 loc) · 2.37 KB
/
Copy pathday18.hs
File metadata and controls
70 lines (56 loc) · 2.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
module Main where
import Control.Monad (ap, guard)
import Data.Bifunctor (bimap)
import Data.Bool (bool)
import Data.Heap qualified as H
import Data.List (find, inits)
import Data.Map.Strict (Map)
import Data.Map.Strict qualified as M
import Data.Maybe (isNothing, mapMaybe)
import Data.Set qualified as S
import Data.Tuple (swap)
import Debug.Trace
type Point = (Int, Int)
main = interact (unlines . sequence [part1, part2] . parse)
part1, part2 :: [Point] -> String
part1 points = ("Part 1: " ++) . maybe "" show . (!! steps points) $ bytes points
part2 points = ("Part 2: " ++) . show . search . drop (steps points) . zip points $ bytes points
search :: [(Point, Maybe Int)] -> Point
search xs = go 0 (length xs - 1)
where
go lo hi
| lo == hi = fst (xs !! lo)
| isNothing (snd (xs !! mid)) = go lo mid
| otherwise = go (mid + 1) hi
where
mid = (lo + hi) `div` 2
bytes :: [Point] -> [Maybe Int]
bytes points = bit (end points) <$> (drop 1 . inits) points
bit :: Point -> [Point] -> Maybe Int
bit end = dijkstra (0, 0) end . bitmap end
dijkstra :: Point -> Point -> Map Point Int -> Maybe Int
dijkstra start end points = step initialHeap initialDists S.empty
where
initialHeap = H.singleton (0, start)
initialDists = M.insert start 0 (maxBound <$ points)
toHeap = H.fromList . map swap . M.toList
step heap dists seen = do
((dist, point), heap') <- H.uncons heap
case point of
_ | point == end -> return dist
_ | S.member point seen -> step heap' dists seen
_ -> step (toHeap better <> heap') (better <> dists) (S.insert point seen)
where
better = M.differenceWith pick (neighbors point dists) dists
pick inc dist' = do
guard (dist + inc <= dist')
return (dist + inc)
-- neighbors :: Point -> Map Point Int -> Map Point Int
neighbors point grid = M.fromList $ mapMaybe (\a -> (a, 1) <$ M.lookup a grid) $ neighborPoints point
neighborPoints (x, y) = [(x + dx, y + dy) | (dx, dy) <- [(1, 0), (0, 1), (-1, 0), (0, -1)]]
bitmap :: Point -> [Point] -> M.Map Point Int
bitmap (mx, my) points = M.fromList [((x, y), 0) | x <- [0 .. mx], y <- [0 .. my], (x, y) `notElem` points]
end = bool (6, 6) (70, 70) . any ((> 7) . fst)
steps = bool 11 1023 . any ((> 7) . fst)
parse :: String -> [(Int, Int)]
parse = (bimap read (read . drop 1) . span (/= ',') <$>) . lines