|
| 1 | +module Main where |
| 2 | + |
| 3 | +import Data.List |
| 4 | +import Data.Matrix (Matrix, (!)) |
| 5 | +import Data.Matrix qualified as Mat |
| 6 | +import Data.Set (Set) |
| 7 | +import Data.Set qualified as S |
| 8 | +import Data.Tuple.Extra |
| 9 | +import Debug.Trace |
| 10 | +import System.Environment |
| 11 | +import Text.Regex.TDFA ((=~)) |
| 12 | + |
| 13 | +type Input = Matrix Char |
| 14 | + |
| 15 | +type Output = Int |
| 16 | + |
| 17 | +data Dir = UP | LEFT | RIGHT | DOWN deriving (Show, Eq, Ord) |
| 18 | + |
| 19 | +parseInput :: String -> Input |
| 20 | +parseInput = Mat.fromLists . lines |
| 21 | + |
| 22 | +indices :: Matrix a -> [(Int, Int)] |
| 23 | +indices grid = [(y, x) | y <- [1 .. Mat.nrows grid], x <- [1 .. Mat.ncols grid]] |
| 24 | + |
| 25 | +move :: (Int, Int) -> Dir -> (Int, Int) |
| 26 | +move (y, x) UP = (y - 1, x) |
| 27 | +move (y, x) DOWN = (y + 1, x) |
| 28 | +move (y, x) LEFT = (y, x - 1) |
| 29 | +move (y, x) RIGHT = (y, x + 1) |
| 30 | + |
| 31 | +isOut :: (Int, Int) -> (Int, Int) -> Bool |
| 32 | +isOut (width, height) (y, x) = x <= 0 || y <= 0 || x > width || y > height |
| 33 | + |
| 34 | +mapOutRegion :: Matrix Char -> Char -> (Int, Int) -> Dir -> Set (Int, Int) -> (Set (Int, Int), Set ((Int, Int), Dir)) |
| 35 | +mapOutRegion grid plant yx dir seen |
| 36 | + | yx `S.member` seen = (seen, S.empty) |
| 37 | + | isOut (Mat.ncols grid, Mat.nrows grid) yx = (seen, S.singleton (yx, dir)) |
| 38 | + | grid ! yx /= plant = (seen, S.singleton (yx, dir)) |
| 39 | + | otherwise = foldl (\res dir' -> second (S.union (snd res)) $ mapOutRegion grid plant (move yx dir') dir' (fst res)) (S.insert yx seen, S.empty) [UP, LEFT, RIGHT, DOWN] |
| 40 | + |
| 41 | +algo :: (Set ((Int, Int), Dir) -> Int) -> Input -> Output |
| 42 | +algo wallCounter grid = sum . fst . foldl folder ([], S.empty) $ indices grid |
| 43 | + where |
| 44 | + folder :: ([Int], Set (Int, Int)) -> (Int, Int) -> ([Int], Set (Int, Int)) |
| 45 | + folder (prices, seen) yx |
| 46 | + | yx `S.member` seen = (prices, seen) |
| 47 | + | otherwise = ((S.size iteSeen * wallCounter wallSeen) : prices, S.union iteSeen seen) |
| 48 | + where |
| 49 | + (iteSeen, wallSeen) = mapOutRegion grid (grid ! yx) yx UP S.empty |
| 50 | + |
| 51 | +part1 :: Input -> Output |
| 52 | +part1 = algo S.size |
| 53 | + |
| 54 | +nbSides :: Set ((Int, Int), Dir) -> Int |
| 55 | +nbSides walls = sum $ map (uncurry nbWallLength) groupedWallLst |
| 56 | + where |
| 57 | + wallLst :: [((Int, Int), Dir)] |
| 58 | + wallLst = S.toList walls |
| 59 | + |
| 60 | + sidedWallLst :: [[((Int, Int), Dir)]] |
| 61 | + sidedWallLst = groupBy (\a b -> snd a == snd b) $ sortOn snd wallLst |
| 62 | + |
| 63 | + groupedWallLst :: [(Dir, [(Int, Int)])] |
| 64 | + groupedWallLst = map (\arr -> (snd $ head arr, map fst arr)) sidedWallLst |
| 65 | + |
| 66 | + nbWallLength UP = nbOn . sort |
| 67 | + nbWallLength DOWN = nbOn . sort |
| 68 | + nbWallLength LEFT = nbOn . sort . map swap |
| 69 | + nbWallLength RIGHT = nbOn . sort . map swap |
| 70 | + |
| 71 | + nbOn = snd . foldl folder ((-1, -1), 0) |
| 72 | + where |
| 73 | + folder (lastYX, nbWalls) yx |
| 74 | + | fst yx /= fst lastYX = (yx, nbWalls + 1) |
| 75 | + | snd yx /= snd lastYX + 1 = (yx, nbWalls + 1) |
| 76 | + | otherwise = (yx, nbWalls) |
| 77 | + |
| 78 | +part2 :: Input -> Output |
| 79 | +part2 = algo nbSides |
| 80 | + |
| 81 | +main :: IO () |
| 82 | +main = do |
| 83 | + args <- getArgs |
| 84 | + content <- readFile (last args) |
| 85 | + let input = parseInput content |
| 86 | + |
| 87 | + -- print $ Mat.toLists input |
| 88 | + |
| 89 | + print $ (== 1465112) $ part1 input |
| 90 | + print $ (== 893790) $ part2 input |
0 commit comments