3 | import Data.SortedMap
4 | import Data.SortedSet
5 | import Data.List.Lazy
13 | import Control.Eff.State
15 | %hide Util.Direction
16 | %hide Util.directions
18 | directions : LazyList (Integer, Integer)
31 | garden : Grid rows cols Char
32 | regions : SortedMap Nat (SortedSet (Coord rows cols))
33 | coloring : Grid rows cols (Maybe Nat)
36 | parseInput : String -> Maybe Input
38 | (
rows ** cols ** grid)
<- stringTo2D str
39 | pure $
MkInput rows cols grid empty (const Nothing)
41 | uncolored : (input : Input) -> LazyList (Coord input.rows input.cols)
43 | map fst . filter (isNothing . snd ) . flat $
input.coloring
45 | coloring : (input : Input) -> (loc : Coord input.rows input.cols) -> Maybe Nat
46 | coloring input loc = loc `index` input.coloring
48 | nextColoring : (input : Input) -> Nat
49 | nextColoring input = 1 + foldl max 0 (keys input.regions)
51 | regionFast : (i : Input) -> (seed : Coord i.rows i.cols)
52 | -> Eff [State (SortedSet (Coord i.rows i.cols)), LazyList] (Coord i.rows i.cols)
53 | regionFast i seed = do
55 | if contains seed cache
56 | then send Data.List.Lazy.Nil
58 | modify $
insert seed
59 | let seed_val = seed `index` i.garden
60 | let nexts = mapMaybe (step seed) directions
63 | then send (the (LazyList _) [seed])
65 | let next_val = next `index` i.garden
66 | if next_val == seed_val
67 | then regionFast i next
68 | else send Data.List.Lazy.Nil
71 | region : (input : Input) -> (seed : Coord input.rows input.cols)
72 | -> LazyList (Coord input.rows input.cols)
74 | fst . extract . runState empty . runLazyList $
regionFast input seed
79 | colorRegion : (input : Input) -> (seed : Coord input.rows input.cols) -> Maybe Input
80 | colorRegion input seed =
81 | case coloring input seed of
84 | let reg = region input seed
85 | region_id = nextColoring input
86 | MkInput rows cols garden regions coloring = input
87 | region_set = foldl (\acc, e => insert e acc) empty reg
88 | coloring = foldl (\acc, e => replaceAt e (Just region_id) acc) coloring reg
89 | regions = insert region_id region_set regions
90 | in Just $
MkInput rows cols garden regions coloring
93 | colorAll : (input : Input) -> Input
95 | case head' $
uncolored input of
98 | case colorRegion input seed of
100 | Just input => colorAll input
102 | record Properties where
104 | area, perimeter : Nat
106 | price : Properties -> Nat
107 | price (MkP area perimeter) = area * perimeter
110 | getProps : (input : Input) -> (region : SortedSet (Coord input.rows input.cols)) -> Properties
111 | getProps input region =
112 | let area = length . Data.SortedSet.toList $
region
113 | perimeter = sum . map (\x => 4 `minus` neighbors x) . Data.SortedSet.toList $
region
114 | in MkP area perimeter
116 | neighbors : Coord input.rows input.cols -> Nat
118 | count (\x => contains x region) . mapMaybe (step x) . filter (/= (0,0)) $
directions
122 | part1 : String -> IO (String, Input)
124 | Just input <- pure $
parseInput str
126 | printLn "Failed to parse input"
128 | let colored = colorAll input
129 | let prices = map (price . getProps colored) . values $
colored.regions
130 | pure (show . sum $
prices, colored)
133 | part2 : Input -> String -> IO String