import Data.List import Data.Maybe type Coo = (Int, Int) type Region = (Plot, [Coo]) type Plot = Char type Grid = [[Plot]] (!?) :: Grid -> Coo -> Maybe Plot grid !? (row, col) | row < 0 = Nothing | row >= length grid = Nothing | col < 0 = Nothing | col >= (length $ head grid) = Nothing | otherwise = Just (grid !! row !! col) neighCoos :: Coo -> [Coo] neighCoos (r,c) = [(r-1, c), (r, c-1), (r, c+1), (r+1, c)] neighs :: Grid -> Plot -> Coo -> [Coo] neighs grid p c = mapMaybe match $ neighCoos c where match coo = if grid !? coo == Just p then Just coo else Nothing region :: Grid -> [Coo] -> [Coo] -> Region region grid res@(h:_) [] = (fromJust $ grid !? h, res) region grid res (h:t) = region grid (h:res) (t ++ filter f ns) where Just p = grid !? h ns = neighs grid p h f c = (not $ c `elem` res) && (not $ c `elem` t) consume1 :: Grid -> [Region] -> [Coo] -> [Region] consume1 _ acc [] = acc consume1 grid acc (h:t) = consume1 grid (r:acc) (t \\ cs) where r@(_,cs) = region grid [] [h] consume :: String -> [Region] consume s = consume1 grid [] allcoos where grid = lines s allcoos = [(r, c) | r <- [0..((length grid) - 1)], c <- [0..((length $ head grid) - 1)]] area :: Region -> Int area (_, cs) = length cs perimeter1 :: Region -> Int -> [Coo] -> Int perimeter1 _ s [] = s perimeter1 r@(_, cs) s (h:t) = perimeter1 r (s + e) t where ncs = filter ((flip elem) cs) $ neighCoos h e = 4 - (length ncs) perimeter :: Region -> Int perimeter r@(_, cs) = perimeter1 r 0 cs fenceCost :: Region -> Int fenceCost r = area r * perimeter r part1 :: [Region] -> Int part1 = sum . map fenceCost main = do file <- readFile "day12.input" let regions = consume file putStrLn ("Part 1: " ++ (show $ part1 regions)) --putStrLn ("Part 2: " ++ (show $ part2 regions))