module Day12 import Prelude import Node import Aoc import SortedMap -- move to lib gridPoints : String → List (Char × Int × Int) gridPoints text = go 0 0 (unpack text) Nil where -- might as well be tail recursive go : Int → Int → List Char → List (Char × Int × Int) → List (Char × Int × Int) go row col Nil points = points go row col ('\n' :: cs) points = go (row + 1) 0 cs points go row col (c :: cs) points = go row (col + 1) cs ((c,row,col) :: points) -- TODO add parameter a and pass Char -> a into getGrid Grid : U Grid = SortedMap Point Int digitToInt : Char → Int digitToInt c = ord c getGrid : String → Grid getGrid text = foldl update EmptyMap $ gridPoints text where update : Grid → Char × Point → Grid update grid (c,pt) = updateMap pt (digitToInt c) grid north east south west : Point north = (0, 0 - 1) south = (0, 1) east = (1, 0) west = (0 - 1, 0) neighbors : Point → List Point neighbors pt = map (_+_ pt) ((0, 0 - 1) :: (0,1) :: (0 - 1,0) :: (1,0) :: Nil) perim2 : Grid → Point → Int → Int perim2 grid pt color = let top = different (north + pt) && (different (west + pt) || same (west + north + pt)) bottom = different (south + pt) && (different (west + pt) || same (west + south + pt)) left = different (west + pt) && (different (north + pt) || same (north + west + pt)) right = different (east + pt) && (different (north + pt) || same (north + east + pt)) in (ite top 1 0) + (ite bottom 1 0) + ite left 1 0 + ite right 1 0 where same : Point → Bool same pt = case lookupMap pt grid of Just (_, c) => c == color Nothing => False different : Point → Bool different pt = not $ same pt -- use negative numbers for the regions we've filled flood : Grid → Point → Int → Maybe (Int × Int × Grid) flood orig start id = let (Just (pt,color)) = lookupMap start orig | Nothing => Nothing in if 0 < color then Just $ go orig color (start :: Nil) 0 0 0 else Nothing where go : Grid → Int → List Point → Int → Int → Int → Int × Int × Grid go grid' color Nil area perim p2 = ((area * perim), (area * p2), grid') go grid' color (pt :: pts) area perim p2 = let (Just (_,c2)) = lookupMap pt grid' | Nothing => go grid' color pts area perim p2 in if c2 /= color then go grid' color pts area perim p2 else let next = map fst $ filter (_==_ color ∘ snd) $ mapMaybe (flip lookupMap orig) $ neighbors pt perim = perim + 4 - cast (length next) p2 = p2 + perim2 orig pt color grid = updateMap pt id grid' in go grid color (next ++ pts) (area + 1) perim p2 part1 : Grid → Int × Int part1 grid = go grid (0 - 1) (map fst $ toList grid) 0 0 where go : Grid → Int → List Point → Int → Int → Int × Int go grid id Nil acc acc2 = (acc, acc2) go grid id (pt :: pts) acc acc2 = case flood grid pt id of Nothing => go grid id pts acc acc2 Just (cost, cost2, grid) => go grid (id - 1) pts (acc + cost) (acc2 + cost2) run : String -> IO Unit run fn = do putStrLn fn text <- readFile fn let grid = getGrid text let (p1, p2) = part1 grid putStrLn $ "part1 " ++ show p1 putStrLn $ "part2 " ++ show p2 main : IO Unit main = do run "aoc2024/day12/eg.txt" run "aoc2024/day12/eg2.txt" run "aoc2024/day12/input.txt"