|
| 1 | +module Main where |
| 2 | + |
| 3 | +import Data.Bits |
| 4 | +import Data.Char |
| 5 | +import Data.List |
| 6 | +import Data.List.Split |
| 7 | +import Data.List.Unique |
| 8 | +import Data.Map (Map) |
| 9 | +import Data.Map qualified as M |
| 10 | +import Data.Matrix (Matrix, (!)) |
| 11 | +import Data.Matrix qualified as Mat |
| 12 | +import Data.Set (Set) |
| 13 | +import Data.Set qualified as S |
| 14 | +import Data.Tuple.Extra |
| 15 | +import Debug.Trace |
| 16 | +import System.Environment |
| 17 | +import Text.Regex.TDFA ((=~)) |
| 18 | + |
| 19 | +-- TODO: Cleanup imports after day done |
| 20 | + |
| 21 | +type Input = [String] |
| 22 | + |
| 23 | +type Output = Int |
| 24 | + |
| 25 | +parseInput :: String -> Input |
| 26 | +parseInput = lines |
| 27 | + |
| 28 | +shorten :: [String] -> [String] |
| 29 | +shorten = head . groupBy (\e1 e2 -> length e1 == length e2) . sortOn length |
| 30 | + |
| 31 | +shortenS :: Set String -> Set String |
| 32 | +shortenS = S.fromList . head . groupBy (\e1 e2 -> length e1 == length e2) . sortOn length . S.toList |
| 33 | + |
| 34 | +sConcat :: (Ord a) => Set (Set a) -> Set a |
| 35 | +sConcat = S.unions . S.toList |
| 36 | + |
| 37 | +memoise2 :: (Ord a, Ord b) => Map (a, b) c -> (a -> b -> c) -> a -> b -> (Map (a, b) c, c) |
| 38 | +memoise2 mem f a b |
| 39 | + | (a, b) `M.member` mem = (mem, mem M.! (a, b)) |
| 40 | + | otherwise = (M.insert (a, b) res mem, res) |
| 41 | + where res = f a b |
| 42 | + |
| 43 | +memoise3 :: (Ord a, Ord b, Ord c) => Map (a, b, c) d -> (a -> b -> c -> d) -> a -> b -> c -> (Map (a, b, c) d, d) |
| 44 | +memoise3 mem f a b c |
| 45 | + | (a, b, c) `M.member` mem = (mem, mem M.! (a, b, c)) |
| 46 | + | otherwise = (M.insert (a, b, c) res mem, res) |
| 47 | + where res = f a b c |
| 48 | + |
| 49 | + |
| 50 | + |
| 51 | +goToY :: [(Int, Int)] -> (Int, Int) -> (Int, Int) -> String |
| 52 | +goToY possible (x, y) (x', y') |
| 53 | + | x == x' && y == y' = [] |
| 54 | + | y < y' && (x, y+1) `elem` possible = 'v' : goToY possible (x, y + 1) (x', y') |
| 55 | + | y > y' && (x, y-1) `elem` possible = '^' : goToY possible (x, y - 1) (x', y') |
| 56 | + | x < x' && (x+1, y) `elem` possible = '>' : goToY possible (x + 1, y) (x', y') |
| 57 | + | x > x' && (x-1, y) `elem` possible = '<' : goToY possible (x - 1, y) (x', y') |
| 58 | + |
| 59 | +goToX :: [(Int, Int)] -> (Int, Int) -> (Int, Int) -> String |
| 60 | +goToX possible (x, y) (x', y') |
| 61 | + | x == x' && y == y' = [] |
| 62 | + | x < x' && (x+1, y) `elem` possible = '>' : goToX possible (x + 1, y) (x', y') |
| 63 | + | x > x' && (x-1, y) `elem` possible = '<' : goToX possible (x - 1, y) (x', y') |
| 64 | + | y < y' && (x, y+1) `elem` possible = 'v' : goToX possible (x, y + 1) (x', y') |
| 65 | + | y > y' && (x, y-1) `elem` possible = '^' : goToX possible (x, y - 1) (x', y') |
| 66 | + |
| 67 | +isValid :: [(Int, Int)] -> (Int, Int) -> String -> Bool |
| 68 | +isValid validSpots xy [] = xy `elem` validSpots |
| 69 | +isValid validSpots (x, y) ('v':s) = (x, y) `elem` validSpots && isValid validSpots (x, y+1) s |
| 70 | +isValid validSpots (x, y) ('^':s) = (x, y) `elem` validSpots && isValid validSpots (x, y-1) s |
| 71 | +isValid validSpots (x, y) ('>':s) = (x, y) `elem` validSpots && isValid validSpots (x+1, y) s |
| 72 | +isValid validSpots (x, y) ('<':s) = (x, y) `elem` validSpots && isValid validSpots (x-1, y) s |
| 73 | + |
| 74 | +generalEmulator :: Map (Int, Int) Char -> (Int, Int )-> String -> String |
| 75 | +generalEmulator poss posA code = sub code posA |
| 76 | + where |
| 77 | + sub [] _ = [] |
| 78 | + sub ('A': l) xy = poss M.! xy : sub l xy |
| 79 | + sub ('v' : l) (x, y) = sub l (x, y+1) |
| 80 | + sub ('^' : l) (x, y) = sub l (x, y-1) |
| 81 | + sub ('>' : l) (x, y) = sub l (x+1, y) |
| 82 | + sub ('<' : l) (x, y) = sub l (x-1, y) |
| 83 | + |
| 84 | +numericalEmulator :: String -> String |
| 85 | +numericalEmulator code = generalEmulator numMap (2, 3) code |
| 86 | + where |
| 87 | + numMap = |
| 88 | + M.fromList $ |
| 89 | + [ ((0, 0), '7'), |
| 90 | + ((1, 0), '8'), |
| 91 | + ((2, 0), '9'), |
| 92 | + ((0, 1), '4'), |
| 93 | + ((1, 1), '5'), |
| 94 | + ((2, 1), '6'), |
| 95 | + ((0, 2), '1'), |
| 96 | + ((1, 2), '2'), |
| 97 | + ((2, 2), '3'), |
| 98 | + ((1, 3), '0'), |
| 99 | + ((2, 3), 'A') |
| 100 | + ] |
| 101 | + |
| 102 | +directionalEmulator :: String -> String |
| 103 | +directionalEmulator code = generalEmulator dirMap (2, 0) code |
| 104 | + where |
| 105 | + dirMap = M.fromList $ [((1, 0), '^'), ((2, 0), 'A'), ((0, 1), '<'), ((1, 1), 'v'), ((2, 1), '>')] |
| 106 | + |
| 107 | + |
| 108 | +sequencerC :: [(Int, Int)] -> (Int, Int) -> (Int, Int) -> ((Int, Int), Set String) |
| 109 | +sequencerC valid xy' xy = (xy', S.map (++ "A") goTos) |
| 110 | + where |
| 111 | + goTos = S.fromList $ shorten . map (\gt -> gt valid xy xy') $ [goToX, goToY] |
| 112 | + |
| 113 | +sequencer :: Map Char (Int, Int) -> String -> (Int, Int) -> Set String |
| 114 | +sequencer poss [] _ = S.singleton [] |
| 115 | +sequencer poss (e : l) xy = sConcat $ S.map (\r -> S.map (++ r) goTos) $ nextRes |
| 116 | + where |
| 117 | + nextRes = sequencer poss l xy' |
| 118 | + (xy', goTos) = sequencerC (M.elems poss) (poss M.! e) xy |
| 119 | + |
| 120 | +numericalSequencer :: String -> Set String |
| 121 | +numericalSequencer code = sequencer numMap code (numMap M.! 'A') |
| 122 | + where |
| 123 | + numMap = |
| 124 | + M.fromList $ |
| 125 | + [ ('7', (0, 0)), |
| 126 | + ('8', (1, 0)), |
| 127 | + ('9', (2, 0)), |
| 128 | + ('4', (0, 1)), |
| 129 | + ('5', (1, 1)), |
| 130 | + ('6', (2, 1)), |
| 131 | + ('1', (0, 2)), |
| 132 | + ('2', (1, 2)), |
| 133 | + ('3', (2, 2)), |
| 134 | + ('0', (1, 3)), |
| 135 | + ('A', (2, 3)) |
| 136 | + ] |
| 137 | + |
| 138 | + |
| 139 | +directionalSequencer :: String -> Set String |
| 140 | +directionalSequencer code = sequencer dirMap code (dirMap M.! 'A') |
| 141 | + where |
| 142 | + dirMap = M.fromList $ [('^', (1, 0)), ('A', (2, 0)), ('<', (0, 1)), ('v', (1, 1)), ('>', (2, 1))] |
| 143 | + |
| 144 | + |
| 145 | +depthMemorySequencer :: Map ((Int, Int), ((Int, Int), Int)) Int -> Int -> String -> (Map ((Int, Int), ((Int, Int), Int)) Int, Int) |
| 146 | +depthMemorySequencer mem depth code = sub mem depth code |
| 147 | + where |
| 148 | + dirMap = M.fromList $ [('^', (1, 0)), ('A', (2, 0)), ('<', (0, 1)), ('v', (1, 1)), ('>', (2, 1))] |
| 149 | + |
| 150 | + subc' :: Map ((Int, Int), ((Int, Int), Int)) Int -> Int -> (Int, Int) -> (Int, Int) -> (Map ((Int, Int), ((Int, Int), Int)) Int, Int) |
| 151 | + subc' mem' 1 c xy = memoise2 mem' (\c (xy, d) -> (length . head . sortOn length . S.toList) . snd $ sequencerC (M.elems dirMap) c xy) c (xy, 1) |
| 152 | + subc' mem' d c xy |
| 153 | + | (c, (xy, d)) `M.member` mem' = (mem', mem' M.! (c, (xy, d))) |
| 154 | + | otherwise = (M.insert (c, (xy, d)) r newMem, r) |
| 155 | + where |
| 156 | + |
| 157 | + (newMem, r) = S.foldl (\(m, r) s -> second (min r) $ sub m (d-1) s) (mem', maxBound) . snd $ sequencerC (M.elems dirMap) c xy |
| 158 | + |
| 159 | + sub :: Map ((Int, Int), ((Int, Int), Int)) Int -> Int -> String -> (Map ((Int, Int), ((Int, Int), Int)) Int, Int) |
| 160 | + sub mem' d s = second snd $ foldl (\(m, (p, r)) e -> second (\r' -> (dirMap M.! e, r + r')) $ subc' m d (dirMap M.! e) p) (mem', ((dirMap M.! 'A'), 0)) s -- foldl |
| 161 | + |
| 162 | + |
| 163 | + |
| 164 | +getComplexity :: Int -> String -> Int |
| 165 | +getComplexity depth code = trace (show sequenceLength ++ "*" ++ show codeValue) codeValue * sequenceLength |
| 166 | + where |
| 167 | + codeValue = read $ takeWhile isDigit code |
| 168 | + numSequences = numericalSequencer code |
| 169 | + |
| 170 | + sequenceLength = snd $ foldl (\(m, r) s -> second (min r) $ depthMemorySequencer m depth s) (M.empty, maxBound) numSequences |
| 171 | + |
| 172 | + |
| 173 | +part1 :: Input -> Output |
| 174 | +part1 = sum . map (getComplexity 2) |
| 175 | + |
| 176 | +part2 :: Input -> Output |
| 177 | +part2 = sum . map (getComplexity 25) |
| 178 | + |
| 179 | +main :: IO () |
| 180 | +main = do |
| 181 | + args <- getArgs |
| 182 | + content <- readFile (last args) |
| 183 | + let input = parseInput content |
| 184 | + |
| 185 | + print input |
| 186 | + |
| 187 | + print $ part1 input -- (== 248684) $ |
| 188 | + print $ part2 input --(== 307055584161760) $ |
0 commit comments