never executed always true always false
1 {-# LANGUAGE TypeFamilies #-}
2 {-|
3 Module: Y2021.D15
4 Description: Advent of Code 2021 Day 15 Solutions.
5 License: MIT
6 Maintainer: @tylerjl
7
8 Solutions to the 2021 day 15 set of problems for <adventofcode.com>.
9 -}
10 module Y2021.D15
11 ( parse15
12 , part15A
13 , part15B
14 ) where
15
16 import Control.Arrow
17 import Data.Attoparsec.Text hiding (take)
18 import Data.Either.Utils (fromRight)
19 import Data.Foldable
20 import Data.List.Extra (transpose)
21 import Data.Map.Strict (Map)
22 import Data.Text (Text)
23 import Math.Geometry.Grid hiding (distance)
24 import Math.Geometry.Grid.Square
25 import Math.Geometry.GridMap hiding (foldl', map, filter)
26 import Math.Geometry.GridMap.Lazy
27
28 import qualified Data.Heap as H
29 import qualified Data.Map.Strict as M
30 import qualified Math.Geometry.GridMap as GM
31
32 type Cavern = LGridMap RectSquareGrid Int
33 data Distance a
34 = Infinite
35 | Distance a
36 deriving (Eq, Show)
37
38 instance Ord a => Ord (Distance a) where
39 Infinite `compare` Infinite = EQ
40 (Distance _) `compare` Infinite = LT
41 (Distance a) `compare` (Distance b) = a `compare` b
42 Infinite `compare` (Distance _) = GT
43
44 type Paths = Map (Int, Int) (Distance Int)
45
46 -- |Solution to part A
47 part15A :: Text -> Int
48 part15A (parse15 -> grid) =
49 case flip (M.!) (endPosition grid) $ cheapestPath grid of
50 Distance d -> d
51 Infinite -> error "can't reach goal"
52
53 cheapestPath :: Cavern -> Paths
54 cheapestPath grid = go (H.singleton (H.Entry (Distance 0) (0, 0))) nodes
55 where
56 go (H.viewMin -> Nothing) paths = paths
57 go (H.viewMin -> Just (H.Entry dist point, q)) paths
58 = go q' paths'
59 where
60 (q', paths') = foldl' measure (q, paths)
61 [(n, grid ! n) | n <- neighbours grid point]
62 measure (heap, costs) (neigh, cost)
63 | alt < toNeigh = ( H.insert (H.Entry alt neigh) heap
64 , M.insert neigh alt costs
65 )
66 | otherwise = (heap, costs)
67 where
68 alt = dist `addDist` Distance cost
69 toNeigh = costs M.! neigh
70 allNodes = map fst $ GM.toList grid
71 nodes = M.fromList $ zip allNodes (repeat Infinite)
72
73 endPosition :: Cavern -> (Int, Int)
74 endPosition = (pred *** pred) . size
75
76 addDist :: Num a => Distance a -> Distance a -> Distance a
77 addDist (Distance a) (Distance b) = Distance (a + b)
78 addDist Infinite (Distance b) = Distance b
79 addDist (Distance a) Infinite = Distance a
80 addDist Infinite Infinite = Infinite
81
82 -- |Solution to part B
83 part15B :: Text -> Int
84 part15B (expandBy 5 . parse15 -> grid) =
85 case flip (M.!) (endPosition grid) $ cheapestPath grid of
86 Distance d -> d
87 Infinite -> error "can't reach goal"
88
89 expandBy :: Int -> Cavern -> Cavern
90 expandBy n g
91 = GM.mapWithKey populate $ lazyGridMap (rectSquareGrid rows' cols') (repeat 0 :: [Int])
92 where
93 (rows, cols) = size g
94 (rows', cols') = (rows * n, cols * n)
95 populate (x, y) _ = iterate elevate (g ! (x `mod` cols, y `mod` rows)) !! (extX + extY)
96 where (extX, extY) = (x `div` cols, y `div` rows)
97
98 elevate :: Integral a => a -> a
99 elevate n = max 1 ((n + 1) `mod` 10)
100
101 -- |Parse.
102 parse15 :: Text -> Cavern
103 parse15 = fromRight . parseOnly (grid <$> parser)
104 where
105 grid [] = error "empty input"
106 grid rows@(row:_) =
107 lazyGridMap (rectSquareGrid (length row) (length rows)) (concat $ transpose rows)
108 parser = line `sepBy1` endOfLine <* atEnd
109 line = many1 (read . (: []) <$> digit)