1 -- Writeup at https://work.njae.me.uk/2023/12/15/advent-of-code-2023-day-13/
7 import qualified Data.Map.Strict as M
9 data Element = Empty | Cube | Round deriving (Show, Eq, Ord)
10 type Grid = [[Element]]
12 type Cache = M.Map Grid Int
17 do dataFileName <- getDataFileName
18 text <- readFile dataFileName
19 let grid = transpose $ fmap (fmap readElem) $ lines text
21 -- print $ rollGrid $ transpose grid
22 -- let r1 = rollToCompletion grid
24 -- putStrLn $ showGrid r1
25 -- print $ transpose $ fmap reverse r1
27 -- print $ rollCycle grid
28 -- putStrLn $ showGrid $ rollCycle grid
29 -- putStrLn $ showGrid $ rollCycle $ rollCycle grid
30 -- putStrLn $ showGrid $ rollCycle $ rollCycle $ rollCycle grid
31 -- print $ findRepeat grid
36 -- print $ part2 patts
39 part1 grid = scoreGrid grid'
40 where grid' = rollToCompletion grid
42 part2 grid = scoreGrid finalGrid
43 where (grid', cache, i) = findRepeat grid
44 repeatStart = cache M.! grid'
45 repeatLen = i - repeatStart
46 finalIndex = (1e9 - repeatStart - 1) `mod` repeatLen
47 (finalGrid, _) = M.findMin $ M.filter (== finalIndex) cache
49 readElem :: Char -> Element
54 rollToCompletion :: Grid -> Grid
55 rollToCompletion grid = fst $ head $ dropWhile (uncurry (/=)) $ zip states $ tail states
56 where states = iterate rollGrid grid
58 rollGrid :: Grid -> Grid
61 roll :: [Element] -> [Element]
63 roll (l:ls) = rs ++ [r]
64 where (rs, r) = foldl' rollStep ([], l) ls
66 rollStep :: ([Element], Element) -> Element -> ([Element], Element)
67 rollStep (handled, Empty) Round = (handled ++ [Round], Empty)
68 rollStep (handled, target) source = (handled ++ [target], source)
70 scoreGrid :: Grid -> Int
71 scoreGrid grid = sum $ fmap scoreRow indexedGrid
72 where indexedGrid = zip [1..] $ reverse $ transpose grid
73 scoreRow (i, r) = i * (length $ filter (== Round) r)
75 rotate1 :: Grid -> Grid
76 rotate1 = transpose . fmap reverse
78 rollCycle :: Grid -> Grid
79 rollCycle = appEndo (stimes 4 (Endo rotate1 <> Endo rollToCompletion))
81 showGrid :: Grid -> String
82 showGrid grid = unlines $ fmap (fmap showElem) $ transpose grid
83 where showElem Empty = '.'
88 rollCycleWithCache (grid, cache, index) = (rollCycle grid, M.insert grid index cache, (index + 1))
90 findRepeat grid = head $ dropWhile test $ iterate rollCycleWithCache (grid, M.empty, 0)
91 where test (g, c, i) = g `M.notMember` c