|
| 1 | +module Main where |
| 2 | + |
| 3 | +import Data.Bits |
| 4 | +import Data.List |
| 5 | +import Data.List.Split |
| 6 | +import Data.List.Unique |
| 7 | +import Data.Matrix (Matrix, (!)) |
| 8 | +import Data.Matrix qualified as Mat |
| 9 | +import Data.Set (Set) |
| 10 | +import Data.Set qualified as S |
| 11 | +import Data.Tuple.Extra |
| 12 | +import Debug.Trace |
| 13 | +import System.Environment |
| 14 | +import Text.Regex.TDFA ((=~)) |
| 15 | + |
| 16 | +-- TODO: Cleanup imports after day done |
| 17 | + |
| 18 | +data Dir = UP | LEFT | RIGHT | DOWN deriving (Show, Eq, Ord) |
| 19 | + |
| 20 | +type Input = (Set (Int, Int), Set (Int, Int), [Dir], (Int, Int)) |
| 21 | + |
| 22 | +type Output = Int |
| 23 | + |
| 24 | +parseInput1 :: String -> Input |
| 25 | +parseInput1 inp = (walls, boxes, parseMoves moves, robotPos) |
| 26 | + where |
| 27 | + [gridStr, moves] = splitOn "\n\n" inp |
| 28 | + (w, h) = (length $ head $ lines gridStr, length $ lines gridStr) |
| 29 | + wharehouse = lines $ gridStr |
| 30 | + parseMoves = map parseMove . concat . lines |
| 31 | + parseMove '<' = LEFT |
| 32 | + parseMove '^' = UP |
| 33 | + parseMove '>' = RIGHT |
| 34 | + parseMove 'v' = DOWN |
| 35 | + walls = S.fromList [(y, x) | y <- [0 .. h - 1], x <- [0 .. w - 1], wharehouse !! y !! x == '#'] |
| 36 | + boxes = S.fromList [(y, x) | y <- [0 .. h - 1], x <- [0 .. w - 1], wharehouse !! y !! x == 'O'] |
| 37 | + robotPos = head [(y, x) | y <- [0 .. h - 1], x <- [0 .. w - 1], wharehouse !! y !! x == '@'] |
| 38 | + |
| 39 | +parseInput2 :: String -> Input |
| 40 | +parseInput2 inp = (walls, boxes, parseMoves moves, robotPos) |
| 41 | + where |
| 42 | + [gridStr, moves] = splitOn "\n\n" inp |
| 43 | + (w, h) = (length $ head $ lines gridStr, length $ lines gridStr) |
| 44 | + wharehouse = lines $ gridStr |
| 45 | + parseMoves = map parseMove . concat . lines |
| 46 | + parseMove '<' = LEFT |
| 47 | + parseMove '^' = UP |
| 48 | + parseMove '>' = RIGHT |
| 49 | + parseMove 'v' = DOWN |
| 50 | + walls = S.fromList $ concat [[(y, 2 * x), (y, 2 * x + 1)] | y <- [0 .. h - 1], x <- [0 .. w - 1], wharehouse !! y !! x == '#'] |
| 51 | + boxes = S.fromList [(y, 2 * x) | y <- [0 .. h - 1], x <- [0 .. w - 1], wharehouse !! y !! x == 'O'] |
| 52 | + robotPos = head [(y, 2 * x) | y <- [0 .. h - 1], x <- [0 .. w - 1], wharehouse !! y !! x == '@'] |
| 53 | + |
| 54 | +move :: (Int, Int) -> Dir -> (Int, Int) |
| 55 | +move (y, x) UP = (y - 1, x) |
| 56 | +move (y, x) DOWN = (y + 1, x) |
| 57 | +move (y, x) LEFT = (y, x - 1) |
| 58 | +move (y, x) RIGHT = (y, x + 1) |
| 59 | + |
| 60 | +processMoves :: Input -> Input |
| 61 | +processMoves (walls, boxes, [], pos) = (walls, boxes, [], pos) |
| 62 | +processMoves (walls, boxes, (e : l), pos) = processMoves (walls, newBoxes, l, newPos) |
| 63 | + where |
| 64 | + (newBoxes, newPos) = processMove e |
| 65 | + processMove e |
| 66 | + | pos' `S.member` walls = (boxes, pos) |
| 67 | + | pos' `S.notMember` boxes = (boxes, pos') |
| 68 | + | pos' `S.notMember` boxes' = (boxes', pos') |
| 69 | + | otherwise = (boxes, pos) |
| 70 | + where |
| 71 | + pos' = move pos e |
| 72 | + boxes' = processMoveBox e pos' |
| 73 | + |
| 74 | + processMoveBox e pos' |
| 75 | + | pos'' `S.member` walls = boxes |
| 76 | + | pos'' `S.notMember` boxes = S.insert pos'' $ S.delete pos' boxes |
| 77 | + | pos'' `S.notMember` boxes' = S.insert pos'' $ S.delete pos' boxes' |
| 78 | + | otherwise = boxes |
| 79 | + where |
| 80 | + boxes' = processMoveBox e (move pos' e) |
| 81 | + pos'' = move pos' e |
| 82 | + |
| 83 | +gps :: [(Int, Int)] -> Output |
| 84 | +gps = sum . map (\(y, x) -> 100 * y + x) |
| 85 | + |
| 86 | +part1 :: Input -> Output |
| 87 | +part1 input = gps $ S.toList boxes |
| 88 | + where |
| 89 | + (_, boxes, _, _) = processMoves input |
| 90 | + |
| 91 | +processMoves2 :: Input -> Input |
| 92 | +processMoves2 (walls, boxes, [], pos) = (walls, boxes, [], pos) |
| 93 | +processMoves2 (walls, boxes, (e : l), pos) = processMoves2 (walls, newBoxes, l, newPos) |
| 94 | + where |
| 95 | + (newBoxes, newPos) = processMove e |
| 96 | + processMove e |
| 97 | + | pos' `S.member` walls = (boxes, pos) |
| 98 | + | pos' `S.notMember` boxes && (y, x - 1) `S.notMember` boxes = (boxes, pos') |
| 99 | + | pos' `S.notMember` boxes' && (y, x - 1) `S.notMember` boxes' = (boxes', pos') |
| 100 | + | otherwise = (boxes, pos) |
| 101 | + where |
| 102 | + (pos'@(y, x)) = move pos e |
| 103 | + boxPos = (if pos' `S.member` boxes then pos' else (y, x - 1)) |
| 104 | + boxes' = processMoveBox e boxPos |
| 105 | + |
| 106 | + processMoveBox :: Dir -> (Int, Int) -> Set (Int, Int) |
| 107 | + processMoveBox LEFT pos = processMoveBoxLeft pos |
| 108 | + processMoveBox RIGHT pos = processMoveBoxRight pos |
| 109 | + processMoveBox e pos = processMoveBoxY boxes e pos |
| 110 | + |
| 111 | + processMoveBoxLeft :: (Int, Int) -> Set (Int, Int) |
| 112 | + processMoveBoxLeft pos' |
| 113 | + | newBoxLeft `S.member` walls = boxes |
| 114 | + | (move newBoxLeft LEFT) `S.notMember` boxes = S.insert newBoxLeft $ S.delete pos' boxes |
| 115 | + | (move newBoxLeft LEFT) `S.notMember` boxes' = S.insert newBoxLeft $ S.delete pos' boxes' |
| 116 | + | otherwise = boxes |
| 117 | + where |
| 118 | + boxes' = processMoveBoxLeft (move newBoxLeft LEFT) |
| 119 | + newBoxLeft = move pos' LEFT |
| 120 | + newBoxRight = second (+ 1) newBoxLeft |
| 121 | + |
| 122 | + processMoveBoxRight :: (Int, Int) -> Set (Int, Int) |
| 123 | + processMoveBoxRight pos' |
| 124 | + | newBoxRight `S.member` walls = boxes |
| 125 | + | newBoxLeft `S.notMember` boxes && newBoxRight `S.notMember` boxes = S.insert newBoxLeft $ S.delete pos' boxes |
| 126 | + | newBoxLeft `S.notMember` boxes' && newBoxRight `S.notMember` boxes' = S.insert newBoxLeft $ S.delete pos' boxes' |
| 127 | + | otherwise = boxes |
| 128 | + where |
| 129 | + boxes' = processMoveBoxRight (move newBoxLeft RIGHT) |
| 130 | + newBoxLeft = move pos' RIGHT |
| 131 | + newBoxRight = second (+ 1) newBoxLeft |
| 132 | + |
| 133 | + processMoveBoxY :: Set (Int, Int) -> Dir -> (Int, Int) -> Set (Int, Int) |
| 134 | + processMoveBoxY boxes e pos' |
| 135 | + | newBoxLeft `S.member` walls || newBoxRight `S.member` walls = boxes |
| 136 | + | newBoxLeft `S.notMember` boxes && newBoxRight `S.notMember` boxes && (move newBoxLeft LEFT) `S.notMember` boxes = S.insert newBoxLeft $ S.delete pos' boxes |
| 137 | + | newBoxLeft `S.notMember` boxes' && newBoxRight `S.notMember` boxes' && (move newBoxLeft LEFT) `S.notMember` boxes' = S.insert newBoxLeft $ S.delete pos' boxes' |
| 138 | + | otherwise = boxes |
| 139 | + where |
| 140 | + boxes' = foldl (\b p -> processMoveBoxY b e p) boxes $ filter (`S.member` boxes) [(move newBoxLeft LEFT), newBoxLeft, newBoxRight] |
| 141 | + newBoxLeft = move pos' e |
| 142 | + newBoxRight = second (+ 1) newBoxLeft |
| 143 | + |
| 144 | +part2 :: Input -> Output |
| 145 | +part2 input = gps $ S.toList boxes |
| 146 | + where |
| 147 | + (_, boxes, _, _) = processMoves2 input |
| 148 | + |
| 149 | +showGrid :: Input -> [String] |
| 150 | +showGrid (walls, boxes, _, pos) = [[if (y, x) `S.member` walls then '#' else if (y, x) `S.member` boxes then 'O' else if (y, x) == pos then '@' else '.' | x <- [0 .. 13]] | y <- [0 .. 6]] |
| 151 | + |
| 152 | +main :: IO () |
| 153 | +main = do |
| 154 | + args <- getArgs |
| 155 | + content <- readFile (last args) |
| 156 | + let input1 = parseInput1 content |
| 157 | + let input2 = parseInput2 content |
| 158 | + |
| 159 | + -- print $ input |
| 160 | + print $ part1 input1 |
| 161 | + print $ part2 input2 |
0 commit comments