diff options
| author | Laura Orvokki Kursula <lav@vampires.gay> | 2024-12-12 18:17:09 +0100 | 
|---|---|---|
| committer | Laura Orvokki Kursula <lav@vampires.gay> | 2024-12-12 18:17:09 +0100 | 
| commit | 447e9f5048ecee06afd9b545ccc445c232ec4635 (patch) | |
| tree | 1951e53ff3f5e49ad289decb710774a3313965ae | |
| parent | 310a752668c80f13803adff665090a868f54b1ab (diff) | |
| download | aoc2024-447e9f5048ecee06afd9b545ccc445c232ec4635.tar.gz aoc2024-447e9f5048ecee06afd9b545ccc445c232ec4635.zip | |
12-1
| -rw-r--r-- | 12-1.hs | 72 | 
1 files changed, 72 insertions, 0 deletions
| @@ -0,0 +1,72 @@ +import Data.Array +import Data.List (nub) + +type Map = Array (Int,Int) (Maybe Char) + +parse :: String -> Map +parse s = let ls = lines s +              w  = length $ head ls +              h  = length ls +          in array ((0,0), (w-1,h-1)) [ ((x,y),Just $ ls !! y !! x) +                                      | x <- [0..w-1] +                                      , y <- [0..h-1] +                                      ] + +findNext :: (Int,Int) -> Map -> Maybe (Int,Int) +findNext = go +  where +    go :: (Int,Int) -> Map -> Maybe (Int,Int) +    go p@(x,y) m | inRange (bounds m) p = case m ! p of +                     Just _ -> Just p +                     Nothing -> let p' = (x+1,y) +                                in if inRange (bounds m) p' +                                   then go p' m +                                   else go (0,y+1) m +                 | otherwise = Nothing + +getRegion :: Map -> (Int,Int) -> [(Int,Int)] +getRegion m p = go [p] [p] +  where +    go :: [(Int,Int)]  -> [(Int,Int)] -> [(Int,Int)] +    go visited points = +      let v = filter f +            . filter (not . (`elem` visited)) +            . nub +            . concatMap neighbours +            $ points +      in case v of +           [] -> visited +           _  -> go (visited ++ v) v +     +    f :: (Int,Int) -> Bool +    f p' = inRange (bounds m) p' && m ! p == m ! p' + +markDone :: Map -> [(Int,Int)] -> Map +markDone m ps = m // map (,Nothing) ps + +getRegions :: Map -> [[(Int,Int)]] +getRegions = go [] (0,0) +  where +    go :: [[(Int,Int)]] -> (Int,Int) -> Map -> [[(Int,Int)]] +    go xs cur m = case findNext cur m of +      Nothing -> xs +      Just p  -> let r = getRegion m p +                 in go (r : xs) p (markDone m r) + +neighbours :: (Int,Int) -> [(Int,Int)] +neighbours (x,y) = [(x+1,y), (x-1,y), (x,y+1), (x,y-1)] + +perimeter :: Map -> (Int,Int) -> Int +perimeter m p = length . filter f $ neighbours p +  where +    f :: (Int,Int) -> Bool +    f p' = not (inRange (bounds m) p') || m ! p' /= m ! p + +score :: Map -> [(Int,Int)] -> Int +score m r = length r * sum (map (perimeter m) r) + +mapScore :: Map -> Int +mapScore m = sum . map (score m) . getRegions $ m + +main :: IO () +main = getContents >>= print . mapScore . parse |